1 ;;; postscript.el --- major mode for editing PostScript programs
5 ;; This file is part of XEmacs.
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)
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.
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
19 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 ;; Boston, MA 02111-1307, USA.
22 ;;; Synched up with: Not in FSF.
26 ;; Last edit: 4 Sep 1988
27 ;; Includes patches from relph@presto.ig.com (John M. Relph) posted to
28 ;; gnu.emacs.sources on 22 Nov 90 04:53:43 GMT.
30 ;; The following two statements, placed in your .emacs file or site-init.el,
31 ;; will cause this file to be autoloaded, and postscript-mode invoked, when
32 ;; visiting .ps or .cps files:
34 ;; (autoload 'postscript-mode "postscript.el" "" t)
35 ;; (setq auto-mode-alist
36 ;; (cons '("\\.c?ps$".postscript-mode) auto-mode-alist))
41 (defconst ps-indent-level 2
42 "*Indentation to be used inside of PostScript blocks or arrays")
44 (defconst ps-tab-width 8
45 "*Tab stop width for PostScript mode")
47 (defun ps-make-tabs (stop)
48 (and (< stop 132) (cons stop (ps-make-tabs (+ stop ps-tab-width)))))
50 (defconst ps-tab-stop-list (ps-make-tabs ps-tab-width)
51 "*Tab stop list for PostScript mode")
53 (defconst ps-postscript-command '("gs" "-")
54 "*Command used to invoke with a printer spooler or NeWS server.")
56 (defvar ps-mode-map nil
57 "Keymap used in PostScript mode buffers")
59 (defvar ps-mode-syntax-table nil
60 "PostScript mode syntax table")
62 (defvar ps-balanced-string-syntax-p
63 (let ((b (current-buffer))
64 (loser (generate-new-buffer "x")))
68 (set-syntax-table (copy-syntax-table))
69 (modify-syntax-entry ?\( "\"\)")
71 (let ((v (parse-partial-sexp (point-min) (point-max))))
73 ;; New syntax code think's we're still inside a string
77 (kill-buffer loser))))
79 (defconst postscript-font-lock-keywords (purecopy
81 ;; Proper rule for Postscript strings
82 '("(\\([^)]*\\))" . font-lock-string-face)
83 ;; Make any line beginning with a / be a ``keyword''
84 '("^/[^\n%]*" . font-lock-keyword-face)
85 ;; Make brackets of all forms be keywords
86 '("[][<>{}]+" . font-lock-keyword-face)
89 "[][ \t\f\n\r()<>{}/%]" ;delimiter
93 "save" "restore" "gsave" "grestore"
94 ;; Any delimited name ending in 'def'
96 "[Dd]efine[a-zA-Z0-9-._]*")
99 "\\([][ \t\f\n\r()<>{}/%]\\|$\\)" ;delimiter
101 1 'font-lock-keyword-face)))
102 "Expressions to highlight in Postscript buffers.")
103 (put 'postscript-mode 'font-lock-defaults '(postscript-font-lock-keywords))
105 (if ps-mode-syntax-table
108 (setq ps-mode-syntax-table (copy-syntax-table nil))
110 (or (= (char-syntax i ps-mode-syntax-table) ?w)
111 (modify-syntax-entry i "_" ps-mode-syntax-table))
113 (modify-syntax-entry ?\ " " ps-mode-syntax-table)
114 (modify-syntax-entry ?\t " " ps-mode-syntax-table)
115 (modify-syntax-entry ?\f " " ps-mode-syntax-table)
116 (modify-syntax-entry ?\r " " ps-mode-syntax-table)
117 (modify-syntax-entry ?\% "<" ps-mode-syntax-table)
118 (modify-syntax-entry ?\n ">" 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 (modify-syntax-entry ?/ "'" ps-mode-syntax-table)
124 (if ps-balanced-string-syntax-p
126 (modify-syntax-entry ?\( "\"\)" ps-mode-syntax-table)
127 (modify-syntax-entry ?\) "\"\(" ps-mode-syntax-table))
129 ;; This isn't correct, but Emacs syntax stuff
130 ;; has no way to deal with string syntax which uses
131 ;; different open and close characters. Sigh.
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 ?\} "\)\{" ps-mode-syntax-table)
138 (modify-syntax-entry ?/ "' p" ps-mode-syntax-table)
143 (defun postscript-mode ()
144 "Major mode for editing PostScript files.
146 \\[ps-execute-buffer] will send the contents of the buffer to the NeWS
147 server using psh(1). \\[ps-execute-region] sends the current region.
148 \\[ps-shell] starts an interactive psh(1) window which will be used for
149 subsequent \\[ps-execute-buffer] or \\[ps-execute-region] commands.
151 In this mode, TAB and \\[indent-region] attempt to indent code
152 based on the position of {}, [], and begin/end pairs. The variable
153 ps-indent-level controls the amount of indentation used inside
154 arrays and begin/end pairs.
158 \\[postscript-mode] calls the value of the variable postscript-mode-hook
159 with no args, if that value is non-nil."
161 (kill-all-local-variables)
162 (use-local-map ps-mode-map)
163 (set-syntax-table ps-mode-syntax-table)
164 (make-local-variable 'comment-start)
165 (setq comment-start "% ")
166 (make-local-variable 'comment-start-skip)
167 (setq comment-start-skip "%+ *")
168 (make-local-variable 'comment-column)
169 (setq comment-column 40)
170 (make-local-variable 'indent-line-function)
171 (setq indent-line-function 'ps-indent-line)
172 (make-local-variable 'tab-stop-list)
173 (setq tab-stop-list ps-tab-stop-list)
174 (make-local-variable 'page-delimiter)
175 (setq page-delimiter "^showpage")
176 (make-local-variable 'parse-sexp-ignore-comments)
177 (setq parse-sexp-ignore-comments t)
178 (setq mode-name "PostScript")
179 (setq major-mode 'postscript-mode)
180 (run-hooks 'ps-mode-hook) ; bad name! Kept for compatibility.
181 (run-hooks 'postscript-mode-hook)
185 "Command assigned to the TAB key in PostScript mode."
187 (if (save-excursion (skip-chars-backward " \t") (bolp))
192 (defun ps-indent-line ()
193 "Indents a line of PostScript code."
196 (delete-horizontal-space)
197 (if (not (or (looking-at "%%") ; "%%" comments stay at left margin
199 (if (and (< (point) (point-max))
200 (eq ?\) (char-syntax (char-after (point)))))
201 (ps-indent-close) ; indent close-delimiter
202 (if (looking-at "\\(dict\\|class\\)?end\\|cdef\\|grestore\\|>>")
203 (ps-indent-end) ; indent end token
204 (ps-indent-in-block))))) ; indent line after open delimiter
208 ; (insert last-command-char))
210 (defun ps-insert-d-char (arg)
211 "Awful hack to make \"end\" and \"cdef\" keywords indent themselves."
213 (insert-char last-command-char arg)
216 (if (looking-at "^[ \t]*\\(\\(dict\\|class\\)?end\\|cdef\\|grestore\\)")
218 (delete-horizontal-space)
222 "Inserts and indents a close delimiter."
224 (insert last-command-char)
228 (blink-matching-open))
230 (defun ps-indent-close ()
231 "Internal function to indent a line containing a an array close delimiter."
232 (if (save-excursion (skip-chars-backward " \t") (bolp))
233 (let (x (oldpoint (point)))
234 (forward-char) (backward-sexp) ;XXX
235 (if (and (eq 1 (count-lines (point) oldpoint))
236 (> 1 (- oldpoint (point))))
239 (skip-chars-forward " \t")
240 (setq x (current-column))
242 (delete-horizontal-space)
245 (defun ps-indent-end ()
246 "Indent an \"end\" token or array close delimiter."
247 (let ((goal (ps-block-start)))
250 (setq goal (save-excursion
251 (goto-char goal) (back-to-indentation) (current-column)))
254 (defun ps-indent-in-block ()
255 "Indent a line which does not open or close a block."
256 (let ((goal (ps-block-start)))
257 (setq goal (save-excursion
259 (back-to-indentation)
262 (back-to-indentation)
263 (+ (current-column) ps-indent-level))))
266 ;;; returns nil if at top-level, or char pos of beginning of current block
267 (defun ps-block-start ()
268 "Returns the character position of the character following the nearest
269 enclosing `[' `{' or `begin' keyword."
271 (let ((open (condition-case nil
276 (ps-begin-end-hack open))))
278 (defun ps-begin-end-hack (start)
279 "Search backwards from point to START for enclosing `begin' and returns the
280 character number of the character following `begin' or START if not found."
283 (while (and (> depth 0)
284 (or (re-search-backward "^[ \t]*\\(dict\\|class\\)?\\(end\\|grestore\\|>>\\)\\|\\(begin\\|gsave\\|<<\\)[ \t]*\\(%.*\\)*$"
286 (re-search-backward "^[ \t]*cdef.*$" start t)))
287 (setq depth (if (looking-at "[ \t]*\\(dict\\|class\\)?\\(end\\|grestore\\|>>\\)")
288 (1+ depth) (1- depth))))
289 (if (not (eq 0 depth))
294 (defun ps-top-level-p ()
295 "Awful test to see whether we are inside some sort of PostScript block."
296 (and (condition-case nil
297 (not (scan-lists (point) -1 1))
299 (not (ps-begin-end-hack nil))))
301 ;;; initialize the keymap if it doesn't already exist
302 (if (null ps-mode-map)
304 (setq ps-mode-map (make-sparse-keymap))
305 (set-keymap-name ps-mode-map 'ps-mode-map)
306 ;;(define-key ps-mode-map "d" 'ps-insert-d-char)
307 ;;(define-key ps-mode-map "f" 'ps-insert-d-char)
308 ;;(define-key ps-mode-map "{" 'ps-open)
309 ;;(define-key ps-mode-map "}" 'ps-close)
310 ;;(define-key ps-mode-map "[" 'ps-open)
311 ;;(define-key ps-mode-map "]" 'ps-close)
312 (define-key ps-mode-map "\t" 'ps-tab)
313 (define-key ps-mode-map "\C-c\C-c" 'ps-execute-buffer)
314 (define-key ps-mode-map "\C-c|" 'ps-execute-region)
315 ;; make up yout mind! -- the below or the above?
316 (define-key ps-mode-map "\C-c!" 'ps-shell)
319 (defun ps-execute-buffer ()
320 "Send the contents of the buffer to a printer or NeWS server."
324 (ps-execute-region (point-min) (point-max))))
326 (defun ps-execute-region (start end)
327 "Send the region between START and END to a printer or NeWS server.
328 You should kill any existing *PostScript* buffer unless you want the
329 PostScript text to be executed in that process."
331 (let ((start (min (point) (mark)))
332 (end (max (point) (mark))))
334 (process-send-string "PostScript" (buffer-substring start end))
335 (error (shell-command-on-region
337 (mapconcat 'identity ps-postscript-command " ")
341 "Start a shell communicating with a PostScript printer or NeWS server."
344 (switch-to-buffer-other-window
347 (car ps-postscript-command)
349 (cdr ps-postscript-command)))
350 (make-local-variable 'shell-prompt-pattern)
351 ; (setq shell-prompt-pattern "PS>")
352 (setq shell-prompt-pattern "GS>")
353 ; (process-send-string "PostScript" "executive\n")
357 ;;;###autoload(add-to-list 'auto-mode-alist '("\\.c?ps\\'" . postscript-mode))