source: tags/1.2/source/cocoa-ide/hemlock/unused/archive/scribe.lisp

Last change on this file was 6567, checked in by Gary Byers, 18 years ago

Move lots of (currently unused, often unlikely to ever be used) stuff to an
archive directory.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.5 KB
Line 
1;;; -*- Log: hemlock.log; Package: Hemlock -*-
2;;;
3;;; **********************************************************************
4;;; This code was written as part of the CMU Common Lisp project at
5;;; Carnegie Mellon University, and has been placed in the public domain.
6;;;
7#+CMU (ext:file-comment
8 "$Header$")
9;;;
10;;; **********************************************************************
11;;;
12
13(in-package :hemlock)
14
15
16
17;;;; Variables.
18
19(defvar *scribe-para-break-table* (make-hash-table :test #'equal)
20 "A table of the Scribe commands that should be paragraph delimiters.")
21;;;
22(dolist (todo '("begin" "newpage" "make" "device" "caption" "tag" "end"
23 "chapter" "section" "appendix" "subsection" "paragraph"
24 "unnumbered" "appendixsection" "prefacesection" "heading"
25 "majorheading" "subheading"))
26 (setf (gethash todo *scribe-para-break-table*) t))
27
28(defhvar "Open Paren Character"
29 "The open bracket inserted by Scribe commands."
30 :value #\[)
31
32(defhvar "Close Paren Character"
33 "The close bracket inserted by Scribe commands."
34 :value #\])
35
36(defhvar "Escape Character"
37 "The escape character inserted by Scribe commands."
38 :value #\@)
39
40(defhvar "Scribe Bracket Table"
41 "This table maps a Scribe brackets, open and close, to their opposing
42 brackets."
43 :value (make-array char-code-limit))
44;;;
45(mapc #'(lambda (x y)
46 (setf (svref (value scribe-bracket-table) (char-code x)) y)
47 (setf (svref (value scribe-bracket-table) (char-code y)) x))
48 '(#\( #\[ #\{ #\<) '(#\) #\] #\} #\>))
49;;;
50(defun opposing-bracket (bracket)
51 (svref (value scribe-bracket-table) (char-code bracket)))
52
53
54
55
56;;;; "Scribe Syntax" Attribute.
57
58(defattribute "Scribe Syntax"
59 "For Scribe Syntax, Possible types are:
60 :ESCAPE ; basically #\@.
61 :OPEN-PAREN ; Characters that open a Scribe paren: #\[, #\{, #\(, #\<.
62 :CLOSE-PAREN ; Characters that close a Scribe paren: #\], #\}, #\), #\>.
63 :SPACE ; Delimits end of a Scribe command.
64 :NEWLINE ; Delimits end of a Scribe command."
65 'symbol nil)
66
67(setf (character-attribute :scribe-syntax #\)) :close-paren)
68(setf (character-attribute :scribe-syntax #\]) :close-paren)
69(setf (character-attribute :scribe-syntax #\}) :close-paren)
70(setf (character-attribute :scribe-syntax #\>) :close-paren)
71
72(setf (character-attribute :scribe-syntax #\() :open-paren)
73(setf (character-attribute :scribe-syntax #\[) :open-paren)
74(setf (character-attribute :scribe-syntax #\{) :open-paren)
75(setf (character-attribute :scribe-syntax #\<) :open-paren)
76
77(setf (character-attribute :scribe-syntax #\space) :space)
78(setf (character-attribute :scribe-syntax #\newline) :newline)
79(setf (character-attribute :scribe-syntax #\@) :escape)
80
81
82
83
84;;;; "Scribe" mode and setup.
85
86(defmode "Scribe" :major-p t)
87
88(shadow-attribute :paragraph-delimiter #\@ 1 "Scribe")
89(shadow-attribute :word-delimiter #\' 0 "Scribe") ;from Text Mode
90(shadow-attribute :word-delimiter #\backspace 0 "Scribe") ;from Text Mode
91(shadow-attribute :word-delimiter #\_ 0 "Scribe") ;from Text Mode
92
93(define-file-type-hook ("mss") (buffer type)
94 (declare (ignore type))
95 (setf (buffer-major-mode buffer) "Scribe"))
96
97
98
99
100;;;; Commands.
101
102(defcommand "Scribe Mode" (p)
103 "Puts buffer in Scribe mode. Sets up comment variables and has delimiter
104 matching. The definition of paragraphs is changed to know about scribe
105 commands."
106 "Puts buffer in Scribe mode."
107 (declare (ignore p))
108 (setf (buffer-major-mode (current-buffer)) "Scribe"))
109
110(defcommand "Select Scribe Warnings" (p)
111 "Goes to the Scribe Warnings buffer if it exists."
112 "Goes to the Scribe Warnings buffer if it exists."
113 (declare (ignore p))
114 (let ((buffer (getstring "Scribe Warnings" *buffer-names*)))
115 (if buffer
116 (change-to-buffer buffer)
117 (editor-error "There is no Scribe Warnings buffer."))))
118
119(defcommand "Add Scribe Paragraph Delimiter"
120 (p &optional
121 (word (prompt-for-string
122 :prompt "Scribe command: "
123 :help "Name of Scribe command to make delimit paragraphs."
124 :trim t)))
125 "Prompts for a name to add to the table of commands that delimit paragraphs
126 in Scribe mode. If a prefix argument is supplied, then the command name is
127 removed from the table."
128 "Add or remove Word in the *scribe-para-break-table*, depending on P."
129 (setf (gethash word *scribe-para-break-table*) (not p)))
130
131(defcommand "List Scribe Paragraph Delimiters" (p)
132 "Pops up a display of the Scribe commands that delimit paragraphs."
133 "Pops up a display of the Scribe commands that delimit paragraphs."
134 (declare (ignore p))
135 (let (result)
136 (maphash #'(lambda (k v)
137 (declare (ignore v))
138 (push k result))
139 *scribe-para-break-table*)
140 (setf result (sort result #'string<))
141 (with-pop-up-display (s :height (length result))
142 (dolist (ele result) (write-line ele s)))))
143
144(defcommand "Scribe Insert Bracket" (p)
145 "Inserts a the bracket it is bound to and then shows the matching bracket."
146 "Inserts a the bracket it is bound to and then shows the matching bracket."
147 (declare (ignore p))
148 (scribe-insert-paren (current-point)
149 (hemlock-ext:key-event-char *last-key-event-typed*)))
150
151
152(defhvar "Scribe Command Table"
153 "This is a character dispatching table indicating which Scribe command or
154 environment to use."
155 :value (make-hash-table)
156 :mode "Scribe")
157
158(defvar *scribe-directive-type-table*
159 (make-string-table :initial-contents
160 '(("Command" . :command)
161 ("Environment" . :environment))))
162
163(defcommand "Add Scribe Directive" (p &optional
164 (command-name nil command-name-p)
165 type key-event mode)
166 "Adds a new scribe function to put into \"Scribe Command Table\"."
167 "Adds a new scribe function to put into \"Scribe Command Table\"."
168 (declare (ignore p))
169 (let ((command-name (if command-name-p
170 command-name
171 (or command-name
172 (prompt-for-string :help "Directive Name"
173 :prompt "Directive: ")))))
174 (multiple-value-bind (ignore type)
175 (if type
176 (values nil type)
177 (prompt-for-keyword
178 (list *scribe-directive-type-table*)
179 :help "Enter Command or Environment."
180 :prompt "Command or Environment: "))
181 (declare (ignore ignore))
182 (let ((key-event (or key-event
183 (prompt-for-key-event :prompt
184 "Dispatch Character: "))))
185 (setf (gethash key-event
186 (cond (mode
187 (variable-value 'scribe-command-table :mode mode))
188 ((hemlock-bound-p 'scribe-command-table)
189 (value scribe-command-table))
190 (t (editor-error
191 "Could not find \"Scribe Command Table\"."))))
192 (cons type command-name))))))
193
194(defcommand "Insert Scribe Directive" (p)
195 "Prompts for a character to dispatch on. Some indicate \"commands\" versus
196 \"environments\". Commands are wrapped around the previous or current word.
197 If there is no previous word, the command is insert, leaving point between
198 the brackets. Environments are wrapped around the next or current
199 paragraph, but when the region is active, this wraps the environment around
200 the region. Each uses \"Open Paren Character\" and \"Close Paren
201 Character\"."
202 "Wrap some text with some stuff."
203 (declare (ignore p))
204 (loop
205 (let ((key-event (prompt-for-key-event :prompt "Dispatch Character: ")))
206 (if (logical-key-event-p key-event :help)
207 (directive-help)
208 (let ((table-entry (gethash key-event (value scribe-command-table))))
209 (ecase (car table-entry)
210 (:command
211 (insert-scribe-directive (current-point) (cdr table-entry))
212 (return))
213 (:environment
214 (enclose-with-environment (current-point) (cdr table-entry))
215 (return))
216 ((nil) (editor-error "Unknown dispatch character."))))))))
217
218
219
220
221;;;; "Insert Scribe Directive" support.
222
223(defun directive-help ()
224 (let ((commands ())
225 (environments ()))
226 (declare (list commands environments))
227 (maphash #'(lambda (k v)
228 (if (eql (car v) :command)
229 (push (cons k (cdr v)) commands)
230 (push (cons k (cdr v)) environments)))
231 (value scribe-command-table))
232 (setf commands (sort commands #'string< :key #'cdr))
233 (setf environments (sort environments #'string< :key #'cdr))
234 (with-pop-up-display (s :height (1+ (max (length commands)
235 (length environments))))
236 (format s "~2TCommands~47TEnvironments~%")
237 (do ((commands commands (rest commands))
238 (environments environments (rest environments)))
239 ((and (endp commands) (endp environments)))
240 (let* ((command (first commands))
241 (environment (first environments))
242 (cmd-char (first command))
243 (cmd-name (rest command))
244 (env-char (first environment))
245 (env-name (rest environment)))
246 (write-string " " s)
247 (when cmd-char
248 (hemlock-ext:print-pretty-key-event cmd-char s)
249 (format s "~7T")
250 (write-string (or cmd-name "<prompts for command name>") s))
251 (when env-char
252 (format s "~47T")
253 (hemlock-ext:print-pretty-key-event env-char s)
254 (format s "~51T")
255 (write-string (or env-name "<prompts for command name>") s))
256 (terpri s))))))
257
258;;;
259;;; Inserting and extending :command directives.
260;;;
261
262(defhvar "Insert Scribe Directive Function"
263 "\"Insert Scribe Directive\" calls this function when the directive type
264 is :command. The function takes four arguments: a mark pointing to the word
265 start, the formatting command string, the open-paren character to use, and a
266 mark pointing to the word end."
267 :value 'scribe-insert-scribe-directive-fun
268 :mode "Scribe")
269
270(defun scribe-insert-scribe-directive-fun (word-start command-string
271 open-paren-char word-end)
272 (insert-character word-start (value escape-character))
273 (insert-string word-start command-string)
274 (insert-character word-start open-paren-char)
275 (insert-character word-end (value close-paren-character)))
276
277(defhvar "Extend Scribe Directive Function"
278 "\"Insert Scribe Directive\" calls this function when the directive type is
279 :command to extend the the commands effect. This function takes a string
280 and three marks: the first on pointing before the open-paren character for
281 the directive. The string is the command-string to selected by the user
282 which this function uses to determine if it is actually extending a command
283 or inserting a new one. The function must move the first mark before any
284 command text for the directive and the second mark to the end of any command
285 text. It moves the third mark to the previous word's start where the
286 command region should be. If this returns non-nil \"Insert Scribe
287 Directive\" moves the command region previous one word, and otherwise it
288 inserts the directive."
289 :value 'scribe-extend-scribe-directive-fun
290 :mode "Scribe")
291
292(defun scribe-extend-scribe-directive-fun (command-string
293 command-end command-start word-start)
294 (word-offset (move-mark command-start command-end) -1)
295 (when (string= (the simple-string (region-to-string
296 (region command-start command-end)))
297 command-string)
298 (mark-before command-start)
299 (mark-after command-end)
300 (word-offset (move-mark word-start command-start) -1)))
301
302;;; INSERT-SCRIBE-DIRECTIVE first looks for the current or previous word at
303;;; mark. Word-p says if we found one. If mark is immediately before a word,
304;;; we use that word instead of the previous. This is because if mark
305;;; corresponds to the CURRENT-POINT, the Hemlock cursor is displayed on the
306;;; first character of the word making users think the mark is in the word
307;;; instead of before it. If we find a word, then we see if it already has
308;;; the given command-string, and if it does, we extend the use of the command-
309;;; string to the previous word. At the end, if we hadn't found a word, we
310;;; backup the mark one character to put it between the command brackets.
311;;;
312(defun insert-scribe-directive (mark &optional command-string)
313 (with-mark ((word-start mark :left-inserting)
314 (word-end mark :left-inserting))
315 (let ((open-paren-char (value open-paren-character))
316 (word-p (if (and (zerop (character-attribute
317 :word-delimiter
318 (next-character word-start)))
319 (= (character-attribute
320 :word-delimiter
321 (previous-character word-start))
322 1))
323 word-start
324 (word-offset word-start -1)))
325 (command-string (or command-string
326 (prompt-for-string
327 :trim t :prompt "Environment: "
328 :help "Name of environment to enclose with."))))
329 (declare (simple-string command-string))
330 (cond
331 (word-p
332 (word-offset (move-mark word-end word-start) 1)
333 (if (test-char (next-character word-end) :scribe-syntax
334 :close-paren)
335 (with-mark ((command-start word-start :left-inserting)
336 (command-end word-end :left-inserting))
337 ;; Move command-end from word-end to open-paren of command.
338 (balance-paren (mark-after command-end))
339 (if (funcall (value extend-scribe-directive-function)
340 command-string command-end command-start word-start)
341 (let ((region (delete-and-save-region
342 (region command-start command-end))))
343 (word-offset (move-mark word-start command-start) -1)
344 (ninsert-region word-start region))
345 (funcall (value insert-scribe-directive-function)
346 word-start command-string open-paren-char
347 word-end)))
348 (funcall (value insert-scribe-directive-function)
349 word-start command-string open-paren-char word-end)))
350 (t
351 (funcall (value insert-scribe-directive-function)
352 word-start command-string open-paren-char word-end)
353 (mark-before mark))))))
354
355;;;
356;;; Inserting :environment directives.
357;;;
358
359(defun enclose-with-environment (mark &optional environment)
360 (if (region-active-p)
361 (let ((region (current-region)))
362 (with-mark ((top (region-start region) :left-inserting)
363 (bottom (region-end region) :left-inserting))
364 (get-and-insert-environment top bottom environment)))
365 (with-mark ((bottom-mark mark :left-inserting))
366 (let ((paragraphp (paragraph-offset bottom-mark 1)))
367 (unless (or paragraphp
368 (and (last-line-p bottom-mark)
369 (end-line-p bottom-mark)
370 (not (blank-line-p (mark-line bottom-mark)))))
371 (editor-error "No paragraph to enclose."))
372 (with-mark ((top-mark bottom-mark :left-inserting))
373 (paragraph-offset top-mark -1)
374 (cond ((not (blank-line-p (mark-line top-mark)))
375 (insert-character top-mark #\Newline)
376 (mark-before top-mark))
377 (t
378 (insert-character top-mark #\Newline)))
379 (cond ((and (last-line-p bottom-mark)
380 (not (blank-line-p (mark-line bottom-mark))))
381 (insert-character bottom-mark #\Newline))
382 (t
383 (insert-character bottom-mark #\Newline)
384 (mark-before bottom-mark)))
385 (get-and-insert-environment top-mark bottom-mark environment))))))
386
387(defun get-and-insert-environment (top-mark bottom-mark environment)
388 (let ((environment (or environment
389 (prompt-for-string
390 :trim t :prompt "Environment: "
391 :help "Name of environment to enclose with."))))
392 (insert-environment top-mark "begin" environment)
393 (insert-environment bottom-mark "end" environment)))
394
395(defun insert-environment (mark command environment)
396 (let ((esc-char (value escape-character))
397 (open-paren (value open-paren-character))
398 (close-paren (value close-paren-character)))
399 (insert-character mark esc-char)
400 (insert-string mark command)
401 (insert-character mark open-paren)
402 (insert-string mark environment)
403 (insert-character mark close-paren)))
404
405
406(add-scribe-directive-command nil nil :Environment #k"Control-l" "Scribe")
407(add-scribe-directive-command nil nil :Command #k"Control-w" "Scribe")
408(add-scribe-directive-command nil "Begin" :Command #k"b" "Scribe")
409(add-scribe-directive-command nil "End" :Command #k"e" "Scribe")
410(add-scribe-directive-command nil "Center" :Environment #k"c" "Scribe")
411(add-scribe-directive-command nil "Description" :Environment #k"d" "Scribe")
412(add-scribe-directive-command nil "Display" :Environment #k"Control-d" "Scribe")
413(add-scribe-directive-command nil "Enumerate" :Environment #k"n" "Scribe")
414(add-scribe-directive-command nil "Example" :Environment #k"x" "Scribe")
415(add-scribe-directive-command nil "FileExample" :Environment #k"y" "Scribe")
416(add-scribe-directive-command nil "FlushLeft" :Environment #k"l" "Scribe")
417(add-scribe-directive-command nil "FlushRight" :Environment #k"r" "Scribe")
418(add-scribe-directive-command nil "Format" :Environment #k"f" "Scribe")
419(add-scribe-directive-command nil "Group" :Environment #k"g" "Scribe")
420(add-scribe-directive-command nil "Itemize" :Environment #k"Control-i" "Scribe")
421(add-scribe-directive-command nil "Multiple" :Environment #k"m" "Scribe")
422(add-scribe-directive-command nil "ProgramExample" :Environment #k"p" "Scribe")
423(add-scribe-directive-command nil "Quotation" :Environment #k"q" "Scribe")
424(add-scribe-directive-command nil "Text" :Environment #k"t" "Scribe")
425(add-scribe-directive-command nil "i" :Command #k"i" "Scribe")
426(add-scribe-directive-command nil "b" :Command #k"Control-b" "Scribe")
427(add-scribe-directive-command nil "-" :Command #k"\-" "Scribe")
428(add-scribe-directive-command nil "+" :Command #k"+" "Scribe")
429(add-scribe-directive-command nil "u" :Command #k"Control-j" "Scribe")
430(add-scribe-directive-command nil "p" :Command #k"Control-p" "Scribe")
431(add-scribe-directive-command nil "r" :Command #k"Control-r" "Scribe")
432(add-scribe-directive-command nil "t" :Command #k"Control-t" "Scribe")
433(add-scribe-directive-command nil "g" :Command #k"Control-a" "Scribe")
434(add-scribe-directive-command nil "un" :Command #k"Control-n" "Scribe")
435(add-scribe-directive-command nil "ux" :Command #k"Control-x" "Scribe")
436(add-scribe-directive-command nil "c" :Command #k"Control-k" "Scribe")
437
438
439
440
441;;;; Scribe paragraph delimiter function.
442
443(defhvar "Paragraph Delimiter Function"
444 "Scribe Mode's way of delimiting paragraphs."
445 :mode "Scribe"
446 :value 'scribe-delim-para-function)
447
448(defun scribe-delim-para-function (mark)
449 "Returns whether there is a paragraph delimiting Scribe command on the
450 current line. Add or remove commands for this purpose with the command
451 \"Add Scribe Paragraph Delimiter\"."
452 (let ((next-char (next-character mark)))
453 (when (paragraph-delimiter-attribute-p next-char)
454 (if (eq (character-attribute :scribe-syntax next-char) :escape)
455 (with-mark ((begin mark)
456 (end mark))
457 (mark-after begin)
458 (if (scan-char end :scribe-syntax (or :space :newline :open-paren))
459 (gethash (nstring-downcase (region-to-string (region begin end)))
460 *scribe-para-break-table*)
461 (editor-error "Unable to find Scribe command ending.")))
462 t))))
463
464
465
466
467;;;; Bracket matching.
468
469(defun scribe-insert-paren (mark bracket-char)
470 (insert-character mark bracket-char)
471 (with-mark ((m mark))
472 (if (balance-paren m)
473 (when (value paren-pause-period)
474 (unless (show-mark m (current-window) (value paren-pause-period))
475 (clear-echo-area)
476 (message "~A" (line-string (mark-line m)))))
477 (editor-error))))
478
479;;; BALANCE-PAREN moves the mark to the matching open paren character, or
480;;; returns nil. The mark must be after the closing paren.
481;;;
482(defun balance-paren (mark)
483 (with-mark ((m mark))
484 (when (rev-scan-char m :scribe-syntax (or :open-paren :close-paren))
485 (mark-before m)
486 (let ((paren-count 1)
487 (first-paren (next-character m)))
488 (loop
489 (unless (rev-scan-char m :scribe-syntax (or :open-paren :close-paren))
490 (return nil))
491 (if (test-char (previous-character m) :scribe-syntax :open-paren)
492 (setq paren-count (1- paren-count))
493 (setq paren-count (1+ paren-count)))
494 (when (< paren-count 0) (return nil))
495 (when (= paren-count 0)
496 ;; OPPOSING-BRACKET calls VALUE (each time around the loop)
497 (cond ((char= (opposing-bracket (previous-character m)) first-paren)
498 (mark-before (move-mark mark m))
499 (return t))
500 (t (editor-error "Scribe paren mismatch."))))
501 (mark-before m))))))
Note: See TracBrowser for help on using the repository browser.