(QCuser_data): Removed.
[bpt/emacs.git] / lisp / progmodes / ps-mode.el
CommitLineData
99485bca
GM
1;;; ps-mode.el --- PostScript mode for GNU Emacs.
2
3;; Copyright (C) 1999 Free Software Foundation, Inc.
4
5;; Author: Peter Kleiweg <kleiweg@let.rug.nl>
6;; Maintainer: Peter Kleiweg <kleiweg@let.rug.nl>
7;; Created: 20 Aug 1997
155fc930 8;; Version: 1.1c, 5 Nov 1999
99485bca
GM
9;; Keywords: PostScript, languages
10
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software; you can redistribute it and/or modify
14;; it under the terms of the GNU General Public License as published by
15;; the Free Software Foundation; either version 2, or (at your option)
16;; any later version.
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
24;; along with GNU Emacs; see the file COPYING. If not, write to the
25;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26;; Boston, MA 02111-1307, USA.
27
28;;; Commentary:
29
30\f
31;;; Code:
32
155fc930 33(defconst ps-mode-version "1.1c, 5 Nov 1999")
c22d928f 34
99485bca
GM
35(require 'easymenu)
36
37;; Define core `PostScript' group.
38(defgroup PostScript nil
39 "PostScript mode for Emacs."
40 :group 'languages)
41
42(defgroup PostScript-edit nil
43 "PostScript editing."
44 :prefix "ps-mode-"
45 :group 'PostScript)
46
47(defgroup PostScript-interaction nil
48 "PostScript interaction."
49 :prefix "ps-run-"
50 :group 'PostScript)
51
52;; User variables.
53
54(defcustom ps-mode-auto-indent t
55 "*Should we use autoindent?"
56 :group 'PostScript-edit
57 :type 'boolean)
58
59(defcustom ps-mode-tab 4
60 "*Number of spaces to use when indenting."
61 :group 'PostScript-edit
62 :type 'integer)
63
64(defcustom ps-mode-paper-size '(595 842)
65 "*Default paper size.
66
67When inserting an EPSF template these values are used
68to set the boundingbox to include the whole page.
69When the figure is finished these values should be replaced."
70 :group 'PostScript-edit
71 :type '(choice
72 (const :tag "letter" (612 792))
73 (const :tag "legal" (612 1008))
74 (const :tag "a0" (2380 3368))
75 (const :tag "a1" (1684 2380))
76 (const :tag "a2" (1190 1684))
77 (const :tag "a3" (842 1190))
78 (const :tag "a4" (595 842))
79 (const :tag "a5" (421 595))
80 (const :tag "a6" (297 421))
81 (const :tag "a7" (210 297))
82 (const :tag "a8" (148 210))
83 (const :tag "a9" (105 148))
84 (const :tag "a10" (74 105))
85 (const :tag "b0" (2836 4008))
86 (const :tag "b1" (2004 2836))
87 (const :tag "b2" (1418 2004))
88 (const :tag "b3" (1002 1418))
89 (const :tag "b4" (709 1002))
90 (const :tag "b5" (501 709))
91 (const :tag "archE" (2592 3456))
92 (const :tag "archD" (1728 2592))
93 (const :tag "archC" (1296 1728))
94 (const :tag "archB" (864 1296))
95 (const :tag "archA" (648 864))
96 (const :tag "flsa" (612 936))
97 (const :tag "flse" (612 936))
98 (const :tag "halfletter" (396 612))
99 (const :tag "11x17" (792 1224))
100 (const :tag "tabloid" (792 1224))
101 (const :tag "ledger" (1224 792))
102 (const :tag "csheet" (1224 1584))
103 (const :tag "dsheet" (1584 2448))
104 (const :tag "esheet" (2448 3168))))
105
c22d928f
GM
106(defcustom ps-mode-print-function
107 '(lambda ()
108 (let ((lpr-switches nil)
155fc930
GM
109 (lpr-command (if (memq system-type '(usg-unix-v dgux hpux irix))
110 "lp" "lpr")))
c22d928f 111 (lpr-buffer)))
99485bca
GM
112 "*Lisp function to print current buffer as PostScript."
113 :group 'PostScript-edit
114 :type 'function)
115
116(defcustom ps-run-prompt "\\(GS\\(<[0-9]+\\)?>\\)+"
117 "*Regexp to match prompt in interactive PostScript."
118 :group 'PostScript-interaction
119 :type 'regexp)
120
c22d928f
GM
121(defcustom ps-run-font-lock-keywords-2
122 (append (unless (string= ps-run-prompt "")
123 (list (list (if (= ?^ (string-to-char ps-run-prompt))
124 ps-run-prompt
125 (concat "^" ps-run-prompt))
126 '(0 font-lock-function-name-face nil nil))))
127 '((">>showpage, press <return> to continue<<"
128 (0 font-lock-keyword-face nil nil))
129 ("^\\(Error\\|Can't\\).*"
130 (0 font-lock-warning-face nil nil))
131 ("^\\(Current file position is\\) \\([0-9]+\\)"
132 (1 font-lock-comment-face nil nil)
133 (2 font-lock-warning-face nil nil))))
99485bca
GM
134 "*Medium level highlighting of messages from the PostScript interpreter.
135
136See documentation on font-lock for details."
137 :group 'PostScript-interaction
138 :type '(repeat (list :tag "Expression with one or more highlighters"
139 :value ("" (0 default nil t))
140 (regexp :tag "Expression")
141 (repeat :tag "Highlighters"
142 :inline regexp
143 (list :tag "Highlighter"
144 (integer :tag "Subexp")
145 face
146 (boolean :tag "Override")
147 (boolean :tag "Laxmatch" :value t))))))
148
149(defcustom ps-run-x '("gs" "-r72" "-sPAPERSIZE=a4")
150 "*Command as list to run PostScript with graphic display."
151 :group 'PostScript-interaction
152 :type '(repeat string))
153
154(defcustom ps-run-dumb '("gs" "-dNODISPLAY")
155 "*Command as list to run PostScript without graphic display."
156 :group 'PostScript-interaction
157 :type '(repeat string))
158
159(defcustom ps-run-init nil
160 "*String of commands to send to PostScript to start interactive.
161
c22d928f 162Example: \"executive\"
99485bca
GM
163
164You won't need to set this option for Ghostscript.
165"
166 :group 'PostScript-interaction
167 :type '(choice (const nil) string))
168
169(defcustom ps-run-error-line-numbers nil
170 "*What values are used by the PostScript interpreter in error messages?"
171 :group 'PostScript-interaction
172 :type '(choice (const :tag "line numbers" t)
173 (const :tag "byte counts" nil)))
174
175(defcustom ps-run-tmp-dir nil
176 "*Name of directory to place temporary file.
177
178If nil, the following are tried in turn, until success:
179 1. \"$TEMP\"
180 2. \"$TMP\"
181 3. \"$HOME/tmp\"
182 4. \"/tmp\"
183"
184 :group 'PostScript-interaction
185 :type '(choice (const nil) directory))
186
187\f
188;; Constants used for font-lock.
189
190;; Only a small set of the PostScript operators is selected for fontification.
191;; Fontification is meant to clarify the document structure and process flow,
192;; fontifying all known PostScript operators would hinder that objective.
193(defconst ps-mode-operators
194 (let ((ops '("clear" "mark" "cleartomark" "counttomark"
195 "forall"
196 "dict" "begin" "end" "def"
197 "true" "false"
198 "exec" "if" "ifelse" "for" "repeat" "loop" "exit"
199 "stop" "stopped" "countexecstack" "execstack"
200 "quit" "start"
201 "save" "restore"
202 "bind" "null"
203 "gsave" "grestore" "grestoreall"
204 "showpage")))
205 (concat "\\<" (regexp-opt ops t) "\\>"))
206 "Regexp of PostScript operators that will be fontified")
207
208;; Level 1 font-lock:
209;; - Special comments (reference face)
210;; - Strings and other comments
211;; - Partial strings (warning face)
212;; - 8bit characters (warning face)
213;; Multiline strings are not supported. Strings with nested brackets are.
214(defconst ps-mode-font-lock-keywords-1
215 '(("\\`%!PS.*" . font-lock-reference-face)
216 ("^%%BoundingBox:[ \t]+-?[0-9]+[ \t]+-?[0-9]+[ \t]+-?[0-9]+[ \t]+-?[0-9]+[ \t]*$"
217 . font-lock-reference-face)
218 (ps-mode-match-string-or-comment
219 (1 font-lock-comment-face nil t)
220 (2 font-lock-string-face nil t))
221 ("([^()\n%]*\\|[^()\n]*)" . font-lock-warning-face)
222 ("[\200-\377]+" (0 font-lock-warning-face prepend nil)))
223 "Subdued level highlighting for PostScript mode.")
224
225;; Level 2 font-lock:
226;; - All from level 1
227;; - PostScript operators (keyword face)
228(defconst ps-mode-font-lock-keywords-2
229 (append
230 ps-mode-font-lock-keywords-1
231 (list
232 (cons
233 ;; exclude names prepended by `/'
234 (concat "\\(^\\|[^/\n]\\)" ps-mode-operators)
235 '(2 font-lock-keyword-face))))
236 "Medium level highlighting for PostScript mode.")
237
238;; Level 3 font-lock:
239;; - All from level 2
240;; - Immediately evaluated names: those starting with `//' (type face)
241;; - Names that look like they are used for the definition of:
242;; * a function
243;; * an array
244;; * a dictionary
245;; * a "global" variable
246;; (function name face)
247;; - Other names (variable name face)
248;; The rules used to determine what names fit in the first category are:
249;; - Only names that are at the left margin, and one of these on the same line:
250;; * Nothing after the name except possibly one or more `[' or a comment
251;; * A `{' or `<<' or `[0-9]+ dict' following the name
252;; * A `def' somewhere in the same line
253;; Names are fontified before PostScript operators, allowing the use of
254;; a more simple (efficient) regexp than the one used in level 2.
255(defconst ps-mode-font-lock-keywords-3
256 (append
257 ps-mode-font-lock-keywords-1
258 (list
259 '("//\\w+" . font-lock-type-face)
260 '("^\\(/\\w+\\)\\>[[ \t]*\\(%.*\\)?\r?$"
261 . (1 font-lock-function-name-face))
262 '("^\\(/\\w+\\)\\>\\([ \t]*{\\|[ \t]*<<\\|.*\\<def\\>\\|[ \t]+[0-9]+[ \t]+dict\\>\\)"
263 . (1 font-lock-function-name-face))
264 '("/\\w+" . font-lock-variable-name-face)
265 (cons ps-mode-operators 'font-lock-keyword-face)))
266 "High level highliting for PostScript mode.")
267
268(defconst ps-mode-font-lock-keywords ps-mode-font-lock-keywords-1
269 "Default expressions to highlight in PostScript mode.")
270
271;; Level 1 font-lock for ps-run-mode
272;; - prompt (function name face)
273(defconst ps-run-font-lock-keywords-1
c22d928f
GM
274 (unless (string= "" ps-run-prompt)
275 (list (cons (if (= ?^ (string-to-char ps-run-prompt))
276 ps-run-prompt
277 (concat "^" ps-run-prompt))
278 'font-lock-function-name-face)))
99485bca
GM
279 "Subdued level highlighting for PostScript run mode.")
280
281(defconst ps-run-font-lock-keywords ps-run-font-lock-keywords-1
282 "Default expressions to highlight in PostScript run mode.")
283
284\f
285;; Variables.
286
287(defvar ps-mode-map nil
288 "Local keymap to use in PostScript mode.")
289
290(defvar ps-mode-syntax-table nil
291 "Syntax table used while in PostScript mode.")
292
293(defvar ps-run-mode-map nil
294 "Local keymap to use in PostScript run mode.")
295
296(defvar ps-mode-tmp-file nil
297 "Name of temporary file, set by `ps-run'.")
298
299(defvar ps-run-mark nil
300 "Mark to start of region that was sent to PostScript interpreter.")
301
302(defvar ps-run-parent nil
303 "Parent window of interactive PostScript.")
304
305\f
306;; Menu
307
308(defconst ps-mode-menu-main
309 '("PostScript"
310 ["EPSF Template, Sparse" ps-mode-epsf-sparse t]
311 ["EPSF Template, Rich" ps-mode-epsf-rich t]
312 "---"
313 ("Cookbook"
314 ["RE" ps-mode-RE t]
315 ["ISOLatin1Extended" ps-mode-latin-extended t]
316 ["center" ps-mode-center t]
317 ["right" ps-mode-right t]
318 ["Heapsort" ps-mode-heapsort t])
319 ("Fonts (1)"
320 ["Times-Roman" (insert "/Times-Roman ") t]
321 ["Times-Bold" (insert "/Times-Bold ") t]
322 ["Times-Italic" (insert "/Times-Italic ") t]
323 ["Times-BoldItalic" (insert "/Times-BoldItalic ") t]
324 ["Helvetica" (insert "/Helvetica ") t]
325 ["Helvetica-Bold" (insert "/Helvetica-Bold ") t]
326 ["Helvetica-Oblique" (insert "/Helvetica-Oblique ") t]
327 ["Helvetica-BoldOblique" (insert "/Helvetica-BoldOblique ") t]
328 ["Courier" (insert "/Courier ") t]
329 ["Courier-Bold" (insert "/Courier-Bold ") t]
330 ["Courier-Oblique" (insert "/Courier-Oblique ") t]
331 ["Courier-BoldOblique" (insert "/Courier-BoldOblique ") t]
332 ["Symbol" (insert "/Symbol") t ])
333 ("Fonts (2)"
334 ["AvantGarde-Book" (insert "/AvantGarde-Book ") t]
335 ["AvantGarde-Demi" (insert "/AvantGarde-Demi ") t]
336 ["AvantGarde-BookOblique" (insert "/AvantGarde-BookOblique ") t]
337 ["AvantGarde-DemiOblique" (insert "/AvantGarde-DemiOblique ") t]
338 ["Bookman-Light" (insert "/Bookman-Light ") t]
339 ["Bookman-Demi" (insert "/Bookman-Demi ") t]
340 ["Bookman-LightItalic" (insert "/Bookman-LightItalic ") t]
341 ["Bookman-DemiItalic" (insert "/Bookman-DemiItalic ") t]
342 ["Helvetica-Narrow" (insert "/Helvetica-Narrow ") t]
343 ["Helvetica-Narrow-Bold" (insert "/Helvetica-Narrow-Bold ") t]
344 ["Helvetica-Narrow-Oblique" (insert "/Helvetica-Narrow-Oblique ") t]
345 ["Helvetica-Narrow-BoldOblique" (insert "/Helvetica-Narrow-BoldOblique ") t]
346 ["NewCenturySchlbk-Roman" (insert "/NewCenturySchlbk-Roman ") t]
347 ["NewCenturySchlbk-Bold" (insert "/NewCenturySchlbk-Bold ") t]
348 ["NewCenturySchlbk-Italic" (insert "/NewCenturySchlbk-Italic ") t]
349 ["NewCenturySchlbk-BoldItalic" (insert "/NewCenturySchlbk-BoldItalic ") t]
350 ["Palatino-Roman" (insert "/Palatino-Roman ") t]
351 ["Palatino-Bold" (insert "/Palatino-Bold ") t]
352 ["Palatino-Italic" (insert "/Palatino-Italic ") t]
353 ["Palatino-BoldItalic" (insert "/Palatino-BoldItalic ") t]
354 ["ZapfChancery-MediumItalic" (insert "/ZapfChancery-MediumItalic ") t]
355 ["ZapfDingbats" (insert "/ZapfDingbats ") t])
356 "---"
357 ["Comment Out Region" ps-mode-comment-out-region (mark t)]
358 ["Uncomment Region" ps-mode-uncomment-region (mark t)]
359 "---"
360 ["8-bit to Octal Buffer" ps-mode-octal-buffer t]
361 ["8-bit to Octal Region" ps-mode-octal-region (mark t)]
362 "---"
363 ("Auto Indent"
364 ["On" (setq ps-mode-auto-indent t) (not ps-mode-auto-indent)]
365 ["Off" (setq ps-mode-auto-indent nil) ps-mode-auto-indent])
366 "---"
367 ["Start PostScript"
368 ps-run-start
369 t]
370 ["Quit PostScript" ps-run-quit (process-status "ps-run")]
371 ["Kill PostScript" ps-run-kill (process-status "ps-run")]
372 ["Send Buffer to Interpreter"
373 ps-run-buffer
374 (process-status "ps-run")]
375 ["Send Region to Interpreter"
376 ps-run-region
377 (and (mark t) (process-status "ps-run"))]
378 ["Send Newline to Interpreter"
379 ps-mode-other-newline
380 (process-status "ps-run")]
381 ["View BoundingBox"
382 ps-run-boundingbox
383 (process-status "ps-run")]
384 ["Clear/Reset PostScript Graphics"
385 ps-run-clear
386 (process-status "ps-run")]
387 "---"
388 ["Print Buffer as PostScript"
389 ps-mode-print-buffer
390 t]
391 ["Print Region as PostScript"
392 ps-mode-print-region
393 (mark t)]
394 "---"
395 ["Customize for PostScript"
396 (customize-group "PostScript")
397 t]))
398
399\f
400;; Mode maps for PostScript edit mode and PostScript interaction mode.
401
402(unless ps-mode-map
403 (setq ps-mode-map (make-sparse-keymap))
c22d928f 404 (define-key ps-mode-map "\C-cv" 'ps-mode-show-version)
99485bca 405 (define-key ps-mode-map "\C-c\C-v" 'ps-run-boundingbox)
c22d928f
GM
406 (define-key ps-mode-map "\C-c\C-u" 'ps-mode-uncomment-region)
407 (define-key ps-mode-map "\C-c\C-t" 'ps-mode-epsf-rich)
408 (define-key ps-mode-map "\C-c\C-s" 'ps-run-start)
409 (define-key ps-mode-map "\C-c\C-r" 'ps-run-region)
410 (define-key ps-mode-map "\C-c\C-q" 'ps-run-quit)
411 (define-key ps-mode-map "\C-c\C-p" 'ps-mode-print-buffer)
412 (define-key ps-mode-map "\C-c\C-o" 'ps-mode-comment-out-region)
413 (define-key ps-mode-map "\C-c\C-k" 'ps-run-kill)
414 (define-key ps-mode-map "\C-c\C-j" 'ps-mode-other-newline)
415 (define-key ps-mode-map "\C-c\C-c" 'ps-run-clear)
416 (define-key ps-mode-map "\C-c\C-b" 'ps-run-buffer)
417 (define-key ps-mode-map ">" 'ps-mode-r-gt)
418 (define-key ps-mode-map "]" 'ps-mode-r-angle)
419 (define-key ps-mode-map "}" 'ps-mode-r-brace)
420 (define-key ps-mode-map "\177" 'ps-mode-backward-delete-char)
421 (define-key ps-mode-map "\t" 'ps-mode-tabkey)
422 (define-key ps-mode-map "\r" 'ps-mode-newline)
423 (define-key ps-mode-map [return] 'ps-mode-newline)
99485bca
GM
424 (easy-menu-define ps-mode-main ps-mode-map "PostScript" ps-mode-menu-main))
425
426(unless ps-run-mode-map
427 (setq ps-run-mode-map (make-sparse-keymap))
99485bca
GM
428 (define-key ps-run-mode-map "\C-c\C-q" 'ps-run-quit)
429 (define-key ps-run-mode-map "\C-c\C-k" 'ps-run-kill)
430 (define-key ps-run-mode-map "\C-c\C-e" 'ps-run-goto-error)
c22d928f
GM
431 (define-key ps-run-mode-map [mouse-2] 'ps-run-mouse-goto-error)
432 (define-key ps-run-mode-map "\r" 'ps-run-newline)
433 (define-key ps-run-mode-map [return] 'ps-run-newline))
99485bca
GM
434
435\f
436;; Syntax table.
437
438(unless ps-mode-syntax-table
439 (setq ps-mode-syntax-table (make-syntax-table))
440
441 (modify-syntax-entry ?\% "< " ps-mode-syntax-table)
442 (modify-syntax-entry ?\n "> " ps-mode-syntax-table)
443 (modify-syntax-entry ?\r "> " ps-mode-syntax-table)
444 (modify-syntax-entry ?\f "> " ps-mode-syntax-table)
445 (modify-syntax-entry ?\< "(>" ps-mode-syntax-table)
446 (modify-syntax-entry ?\> ")<" ps-mode-syntax-table)
447
448 (modify-syntax-entry ?\! "w " ps-mode-syntax-table)
449 (modify-syntax-entry ?\" "w " ps-mode-syntax-table)
450 (modify-syntax-entry ?\# "w " ps-mode-syntax-table)
451 (modify-syntax-entry ?\$ "w " ps-mode-syntax-table)
452 (modify-syntax-entry ?\& "w " ps-mode-syntax-table)
453 (modify-syntax-entry ?\' "w " ps-mode-syntax-table)
454 (modify-syntax-entry ?\* "w " ps-mode-syntax-table)
455 (modify-syntax-entry ?\+ "w " ps-mode-syntax-table)
456 (modify-syntax-entry ?\, "w " ps-mode-syntax-table)
457 (modify-syntax-entry ?\- "w " ps-mode-syntax-table)
458 (modify-syntax-entry ?\. "w " ps-mode-syntax-table)
459 (modify-syntax-entry ?\: "w " ps-mode-syntax-table)
460 (modify-syntax-entry ?\; "w " ps-mode-syntax-table)
461 (modify-syntax-entry ?\= "w " ps-mode-syntax-table)
462 (modify-syntax-entry ?\? "w " ps-mode-syntax-table)
463 (modify-syntax-entry ?\@ "w " ps-mode-syntax-table)
464 (modify-syntax-entry ?\\ "w " ps-mode-syntax-table)
465 (modify-syntax-entry ?^ "w " ps-mode-syntax-table) ; NOT: ?\^
466 (modify-syntax-entry ?\_ "w " ps-mode-syntax-table)
467 (modify-syntax-entry ?\` "w " ps-mode-syntax-table)
468 (modify-syntax-entry ?\| "w " ps-mode-syntax-table)
469 (modify-syntax-entry ?\~ "w " ps-mode-syntax-table)
470
471 (let ((i 128))
472 (while (< i 256)
473 (modify-syntax-entry i "w " ps-mode-syntax-table)
474 (setq i (1+ i)))))
475
476\f
477;; PostScript mode.
478
479;;;###autoload
480(defun ps-mode ()
481 "Major mode for editing PostScript with GNU Emacs.
482
483Entry to this mode calls `ps-mode-hook'.
484
485The following variables hold user options, and can
486be set through the `customize' command:
487
488 ps-mode-auto-indent
489 ps-mode-tab
490 ps-mode-paper-size
491 ps-mode-print-function
99485bca 492 ps-run-prompt
c22d928f 493 ps-run-font-lock-keywords-2
99485bca
GM
494 ps-run-x
495 ps-run-dumb
496 ps-run-init
497 ps-run-error-line-numbers
c22d928f 498 ps-run-tmp-dir
99485bca
GM
499
500Type \\[describe-variable] for documentation on these options.
501
502
503\\{ps-mode-map}
504
505
506When starting an interactive PostScript process with \\[ps-run-start],
507a second window will be displayed, and `ps-run-mode-hook' will be called.
508The keymap for this second window is:
509
510\\{ps-run-mode-map}
511
512
513When Ghostscript encounters an error it displays an error message
514with a file position. Clicking mouse-2 on this number will bring
515point to the corresponding spot in the PostScript window, if input
516to the interpreter was sent from that window.
517Typing \\<ps-run-mode-map>\\[ps-run-goto-error] when the cursor is at the number has the same effect.
518"
519 (interactive)
520 (kill-all-local-variables)
521 (make-local-variable 'font-lock-defaults)
522 (setq font-lock-defaults '((ps-mode-font-lock-keywords
523 ps-mode-font-lock-keywords-1
524 ps-mode-font-lock-keywords-2
525 ps-mode-font-lock-keywords-3)
526 t)
527 major-mode 'ps-mode
528 mode-name "PostScript")
529 (use-local-map ps-mode-map)
530 (set-syntax-table ps-mode-syntax-table)
531 (run-hooks 'ps-mode-hook))
532
c22d928f
GM
533(defun ps-mode-show-version ()
534 "Show current version of PostScript mode"
535 (interactive)
536 (message " *** PostScript Mode (ps-mode) Version %s *** " ps-mode-version))
537
99485bca
GM
538\f
539;; Helper functions for font-lock.
540
541;; When this function is called, point is at an opening bracket.
542;; This function should test if point is at the start of a string
543;; with nested brackets.
544;; If true: move point to end of string
545;; set string to match data nr 2
546;; return new point
547;; If false: return nil
548(defun ps-mode-looking-at-nested (limit)
549 (let ((first (point))
550 (level 1)
551 pos)
552 ;; Move past opening bracket.
553 (forward-char 1)
554 (setq pos (point))
555 (while (and (> level 0) (< pos limit))
556 ;; Search next bracket, stepping over escaped brackets.
557 (if (not (looking-at "\\([^()\\\n]\\|\\\\.\\)*\\([()]\\)"))
558 (setq level -1)
559 (if (string= "(" (match-string 2))
560 (setq level (1+ level))
561 (setq level (1- level)))
562 (goto-char (setq pos (match-end 0)))))
563 (if (not (= level 0))
564 nil
565 ;; Found string with nested brackets, now set match data nr 2.
566 (goto-char first)
567 (re-search-forward "\\(%\\)\\|\\((.*\\)" pos))))
568
569;; This function should search for a string or comment
570;; If comment, return as match data nr 1
571;; If string, return as match data nr 2
572(defun ps-mode-match-string-or-comment (limit)
573 ;; Find the first potential match.
574 (if (not (re-search-forward "[%(]" limit t))
575 ;; Nothing found: return failure.
576 nil
577 (let (end)
578 (goto-char (match-beginning 0))
579 (setq end (match-end 0))
580 (cond ((looking-at "\\(%.*\\)\\|\\((\\([^()\\\n]\\|\\\\.\\)*)\\)")
581 ;; It's a comment or string without nested, unescaped brackets.
582 (goto-char (match-end 0))
583 (point))
584 ((ps-mode-looking-at-nested limit)
585 ;; It's a string with nested brackets.
586 (point))
587 (t
588 ;; Try next match.
589 (goto-char end)
590 (ps-mode-match-string-or-comment limit))))))
591
592\f
593;; Key-handlers.
594
595(defun ps-mode-target-column ()
596 "To what column should text on current line be indented?
597
598Identation is increased if the last token on the current line
599defines the beginning of a group. These tokens are: { [ <<"
600 (save-excursion
601 (beginning-of-line)
602 (if (looking-at "[ \t]*\\(}\\|\\]\\|>>\\)")
603 (condition-case err
604 (progn
605 (goto-char (match-end 0))
606 (backward-sexp 1)
607 (beginning-of-line)
608 (if (looking-at "[ \t]+")
609 (goto-char (match-end 0)))
610 (current-column))
611 (error
612 (ding)
613 (message (error-message-string err))
614 0))
615 (let (target)
616 (if (not (re-search-backward "[^ \t\n\r\f][ \t\n\r\f]*\\=" nil t))
617 0
618 (goto-char (match-beginning 0))
619 (beginning-of-line)
620 (if (looking-at "[ \t]+")
621 (goto-char (match-end 0)))
622 (setq target (current-column))
623 (end-of-line)
624 (if (re-search-backward "\\({\\|\\[\\|<<\\)[ \t]*\\(%[^\n]*\\)?\\=" nil t)
625 (setq target (+ target ps-mode-tab)))
626 target)))))
627
628(defun ps-mode-newline ()
629 "Insert newline with proper indentation."
630 (interactive)
631 (delete-horizontal-space)
632 (insert "\n")
633 (if ps-mode-auto-indent
634 (indent-to (ps-mode-target-column))))
635
636(defun ps-mode-tabkey ()
637 "Indent/reindent current line, or insert tab"
638 (interactive)
639 (let ((column (current-column))
640 target)
641 (if (or (not ps-mode-auto-indent)
642 (< ps-mode-tab 1)
643 (not (re-search-backward "^[ \t]*\\=" nil t)))
644 (insert "\t")
645 (setq target (ps-mode-target-column))
646 (while (<= target column)
647 (setq target (+ target ps-mode-tab)))
648 (delete-horizontal-space)
649 (indent-to target))))
650
651(defun ps-mode-backward-delete-char ()
652 "Delete backward indentation, or delete backward character"
653 (interactive)
654 (let ((column (current-column))
655 target)
656 (if (or (not ps-mode-auto-indent)
657 (< ps-mode-tab 1)
658 (not (re-search-backward "^[ \t]+\\=" nil t)))
659 (delete-backward-char 1)
660 (setq target (ps-mode-target-column))
661 (while (> column target)
662 (setq target (+ target ps-mode-tab)))
663 (while (>= target column)
664 (setq target (- target ps-mode-tab)))
665 (if (< target 0)
666 (setq target 0))
667 (delete-horizontal-space)
668 (indent-to target))))
669
670(defun ps-mode-r-brace ()
671 "Insert `}' and perform balance."
672 (interactive)
673 (insert "}")
674 (ps-mode-r-balance "}"))
675
676(defun ps-mode-r-angle ()
677 "Insert `]' and perform balance."
678 (interactive)
679 (insert "]")
680 (ps-mode-r-balance "]"))
681
682(defun ps-mode-r-gt ()
683 "Insert `>' and perform balance."
684 (interactive)
685 (insert ">")
686 (ps-mode-r-balance ">>"))
687
688(defun ps-mode-r-balance (right)
689 "Adjust indentification if point after RIGHT."
690 (if ps-mode-auto-indent
691 (save-excursion
692 (when (re-search-backward (concat "^[ \t]*" (regexp-quote right) "\\=") nil t)
693 (delete-horizontal-space)
694 (indent-to (ps-mode-target-column)))))
695 (blink-matching-open))
696
697(defun ps-mode-other-newline ()
698 "Perform newline in `*ps run*' buffer"
699 (interactive)
700 (let ((buf (current-buffer)))
701 (set-buffer "*ps run*")
702 (ps-run-newline)
703 (set-buffer buf)))
704
705\f
706;; Print PostScript.
707
708(defun ps-mode-print-buffer ()
709 "Print buffer as PostScript"
710 (interactive)
711 (eval (list ps-mode-print-function)))
712
713(defun ps-mode-print-region (begin end)
714 "Print region as PostScript, adding minimal header and footer lines:
715
716%!PS
717<region>
718showpage
719"
720 (interactive "r")
721 (let ((oldbuf (current-buffer))
722 (tmpbuf (get-buffer-create "*ps print*")))
723 (copy-to-buffer tmpbuf begin end)
724 (set-buffer tmpbuf)
725 (goto-char 1)
726 (insert "%!PS\n")
727 (goto-char (point-max))
728 (insert "\nshowpage\n")
729 (eval (list ps-mode-print-function))
730 (set-buffer oldbuf)
731 (kill-buffer tmpbuf)))
732
733\f
734;; Comment Out / Uncomment.
735
736(defun ps-mode-comment-out-region (begin end)
737 "Comment out region."
738 (interactive "r")
739 (let ((endm (make-marker)))
740 (set-marker endm end)
741 (save-excursion
742 (goto-char begin)
743 (if (= (current-column) 0)
744 (insert "%"))
745 (while (and (= (forward-line) 0)
746 (< (point) (marker-position endm)))
747 (insert "%")))
748 (set-marker endm nil)))
749
750(defun ps-mode-uncomment-region (begin end)
751 "Uncomment region.
752
753Only one `%' is removed, and it has to be in the first column."
754 (interactive "r")
755 (let ((endm (make-marker)))
756 (set-marker endm end)
757 (save-excursion
758 (goto-char begin)
759 (if (looking-at "^%")
760 (delete-char 1))
761 (while (and (= (forward-line) 0)
762 (< (point) (marker-position endm)))
763 (if (looking-at "%")
764 (delete-char 1))))
765 (set-marker endm nil)))
766
767\f
768;; Convert 8-bit to octal codes.
769
770(defun ps-mode-octal-buffer ()
771 "Change 8-bit characters to octal codes in buffer."
772 (interactive)
773 (ps-mode-octal-region (point-min) (point-max)))
774
775(defun ps-mode-octal-region (begin end)
776 "Change 8-bit characters to octal codes in region."
777 (interactive "r")
778 (if buffer-read-only
779 (progn
780 (ding)
781 (message "Buffer is read only"))
782 (save-excursion
783 (let (endm i)
784 (setq endm (make-marker))
785 (set-marker endm end)
786 (goto-char begin)
787 (setq i 0)
788 (while (re-search-forward "[\200-\377]" (marker-position endm) t)
789 (setq i (1+ i))
790 (backward-char)
791 (insert (format "\\%03o" (string-to-char (buffer-substring (point) (1+ (point))))))
792 (delete-char 1))
c22d928f 793 (message "%d change%s made" i (if (= i 1) "" "s"))
99485bca
GM
794 (set-marker endm nil)))))
795
796\f
797;; Cookbook.
798
799(defun ps-mode-center ()
800 "Insert function /center."
801 (interactive)
802 (insert "
803/center {
804 dup stringwidth
805 exch 2 div neg
806 exch 2 div neg
807 rmoveto
808} bind def
809"))
810
811(defun ps-mode-right ()
812 "Insert function /right."
813 (interactive)
814 (insert "
815/right {
816 dup stringwidth
817 exch neg
818 exch neg
819 rmoveto
820} bind def
821"))
822
823(defun ps-mode-RE ()
824 "Insert function /RE."
825 (interactive)
826 (insert "
827% `new-font-name' `encoding-vector' `old-font-name' RE -
828/RE {
829 findfont
830 dup maxlength dict begin {
831 1 index /FID ne { def } { pop pop } ifelse
832 } forall
833 /Encoding exch def
834 dup /FontName exch def
835 currentdict end definefont pop
836} bind def
837"))
838
839(defun ps-mode-latin-extended ()
840 "Insert array /ISOLatin1Extended.
841
842This encoding vector contains all the entries from ISOLatin1Encoding
843plus the usually uncoded characters inserted on positions 1 through 28.
844"
845 (interactive)
846 (insert "
847% ISOLatin1Encoding, extended with remaining uncoded glyphs
848/ISOLatin1Extended [
849 /.notdef /Lslash /lslash /OE /oe /Scaron /scaron /Zcaron /zcaron
850 /Ydieresis /trademark /bullet /dagger /daggerdbl /ellipsis /emdash
851 /endash /fi /fl /florin /fraction /guilsinglleft /guilsinglright
852 /perthousand /quotedblbase /quotedblleft /quotedblright
853 /quotesinglbase /quotesingle /.notdef /.notdef /.notdef /space
854 /exclam /quotedbl /numbersign /dollar /percent /ampersand
855 /quoteright /parenleft /parenright /asterisk /plus /comma /minus
856 /period /slash /zero /one /two /three /four /five /six /seven /eight
857 /nine /colon /semicolon /less /equal /greater /question /at /A /B /C
858 /D /E /F /G /H /I /J /K /L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z
859 /bracketleft /backslash /bracketright /asciicircum /underscore
860 /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o /p /q /r /s
861 /t /u /v /w /x /y /z /braceleft /bar /braceright /asciitilde
862 /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
863 /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
864 /.notdef /.notdef /.notdef /dotlessi /grave /acute /circumflex
865 /tilde /macron /breve /dotaccent /dieresis /.notdef /ring /cedilla
866 /.notdef /hungarumlaut /ogonek /caron /space /exclamdown /cent
867 /sterling /currency /yen /brokenbar /section /dieresis /copyright
868 /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron
869 /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph
870 /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright
871 /onequarter /onehalf /threequarters /questiondown /Agrave /Aacute
872 /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute
873 /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth
874 /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply
875 /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn
876 /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring
877 /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave
878 /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute
879 /ocircumflex /otilde /odieresis /divide /oslash /ugrave /uacute
880 /ucircumflex /udieresis /yacute /thorn /ydieresis
881] def
882"))
883
884(defun ps-mode-heapsort ()
885 "Insert function /Heapsort."
886 (interactive)
887 (insert "
888% `array-element' Heapsort-cvi-or-cvr-or-cvs `number-or-string'
889/Heapsort-cvi-or-cvr-or-cvs {
890 % 0 get
891} bind def
892% `array' Heapsort `sorted-array'
893/Heapsort {
894 dup length /hsR exch def
895 /hsL hsR 2 idiv 1 add def
896 {
897 hsR 2 lt { exit } if
898 hsL 1 gt {
899 /hsL hsL 1 sub def
900 } {
901 /hsR hsR 1 sub def
902 dup dup dup 0 get exch dup hsR get
903 0 exch put
904 hsR exch put
905 } ifelse
906 dup hsL 1 sub get /hsT exch def
907 /hsJ hsL def
908 {
909 /hsS hsJ def
910 /hsJ hsJ dup add def
911 hsJ hsR gt { exit } if
912 hsJ hsR lt {
913 dup dup hsJ 1 sub get Heapsort-cvi-or-cvr-or-cvs
914 exch hsJ get Heapsort-cvi-or-cvr-or-cvs
915 lt { /hsJ hsJ 1 add def } if
916 } if
917 dup hsJ 1 sub get Heapsort-cvi-or-cvr-or-cvs
918 hsT Heapsort-cvi-or-cvr-or-cvs
919 le { exit } if
920 dup dup hsS 1 sub exch hsJ 1 sub get put
921 } loop
922 dup hsS 1 sub hsT put
923 } loop
924} bind def
925"))
926
927\f
928;; EPSF document lay-out.
929
930(defun ps-mode-epsf-sparse ()
931 "Insert sparse EPSF template."
932 (interactive)
933 (goto-char (point-max))
934 (unless (re-search-backward "%%EOF[ \t\n]*\\'" nil t)
935 (goto-char (point-max))
936 (insert "\n%%EOF\n"))
937 (goto-char (point-max))
938 (unless (re-search-backward "\\bshowpage[ \t\n]+%%EOF[ \t\n]*\\'" nil t)
939 (re-search-backward "%%EOF")
940 (insert "showpage\n"))
941 (goto-char (point-max))
942 (unless (re-search-backward "\\bend[ \t\n]+\\bshowpage[ \t\n]+%%EOF[ \t\n]*\\'" nil t)
943 (re-search-backward "showpage")
944 (insert "\nend\n"))
945 (goto-char (point-min))
946 (insert "%!PS-Adobe-3.0 EPSF-3.0\n%%BoundingBox: 0 0 ")
947 (insert (format "%d %d\n\n"
948 (car ps-mode-paper-size)
949 (car (cdr ps-mode-paper-size))))
950 (insert "64 dict begin\n\n"))
951
952(defun ps-mode-epsf-rich ()
953 "Insert rich EPSF template."
954 (interactive)
955 (ps-mode-epsf-sparse)
956 (forward-line -3)
957 (when buffer-file-name
958 (insert "%%Title: " (file-name-nondirectory buffer-file-name) "\n"))
959 (insert "%%Creator: " (user-full-name) "\n")
960 (insert "%%CreationDate: " (current-time-string) "\n")
961 (insert "%%EndComments\n")
962 (forward-line 3))
963
964\f
965;; Interactive PostScript interpreter.
966
967(defun ps-run-mode ()
968 "Major mode in interactive PostScript window.
969This mode is invoked from ps-mode and should not be called directly.
970
971\\{ps-run-mode-map}
972"
973 (kill-all-local-variables)
974 (make-local-variable 'font-lock-defaults)
c22d928f
GM
975 (setq font-lock-defaults '((ps-run-font-lock-keywords
976 ps-run-font-lock-keywords-1
977 ps-run-font-lock-keywords-2)
978 t)
99485bca
GM
979 major-mode 'ps-run-mode
980 mode-name "Interactive PS"
981 mode-line-process '(":%s"))
982 (use-local-map ps-run-mode-map)
983 (run-hooks 'ps-run-mode-hook))
984
985(defun ps-run-running ()
986 "Error if not in ps-mode or not running PostScript."
987 (unless (equal major-mode 'ps-mode)
988 (error "This function can only be called from PostScript mode"))
989 (unless (equal (process-status "ps-run") 'run)
990 (error "No PostScript process running")))
991
992(defun ps-run-start ()
993 "Start interactive PostScript."
994 (interactive)
995 (let ((command (if (and window-system ps-run-x) ps-run-x ps-run-dumb))
996 (init-file nil)
997 (process-connection-type nil)
99485bca
GM
998 (oldwin (selected-window))
999 i)
1000 (unless command
1001 (error "No command specified to run interactive PostScript"))
1002 (unless (and ps-run-mark (markerp ps-run-mark))
1003 (setq ps-run-mark (make-marker)))
1004 (when ps-run-init
1005 (setq init-file (ps-run-make-tmp-filename))
c22d928f 1006 (write-region (concat ps-run-init "\n") 0 init-file)
99485bca
GM
1007 (setq init-file (list init-file)))
1008 (pop-to-buffer "*ps run*")
1009 (ps-run-mode)
1010 (when (process-status "ps-run")
1011 (delete-process "ps-run"))
1012 (erase-buffer)
1013 (setq i (append command init-file))
1014 (while i
1015 (insert (car i) (if (cdr i) " " "\n"))
1016 (setq i (cdr i)))
1017 (eval (append '(start-process "ps-run" "*ps run*") command init-file))
1018 (select-window oldwin)))
1019
1020(defun ps-run-quit ()
1021 "Quit interactive PostScript."
1022 (interactive)
1023 (ps-run-send-string "quit" t)
1024 (ps-run-cleanup))
1025
1026(defun ps-run-kill ()
1027 "Kill interactive PostScript."
1028 (interactive)
1029 (delete-process "ps-run")
1030 (ps-run-cleanup))
1031
1032(defun ps-run-clear ()
1033 "Clear/reset PostScript graphics."
1034 (interactive)
1035 (ps-run-send-string "showpage" t)
1036 (sit-for 1)
1037 (ps-run-send-string "" t))
1038
1039(defun ps-run-buffer ()
1040 "Send buffer to PostScript interpreter."
1041 (interactive)
1042 (ps-run-region (point-min) (point-max)))
1043
1044(defun ps-run-region (begin end)
1045 "Send region to PostScript interpreter."
1046 (interactive "r")
1047 (ps-run-running)
1048 (setq ps-run-parent (buffer-name))
1049 (let ((f (ps-run-make-tmp-filename)))
1050 (set-marker ps-run-mark begin)
1051 (write-region begin end f)
1052 (ps-run-send-string (format "(%s) run" f) t)))
1053
1054(defun ps-run-boundingbox ()
1055 "View BoundingBox"
1056 (interactive)
1057 (ps-run-running)
1058 (let (x1 y1 x2 y2 f
1059 (buf (current-buffer)))
1060 (save-excursion
1061 (goto-char 1)
1062 (re-search-forward
1063 "^%%BoundingBox:[ \t]+\\(-?[0-9]+\\)[ \t]+\\(-?[0-9]+\\)[ \t]+\\(-?[0-9]+\\)[ \t]+\\(-?[0-9]+\\)")
1064 (setq x1 (match-string 1)
1065 y1 (match-string 2)
1066 x2 (match-string 3)
1067 y2 (match-string 4)))
1068 (unless (< (string-to-number x1) (string-to-number x2))
1069 (error "x1 (%s) should be less than x2 (%s)" x1 x2))
1070 (unless (< (string-to-number y1) (string-to-number y2))
1071 (error "y1 (%s) should be less than y2 (%s)" y1 y2))
1072 (setq f (ps-run-make-tmp-filename))
1073 (write-region
1074 (format
1075 "gsave
1076 initgraphics
1077 2 setlinewidth
1078 %s %s moveto
1079 %s %s lineto
1080 %s %s lineto
1081 %s %s lineto
1082 closepath
1083 gsave
1084 [ 4 20 ] 0 setdash
1085 1 0 0 setrgbcolor
1086 stroke
1087 grestore
1088 gsave
1089 [ 4 20 ] 8 setdash
1090 0 1 0 setrgbcolor
1091 stroke
1092 grestore
1093 [ 4 20 ] 16 setdash
1094 0 0 1 setrgbcolor
1095 stroke
1096grestore
1097" x1 y1 x2 y1 x2 y2 x1 y2)
1098 0
1099 f)
1100 (ps-run-send-string (format "(%s) run" f) t)
1101 (set-buffer buf)))
1102
1103(defun ps-run-send-string (string &optional echo)
1104 (let ((oldwin (selected-window)))
1105 (pop-to-buffer "*ps run*")
1106 (goto-char (point-max))
1107 (when echo
1108 (insert string "\n"))
1109 (set-marker (process-mark (get-process "ps-run")) (point))
1110 (process-send-string "ps-run" (concat string "\n"))
1111 (select-window oldwin)))
1112
1113(defun ps-run-make-tmp-filename ()
1114 (unless ps-mode-tmp-file
1115 (cond (ps-run-tmp-dir)
1116 ((setq ps-run-tmp-dir (getenv "TEMP")))
1117 ((setq ps-run-tmp-dir (getenv "TMP")))
1118 ((setq ps-run-tmp-dir (getenv "HOME"))
1119 (setq
1120 ps-run-tmp-dir
1121 (concat (file-name-as-directory ps-run-tmp-dir) "tmp"))
1122 (unless (file-directory-p ps-run-tmp-dir)
1123 (setq ps-run-tmp-dir nil))))
1124 (unless ps-run-tmp-dir
1125 (setq ps-run-tmp-dir "/tmp"))
1126 (setq ps-mode-tmp-file
1127 (make-temp-name
1128 (concat
1129 (if ps-run-tmp-dir
1130 (file-name-as-directory ps-run-tmp-dir)
1131 "")
1132 "ps-run-"))))
1133 ps-mode-tmp-file)
1134
1135;; Remove temporary file
1136;; This shouldn't fail twice, because it is called at kill-emacs
1137(defun ps-run-cleanup ()
1138 (when ps-mode-tmp-file
1139 (let ((i ps-mode-tmp-file))
1140 (setq ps-mode-tmp-file nil)
1141 (when (file-exists-p i)
1142 (delete-file i)))))
1143
1144(defun ps-run-mouse-goto-error (event)
1145 "Set point at mouse click, then call ps-run-goto-error."
1146 (interactive "e")
1147 (mouse-set-point event)
1148 (ps-run-goto-error))
1149
1150(defun ps-run-newline ()
1151 "Process newline in PostScript interpreter window."
1152 (interactive)
1153 (end-of-line)
1154 (insert "\n")
1155 (forward-line -1)
c22d928f 1156 (when (looking-at ps-run-prompt)
99485bca
GM
1157 (goto-char (match-end 0)))
1158 (looking-at ".*")
1159 (goto-char (1+ (match-end 0)))
1160 (ps-run-send-string (buffer-substring (match-beginning 0) (match-end 0))))
1161
1162(defun ps-run-goto-error ()
1163 "Jump to buffer position read as integer at point.
1164Use line numbers if ps-run-error-line-numbers is not nil"
1165 (interactive)
1166 (let ((p (point)))
1167 (unless (looking-at "[0-9]")
1168 (goto-char (max 1 (1- (point)))))
1169 (when (looking-at "[0-9]")
1170 (forward-char 1)
1171 (forward-word -1)
1172 (when (looking-at "[0-9]+")
1173 (let (i)
1174 (setq
1175 i
1176 (string-to-int
1177 (buffer-substring (match-beginning 0) (match-end 0))))
1178 (goto-char p)
1179 (pop-to-buffer ps-run-parent)
1180 (if ps-run-error-line-numbers
1181 (progn
1182 (goto-char (marker-position ps-run-mark))
1183 (forward-line (1- i))
1184 (end-of-line))
1185 (goto-char (+ i (marker-position ps-run-mark)))))))))
1186
1187\f
1188;;
1189(add-hook 'kill-emacs-hook 'ps-run-cleanup)
1190
1191(provide 'ps-mode)
1192
1193;;; ps-mode.el ends here