0
|
1 ;;!emacs
|
|
2 ;;
|
|
3 ;; FILE: klink.el
|
|
4 ;; SUMMARY: Implicit reference to a kcell action type, for use in koutlines.
|
|
5 ;; USAGE: GNU Emacs V19 Lisp Library
|
|
6 ;; KEYWORDS: extensions, hypermedia, outlines, wp
|
|
7 ;;
|
|
8 ;; AUTHOR: Bob Weiner & Kellie Clark
|
|
9 ;;
|
|
10 ;; ORIG-DATE: 15-Nov-93 at 12:15:16
|
36
|
11 ;; LAST-MOD: 6-Mar-97 at 01:19:19 by Bob Weiner
|
0
|
12 ;;
|
|
13 ;; DESCRIPTION:
|
|
14 ;;
|
|
15 ;;; link =
|
|
16 ;; < pathname [, cell-ref] [, position] >
|
|
17 ;; < @ cell-ref > ;; In same buffer
|
|
18 ;; < journal-name, journal-item-number [, cell-ref] [, position] >
|
|
19 ;;
|
|
20 ;;; pathname =
|
|
21 ;; path ;; display path in Emacs buffer
|
|
22 ;; !path ;; execute pathname within a shell
|
|
23 ;; &path ;; execute path as a windowed program
|
|
24 ;; -path ;; Load as an Emacs Lisp program
|
|
25 ;;
|
|
26 ;;; cell-ref =
|
|
27 ;; cell - 1a, 012, 1.2, 1a=012 (both relative and absolute ids separated
|
|
28 ;; by an equal sign)
|
|
29 ;; range - 1a-5c, 1a-+3 (include 3 cells past 1a) (not yet implemented)
|
|
30 ;; tree - 1a+ (not yet implemented)
|
|
31 ;;
|
|
32 ;; optionally followed by a period and 1 or more relative position specs
|
|
33 ;; (not yet implemented):
|
|
34 ;;
|
|
35 ;; previous-cell - .b
|
|
36 ;; down-a-level - .d
|
|
37 ;; end-of-branch - .e
|
|
38 ;; follow-next-link - .l
|
|
39 ;; return-to-prev-location - .r
|
|
40 ;; return-to-prev-buffer - .rf
|
|
41 ;; sibling - .s, .2s for 2 siblings forward
|
|
42 ;; tail-of-tree - .t
|
|
43 ;; up-a-level - .u
|
|
44 ;; last char of cell - .f
|
|
45 ;;
|
|
46 ;; and then optionally followed by any amount of whitespace, a pipe `|'
|
|
47 ;; character and then one or more view specification characters. (Augment
|
|
48 ;; viewspec characters may be given instead, preceded by a colon. They are
|
|
49 ;; ignored for now.)
|
|
50 ;;
|
|
51 ;;; position (relative to cell start) = (not yet implemented)
|
|
52 ;; char-pos, e.g. 28 or C28
|
|
53 ;; word-num, e.g. W5
|
|
54 ;; line-num, e.g. L2
|
|
55 ;; paragraph-num, e.g. P3
|
|
56 ;; regexp-match, e.g. "regexp"
|
|
57 ;;
|
|
58 ;; DESCRIP-END.
|
|
59
|
|
60 ;;; ************************************************************************
|
|
61 ;;; Public functions
|
|
62 ;;; ************************************************************************
|
|
63
|
|
64 ;;;###autoload
|
|
65 (defun klink:create (reference)
|
|
66 "Insert at point an implicit link to REFERENCE.
|
|
67 REFERENCE should be a cell-ref or a string containing \"filename, cell-ref\".
|
36
|
68 See documentation for `kcell:ref-to-id' for valid cell-ref formats."
|
0
|
69 (interactive
|
|
70 ;; Don't change the name or delete default-dir used here. It is referenced
|
|
71 ;; in "hargs.el" for argument getting.
|
|
72 (let ((default-dir default-directory))
|
|
73 (barf-if-buffer-read-only)
|
|
74 (hargs:iform-read
|
|
75 (list 'interactive "*+LInsert link to <[file,] cell-id [|vspecs]>: "))))
|
|
76 (barf-if-buffer-read-only)
|
|
77 ;; Reference generally is a string. It may be a list as a string, e.g.
|
|
78 ;; "(\"file\" \"cell\")", in which case, we remove the unneeded internal
|
|
79 ;; double quotes and then parse it with pattern matching.
|
|
80 (and (stringp reference) (> (length reference) 0)
|
|
81 (= (aref reference 0) ?\()
|
|
82 (setq reference (hypb:replace-match-string "\\\"" reference "" t)))
|
|
83 (let ((default-dir default-directory)
|
|
84 file-ref cell-ref)
|
|
85 (setq reference (klink:parse reference)
|
|
86 file-ref (car reference)
|
|
87 cell-ref (car (cdr reference)))
|
|
88 ;; Don't need filename if link is to a cell in current buffer.
|
|
89 (if (and file-ref (equal buffer-file-name
|
|
90 (expand-file-name file-ref default-directory)))
|
|
91 (setq file-ref nil))
|
|
92 (cond (file-ref
|
|
93 (setq file-ref (hpath:relative-to file-ref))
|
|
94 ;; "./" prefix, if any.
|
|
95 (if (string-match "^\\./" file-ref)
|
|
96 (setq file-ref (substring file-ref (match-end 0))))
|
|
97 (insert "<" file-ref)
|
|
98 (if cell-ref (insert ", " cell-ref))
|
|
99 (insert ">"))
|
|
100 (cell-ref (insert "<@ " cell-ref ">"))
|
36
|
101 (t (error "(klink:create) Invalid reference, `%s'" reference)))))
|
0
|
102
|
|
103 (defun klink:at-p ()
|
|
104 "Return non-nil iff point is within a klink.
|
24
|
105 See documentation for the `actypes::link-to-kotl' function for valid klink
|
|
106 formats. Value returned is a list of: link-label, link-start-position, and
|
0
|
107 link-end-position, (including delimiters)."
|
|
108 (let (bol klink referent)
|
|
109 (if (and
|
|
110 ;; If this is an OO-Browser listing buffer, ignore anything that
|
|
111 ;; looks like a klink, e.g. a C++ <template> class.
|
|
112 (if (fboundp 'br-browser-buffer-p)
|
|
113 (not (br-browser-buffer-p))
|
|
114 t)
|
|
115 ;; Don't match to C/C++ lines like: #include < path >
|
|
116 (save-excursion
|
|
117 (beginning-of-line)
|
|
118 (setq bol (point))
|
|
119 (require 'hmouse-tag)
|
|
120 (not (looking-at smart-c-include-regexp)))
|
|
121 (save-excursion
|
|
122 ;; Don't match Elisp print objects such as #<buffer>
|
|
123 (and (search-backward "<" bol t)
|
|
124 (/= (preceding-char) ?#)
|
|
125 ;; Don't match to \<(explicit)> Hyperbole buttons
|
|
126 (/= (char-after (1+ (point))) ?\()))
|
|
127 (setq klink (hbut:label-p t "<" ">" t))
|
|
128 (stringp (setq referent (car klink)))
|
|
129 ;; Eliminate matches to e-mail address like, <user@domain>.
|
24
|
130 (not (string-match "[^<> \t\n][!&@]" referent))
|
|
131 ;; Eliminate matches to URLs
|
|
132 (not (string-match "\\`[a-zA-Z]+:" referent))
|
|
133 ;; Don't match to <HTML> and </SGML> tags.
|
|
134 (not (and (memq major-mode
|
|
135 (if (boundp 'id-select-markup-modes)
|
|
136 id-select-markup-modes
|
|
137 '(html-mode sgml-mode)))
|
|
138 ;; Assume , followed by a number is a klink.
|
|
139 (not (string-match ",\\s *[0-9]" referent))
|
|
140 (string-match "\\`[a-zA-Z!/]" referent))))
|
0
|
141 klink)))
|
|
142
|
|
143 ;;; ************************************************************************
|
|
144 ;;; Hyperbole type definitions
|
|
145 ;;; ************************************************************************
|
|
146
|
|
147 (defib klink ()
|
|
148 "Follows a link delimited by <> to a koutline cell.
|
|
149 See documentation for the `link-to-kotl' function for valid klink formats."
|
|
150 (let* ((link-and-pos (klink:at-p))
|
|
151 (link (car link-and-pos))
|
|
152 (start-pos (car (cdr link-and-pos))))
|
|
153 (if link
|
|
154 (progn (ibut:label-set link-and-pos)
|
|
155 (hact 'klink:act link start-pos)))))
|
|
156
|
|
157 (defact link-to-kotl (link)
|
|
158 "Displays at the top of another window the referent pointed to by LINK.
|
|
159 LINK may be of any of the following forms, with or without delimiters:
|
|
160 < pathname [, cell-ref] >
|
|
161 < [-!&] pathname >
|
|
162 < @ cell-ref >
|
|
163
|
36
|
164 See documentation for `kcell:ref-to-id' for valid cell-ref formats."
|
0
|
165
|
|
166 (interactive "sKotl link specifier: ")
|
|
167 (or (stringp link) (error "(link-to-kotl): Non-string link argument, %s"
|
|
168 link))
|
|
169 (cond
|
|
170 ((or (string-match (format "\\`<?\\s *@\\s *\\(%s\\)\\s *>?\\'"
|
|
171 klink:cell-ref-regexp) link)
|
|
172 (string-match (format "\\`<?\\s *\\([|:]%s\\)\\s *>?\\'"
|
|
173 klink:cell-ref-regexp) link))
|
|
174 ;; < @ cell-ref > or < |viewspec > or < :augment-viewspec >
|
|
175 (hact 'link-to-kcell
|
|
176 nil
|
|
177 (kcell:ref-to-id
|
|
178 (substring link (match-beginning 1) (match-end 1)))))
|
|
179 ((string-match
|
|
180 (format "\\`<?\\s *\\([^ \t\n,<>]+\\)\\s *\\(,\\s *\\(%s\\)\\)?\\s *>?\\'"
|
|
181 klink:cell-ref-regexp)
|
|
182 link)
|
|
183 ;; < pathname [, cell-ref] >
|
|
184 (hact 'link-to-kcell
|
|
185 (substring link (match-beginning 1) (match-end 1))
|
|
186 (if (match-end 3)
|
|
187 (kcell:ref-to-id
|
|
188 (substring link (match-beginning 3) (match-end 3))))))
|
|
189 ((string-match
|
|
190 "\\`<?\\s *\\(\\([-!&]\\)?\\s *[^ \t\n,<>]+\\)\\s *>?\\'" link)
|
|
191 ;; < [-!&] pathname >
|
|
192 (hpath:find-other-window
|
|
193 (substring link (match-beginning 1) (match-end 1))))
|
|
194 (t (error "(link-to-kotl): Invalid link specifier, %s" link))))
|
|
195
|
|
196 ;;; ************************************************************************
|
|
197 ;;; Private functions
|
|
198 ;;; ************************************************************************
|
|
199
|
|
200 (defun klink:act (link start-pos)
|
|
201 (let ((obuf (current-buffer)))
|
|
202 ;; Perform klink's action which is to jump to link referent.
|
|
203 (hact 'link-to-kotl link)
|
|
204 ;; Update klink label if need be, which might be in a different buffer
|
|
205 ;; than the current one.
|
|
206 (klink:update-label link start-pos obuf)))
|
|
207
|
|
208 (defun klink:parse (reference)
|
|
209 "Returns (file-ref cell-ref) list parsed from REFERENCE string.
|
|
210 Either element of the list may be nil if REFERENCE does not contain that
|
|
211 element. REFERENCE should be one of the following forms (and may include an
|
|
212 optional pair of <> delimiters:
|
|
213 (pathname, cell-ref)
|
|
214 pathname, cell-ref
|
|
215 cell-ref
|
|
216 |viewspec
|
|
217 :augment-viewspec (ignored for now)
|
|
218
|
36
|
219 See documentation for `kcell:ref-to-id' for valid cell-ref formats."
|
0
|
220
|
|
221 (or (stringp reference)
|
|
222 (error "(klink:parse): Non-string reference argument, %s"
|
|
223 reference))
|
|
224 (cond
|
|
225 ((string-match
|
|
226 (format
|
|
227 "\\`\\s *[<\(]?\\s *\\([^|: \t\n\r,<>][^ \t\n\r,<>]*\\)\\s *,\\s *\\(%s\\)\\s *[\)>]?\\s *\\'"
|
|
228 klink:cell-ref-regexp)
|
|
229 reference)
|
|
230 ;; pathname cell-ref
|
|
231 (list (substring reference (match-beginning 1) (match-end 1))
|
|
232 (substring reference (match-beginning 2) (match-end 2))))
|
|
233 ((string-match (format "\\`\\s *<?\\s *\\(%s\\)\\s *>?\\s *\\'"
|
|
234 klink:cell-ref-regexp)
|
|
235 reference)
|
|
236 ;; cell-ref
|
|
237 (list nil (substring reference (match-beginning 1) (match-end 1))))
|
|
238 (t (error "(klink:parse): Invalid reference specifier, %s" reference))))
|
|
239
|
|
240 (defun klink:replace-label (klink link-buf start new-label)
|
|
241 "Replace out of date relative id in a link reference of the form, relid=idstamp."
|
|
242 (save-excursion
|
|
243 (set-buffer link-buf)
|
|
244 (if buffer-read-only
|
|
245 (message "Relative label should be `%s' in klink <%s>."
|
|
246 new-label klink)
|
|
247 (goto-char start)
|
|
248 (cond ((or (looking-at "<\\s *@\\s *")
|
|
249 (looking-at "[^,]+,\\s *"))
|
|
250 (goto-char (match-end 0))
|
|
251 (zap-to-char 1 ?=)
|
|
252 (insert new-label ?=))
|
|
253 (t nil)))))
|
|
254
|
|
255 (defun klink:update-label (klink start link-buf)
|
|
256 "Update label of KLINK if its relative cell id has changed.
|
|
257 Assume point is in klink referent buffer, where the klink points."
|
|
258 (if (and (stringp klink)
|
|
259 (string-match
|
|
260 "[@,]\\s *\\([*0-9][*.0-9a-zA-Z]*\\)\\s *=\\s *0[0-9]*"
|
|
261 klink))
|
|
262 ;; Then klink has both relative and permanent ids.
|
|
263 (let* ((label (substring klink (match-beginning 1) (match-end 1)))
|
|
264 (new-label (kcell-view:label)))
|
|
265 (if (and new-label (not (equal label new-label)))
|
|
266 (klink:replace-label klink link-buf start new-label)))))
|
|
267
|
|
268 ;;; ************************************************************************
|
|
269 ;;; Private variables.
|
|
270 ;;; ************************************************************************
|
|
271
|
|
272 (defvar klink:cell-ref-regexp
|
|
273 "[|:0-9a-zA-Z][|:.*~=0-9a-zA-Z \t\n\r]*"
|
|
274 "Regexp matching a cell reference including relative and view specs.
|
|
275 Contains no groupings.")
|
|
276
|
|
277 (provide 'klink)
|