0
|
1 ;;; postscript.el --- major mode for editing PostScript programs
|
|
2
|
|
3 ;; Keywords: langauges
|
|
4
|
|
5 ;; This file is part of XEmacs.
|
|
6
|
|
7 ;; XEmacs is free software; you can redistribute it and/or modify it
|
|
8 ;; under the terms of the GNU General Public License as published by
|
|
9 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
10 ;; any later version.
|
|
11
|
|
12 ;; XEmacs is distributed in the hope that it will be useful, but
|
|
13 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
15 ;; General Public License for more details.
|
|
16
|
|
17 ;; You should have received a copy of the GNU General Public License
|
|
18 ;; along with XEmacs; see the file COPYING. If not, write to the Free
|
|
19 ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
20
|
|
21 ;;; Synched up with: Not in FSF.
|
|
22
|
|
23 ;;
|
|
24 ;; Author: Chris Maio
|
|
25 ;; Last edit: 4 Sep 1988
|
|
26 ;; Includes patches from relph@presto.ig.com (John M. Relph) posted to
|
|
27 ;; gnu.emacs.sources on 22 Nov 90 04:53:43 GMT.
|
|
28 ;;
|
|
29 ;; The following two statements, placed in your .emacs file or site-init.el,
|
|
30 ;; will cause this file to be autoloaded, and postscript-mode invoked, when
|
|
31 ;; visiting .ps or .cps files:
|
|
32 ;;
|
|
33 ;; (autoload 'postscript-mode "postscript.el" "" t)
|
|
34 ;; (setq auto-mode-alist
|
|
35 ;; (cons '("\\.c?ps$".postscript-mode) auto-mode-alist))
|
|
36 ;;
|
|
37
|
|
38 (provide 'postscript)
|
|
39
|
|
40 (defconst ps-indent-level 2
|
|
41 "*Indentation to be used inside of PostScript blocks or arrays")
|
|
42
|
|
43 (defconst ps-tab-width 8
|
|
44 "*Tab stop width for PostScript mode")
|
|
45
|
|
46 (defun ps-make-tabs (stop)
|
|
47 (and (< stop 132) (cons stop (ps-make-tabs (+ stop ps-tab-width)))))
|
|
48
|
|
49 (defconst ps-tab-stop-list (ps-make-tabs ps-tab-width)
|
|
50 "*Tab stop list for PostScript mode")
|
|
51
|
|
52 (defconst ps-postscript-command '("gs" "-")
|
|
53 "*Command used to invoke with a printer spooler or NeWS server.")
|
|
54
|
|
55 (defvar ps-mode-map nil
|
|
56 "Keymap used in PostScript mode buffers")
|
|
57
|
|
58 (defvar ps-mode-syntax-table nil
|
|
59 "PostScript mode syntax table")
|
|
60
|
|
61 (defvar ps-balanced-string-syntax-p
|
|
62 (let ((b (current-buffer))
|
|
63 (loser (generate-new-buffer "x")))
|
|
64 (unwind-protect
|
|
65 (progn
|
|
66 (set-buffer loser)
|
|
67 (set-syntax-table (copy-syntax-table))
|
|
68 (modify-syntax-entry ?\( "\"\)")
|
|
69 (insert "((")
|
|
70 (let ((v (parse-partial-sexp (point-min) (point-max))))
|
|
71 (if (elt v 3)
|
|
72 ;; New syntax code think's we're still inside a string
|
|
73 t
|
|
74 nil)))
|
|
75 (set-buffer b)
|
|
76 (kill-buffer loser))))
|
|
77
|
|
78 (defconst postscript-font-lock-keywords (purecopy
|
|
79 (list
|
|
80 ;; Proper rule for Postscript strings
|
|
81 '("(\\([^)]\\|\\\\.\\|\\\\\n\\)*)" . font-lock-string-face)
|
|
82 ;; Make any line beginning with a / be a ``keyword''
|
|
83 '("^/[^\n%]*" . font-lock-keyword-face)
|
|
84 ;; Make brackets of all forms be keywords
|
|
85 '("[][<>{}]+" . font-lock-keyword-face)
|
|
86 ;; Keywords
|
|
87 (list (concat
|
|
88 "[][ \t\f\n\r()<>{}/%]" ;delimiter
|
|
89 "\\("
|
|
90 (mapconcat 'identity
|
|
91 '("begin" "end"
|
|
92 "save" "restore" "gsave" "grestore"
|
|
93 ;; Any delimited name ending in 'def'
|
|
94 "[a-zA-Z0-9-._]*def"
|
|
95 "[Dd]efine[a-zA-Z0-9-._]*")
|
|
96 "\\|")
|
|
97 "\\)"
|
|
98 "\\([][ \t\f\n\r()<>{}/%]\\|$\\)" ;delimiter
|
|
99 )
|
|
100 1 'font-lock-keyword-face)))
|
|
101 "Expressions to highlight in Postscript buffers.")
|
|
102 (put 'postscript-mode 'font-lock-defaults '(postscript-font-lock-keywords))
|
|
103
|
|
104 (if ps-mode-syntax-table
|
|
105 nil
|
|
106 (let ((i 0))
|
|
107 (setq ps-mode-syntax-table (copy-syntax-table nil))
|
|
108 (while (< i 256)
|
|
109 (or (= (char-syntax i ps-mode-syntax-table) ?w)
|
|
110 (modify-syntax-entry i "_" ps-mode-syntax-table))
|
|
111 (setq i (1+ i)))
|
|
112 (modify-syntax-entry ?\ " " ps-mode-syntax-table)
|
|
113 (modify-syntax-entry ?\t " " ps-mode-syntax-table)
|
|
114 (modify-syntax-entry ?\f " " ps-mode-syntax-table)
|
|
115 (modify-syntax-entry ?\r " " ps-mode-syntax-table)
|
|
116 (modify-syntax-entry ?\% "<" ps-mode-syntax-table)
|
|
117 (modify-syntax-entry ?\n ">" ps-mode-syntax-table)
|
|
118 (modify-syntax-entry ?\\ "\\" ps-mode-syntax-table)
|
|
119 (modify-syntax-entry ?? "_" ps-mode-syntax-table)
|
|
120 (modify-syntax-entry ?_ "_" ps-mode-syntax-table)
|
|
121 (modify-syntax-entry ?. "_" ps-mode-syntax-table)
|
|
122 (modify-syntax-entry ?/ "'" ps-mode-syntax-table)
|
|
123 (if ps-balanced-string-syntax-p
|
|
124 (progn
|
|
125 (modify-syntax-entry ?\( "\"\)" ps-mode-syntax-table)
|
|
126 (modify-syntax-entry ?\) "\"\(" ps-mode-syntax-table))
|
|
127 (progn
|
|
128 ;; This isn't correct, but Emacs syntax stuff
|
|
129 ;; has no way to deal with string syntax which uses
|
|
130 ;; different open and close characters. Sigh.
|
|
131 (modify-syntax-entry ?\( "(" ps-mode-syntax-table)
|
|
132 (modify-syntax-entry ?\) ")" ps-mode-syntax-table)))
|
|
133 (modify-syntax-entry ?\[ "(\]" ps-mode-syntax-table)
|
|
134 (modify-syntax-entry ?\] ")\[" ps-mode-syntax-table)
|
|
135 (modify-syntax-entry ?\{ "\(\}" ps-mode-syntax-table)
|
|
136 (modify-syntax-entry ?\} "\)\}" ps-mode-syntax-table)
|
|
137 (modify-syntax-entry ?/ "' p" ps-mode-syntax-table)
|
|
138 ))
|
|
139
|
|
140
|
|
141 ;;;###autoload
|
|
142 (defun postscript-mode ()
|
|
143 "Major mode for editing PostScript files.
|
|
144
|
|
145 \\[ps-execute-buffer] will send the contents of the buffer to the NeWS
|
|
146 server using psh(1). \\[ps-execute-region] sends the current region.
|
|
147 \\[ps-shell] starts an interactive psh(1) window which will be used for
|
|
148 subsequent \\[ps-execute-buffer] or \\[ps-execute-region] commands.
|
|
149
|
|
150 In this mode, TAB and \\[indent-region] attempt to indent code
|
|
151 based on the position of {}, [], and begin/end pairs. The variable
|
|
152 ps-indent-level controls the amount of indentation used inside
|
|
153 arrays and begin/end pairs.
|
|
154
|
|
155 \\{ps-mode-map}
|
|
156
|
|
157 \\[postscript-mode] calls the value of the variable postscript-mode-hook
|
|
158 with no args, if that value is non-nil."
|
|
159 (interactive)
|
|
160 (kill-all-local-variables)
|
|
161 (use-local-map ps-mode-map)
|
|
162 (set-syntax-table ps-mode-syntax-table)
|
|
163 (make-local-variable 'comment-start)
|
|
164 (setq comment-start "% ")
|
|
165 (make-local-variable 'comment-start-skip)
|
|
166 (setq comment-start-skip "%+ *")
|
|
167 (make-local-variable 'comment-column)
|
|
168 (setq comment-column 40)
|
|
169 (make-local-variable 'indent-line-function)
|
|
170 (setq indent-line-function 'ps-indent-line)
|
|
171 (make-local-variable 'tab-stop-list)
|
|
172 (setq tab-stop-list ps-tab-stop-list)
|
|
173 (make-local-variable 'page-delimiter)
|
|
174 (setq page-delimiter "^showpage")
|
|
175 (make-local-variable 'parse-sexp-ignore-comments)
|
|
176 (setq parse-sexp-ignore-comments t)
|
|
177 (setq mode-name "PostScript")
|
|
178 (setq major-mode 'postscript-mode)
|
|
179 (run-hooks 'ps-mode-hook) ; bad name! Kept for compatibility.
|
|
180 (run-hooks 'postscript-mode-hook)
|
|
181 )
|
|
182
|
|
183 (defun ps-tab ()
|
|
184 "Command assigned to the TAB key in PostScript mode."
|
|
185 (interactive)
|
|
186 (if (save-excursion (skip-chars-backward " \t") (bolp))
|
|
187 (ps-indent-line)
|
|
188 (save-excursion
|
|
189 (ps-indent-line))))
|
|
190
|
|
191 (defun ps-indent-line ()
|
|
192 "Indents a line of PostScript code."
|
|
193 (interactive)
|
|
194 (beginning-of-line)
|
|
195 (delete-horizontal-space)
|
|
196 (if (not (or (looking-at "%%") ; "%%" comments stay at left margin
|
|
197 (ps-top-level-p)))
|
|
198 (if (and (< (point) (point-max))
|
|
199 (eq ?\) (char-syntax (char-after (point)))))
|
|
200 (ps-indent-close) ; indent close-delimiter
|
|
201 (if (looking-at "\\(dict\\|class\\)?end\\|cdef\\|grestore\\|>>")
|
|
202 (ps-indent-end) ; indent end token
|
|
203 (ps-indent-in-block))))) ; indent line after open delimiter
|
|
204
|
|
205 ;(defun ps-open ()
|
|
206 ; (interactive)
|
|
207 ; (insert last-command-char))
|
|
208
|
|
209 (defun ps-insert-d-char (arg)
|
|
210 "Awful hack to make \"end\" and \"cdef\" keywords indent themselves."
|
|
211 (interactive "p")
|
|
212 (insert-char last-command-char arg)
|
|
213 (save-excursion
|
|
214 (beginning-of-line)
|
|
215 (if (looking-at "^[ \t]*\\(\\(dict\\|class\\)?end\\|cdef\\|grestore\\)")
|
|
216 (progn
|
|
217 (delete-horizontal-space)
|
|
218 (ps-indent-end)))))
|
|
219
|
|
220 (defun ps-close ()
|
|
221 "Inserts and indents a close delimiter."
|
|
222 (interactive)
|
|
223 (insert last-command-char)
|
|
224 (backward-char 1)
|
|
225 (ps-indent-close)
|
|
226 (forward-char 1)
|
|
227 (blink-matching-open))
|
|
228
|
|
229 (defun ps-indent-close ()
|
|
230 "Internal function to indent a line containing a an array close delimiter."
|
|
231 (if (save-excursion (skip-chars-backward " \t") (bolp))
|
|
232 (let (x (oldpoint (point)))
|
|
233 (forward-char) (backward-sexp) ;XXX
|
|
234 (if (and (eq 1 (count-lines (point) oldpoint))
|
|
235 (> 1 (- oldpoint (point))))
|
|
236 (goto-char oldpoint)
|
|
237 (beginning-of-line)
|
|
238 (skip-chars-forward " \t")
|
|
239 (setq x (current-column))
|
|
240 (goto-char oldpoint)
|
|
241 (delete-horizontal-space)
|
|
242 (indent-to x)))))
|
|
243
|
|
244 (defun ps-indent-end ()
|
|
245 "Indent an \"end\" token or array close delimiter."
|
|
246 (let ((goal (ps-block-start)))
|
|
247 (if (not goal)
|
|
248 (indent-relative)
|
|
249 (setq goal (save-excursion
|
|
250 (goto-char goal) (back-to-indentation) (current-column)))
|
|
251 (indent-to goal))))
|
|
252
|
|
253 (defun ps-indent-in-block ()
|
|
254 "Indent a line which does not open or close a block."
|
|
255 (let ((goal (ps-block-start)))
|
|
256 (setq goal (save-excursion
|
|
257 (goto-char goal)
|
|
258 (back-to-indentation)
|
|
259 (if (bolp)
|
|
260 ps-indent-level
|
|
261 (back-to-indentation)
|
|
262 (+ (current-column) ps-indent-level))))
|
|
263 (indent-to goal)))
|
|
264
|
|
265 ;;; returns nil if at top-level, or char pos of beginning of current block
|
|
266 (defun ps-block-start ()
|
|
267 "Returns the character position of the character following the nearest
|
|
268 enclosing `[' `{' or `begin' keyword."
|
|
269 (save-excursion
|
|
270 (let ((open (condition-case nil
|
|
271 (save-excursion
|
|
272 (backward-up-list 1)
|
|
273 (1+ (point)))
|
|
274 (error nil))))
|
|
275 (ps-begin-end-hack open))))
|
|
276
|
|
277 (defun ps-begin-end-hack (start)
|
|
278 "Search backwards from point to START for enclosing `begin' and returns the
|
|
279 character number of the character following `begin' or START if not found."
|
|
280 (save-excursion
|
|
281 (let ((depth 1))
|
|
282 (while (and (> depth 0)
|
|
283 (or (re-search-backward "^[ \t]*\\(dict\\|class\\)?\\(end\\|grestore\\|>>\\)\\|\\(begin\\|gsave\\|<<\\)[ \t]*\\(%.*\\)*$"
|
|
284 start t)
|
|
285 (re-search-backward "^[ \t]*cdef.*$" start t)))
|
|
286 (setq depth (if (looking-at "[ \t]*\\(dict\\|class\\)?\\(end\\|grestore\\|>>\\)")
|
|
287 (1+ depth) (1- depth))))
|
|
288 (if (not (eq 0 depth))
|
|
289 start
|
|
290 (forward-word 1)
|
|
291 (point)))))
|
|
292
|
|
293 (defun ps-top-level-p ()
|
|
294 "Awful test to see whether we are inside some sort of PostScript block."
|
|
295 (and (condition-case nil
|
|
296 (not (scan-lists (point) -1 1))
|
|
297 (error t))
|
|
298 (not (ps-begin-end-hack nil))))
|
|
299
|
|
300 ;;; initialize the keymap if it doesn't already exist
|
|
301 (if (null ps-mode-map)
|
|
302 (progn
|
|
303 (setq ps-mode-map (make-sparse-keymap))
|
|
304 (set-keymap-name ps-mode-map 'ps-mode-map)
|
|
305 ;;(define-key ps-mode-map "d" 'ps-insert-d-char)
|
|
306 ;;(define-key ps-mode-map "f" 'ps-insert-d-char)
|
|
307 ;;(define-key ps-mode-map "{" 'ps-open)
|
|
308 ;;(define-key ps-mode-map "}" 'ps-close)
|
|
309 ;;(define-key ps-mode-map "[" 'ps-open)
|
|
310 ;;(define-key ps-mode-map "]" 'ps-close)
|
|
311 (define-key ps-mode-map "\t" 'ps-tab)
|
|
312 (define-key ps-mode-map "\C-c\C-c" 'ps-execute-buffer)
|
|
313 (define-key ps-mode-map "\C-c|" 'ps-execute-region)
|
|
314 ;; make up yout mind! -- the below or the above?
|
|
315 (define-key ps-mode-map "\C-c!" 'ps-shell)
|
|
316 ))
|
|
317
|
|
318 (defun ps-execute-buffer ()
|
|
319 "Send the contents of the buffer to a printer or NeWS server."
|
|
320 (interactive)
|
|
321 (save-excursion
|
|
322 (mark-whole-buffer)
|
|
323 (ps-execute-region (point-min) (point-max))))
|
|
324
|
|
325 (defun ps-execute-region (start end)
|
|
326 "Send the region between START and END to a printer or NeWS server.
|
|
327 You should kill any existing *PostScript* buffer unless you want the
|
|
328 PostScript text to be executed in that process."
|
|
329 (interactive "r")
|
|
330 (let ((start (min (point) (mark)))
|
|
331 (end (max (point) (mark))))
|
|
332 (condition-case nil
|
|
333 (process-send-string "PostScript" (buffer-substring start end))
|
|
334 (error (shell-command-on-region
|
|
335 start end
|
|
336 (mapconcat 'identity ps-postscript-command " ")
|
|
337 nil)))))
|
|
338
|
|
339 (defun ps-shell ()
|
|
340 "Start a shell communicating with a PostScript printer or NeWS server."
|
|
341 (interactive)
|
|
342 (require 'shell)
|
|
343 (switch-to-buffer-other-window
|
|
344 (apply 'make-comint
|
|
345 "PostScript"
|
|
346 (car ps-postscript-command)
|
|
347 nil
|
|
348 (cdr ps-postscript-command)))
|
|
349 (make-local-variable 'shell-prompt-pattern)
|
|
350 ; (setq shell-prompt-pattern "PS>")
|
|
351 (setq shell-prompt-pattern "GS>")
|
|
352 ; (process-send-string "PostScript" "executive\n")
|
|
353 )
|