Mercurial > hg > xemacs-beta
diff lisp/modes/postscript.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 0293115a14e9 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/modes/postscript.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,353 @@ +;;; postscript.el --- major mode for editing PostScript programs + +;; Keywords: langauges + +;; This file is part of XEmacs. + +;; XEmacs is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; XEmacs is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with XEmacs; see the file COPYING. If not, write to the Free +;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +;;; Synched up with: Not in FSF. + +;; +;; Author: Chris Maio +;; Last edit: 4 Sep 1988 +;; Includes patches from relph@presto.ig.com (John M. Relph) posted to +;; gnu.emacs.sources on 22 Nov 90 04:53:43 GMT. +;; +;; The following two statements, placed in your .emacs file or site-init.el, +;; will cause this file to be autoloaded, and postscript-mode invoked, when +;; visiting .ps or .cps files: +;; +;; (autoload 'postscript-mode "postscript.el" "" t) +;; (setq auto-mode-alist +;; (cons '("\\.c?ps$".postscript-mode) auto-mode-alist)) +;; + +(provide 'postscript) + +(defconst ps-indent-level 2 + "*Indentation to be used inside of PostScript blocks or arrays") + +(defconst ps-tab-width 8 + "*Tab stop width for PostScript mode") + +(defun ps-make-tabs (stop) + (and (< stop 132) (cons stop (ps-make-tabs (+ stop ps-tab-width))))) + +(defconst ps-tab-stop-list (ps-make-tabs ps-tab-width) + "*Tab stop list for PostScript mode") + +(defconst ps-postscript-command '("gs" "-") + "*Command used to invoke with a printer spooler or NeWS server.") + +(defvar ps-mode-map nil + "Keymap used in PostScript mode buffers") + +(defvar ps-mode-syntax-table nil + "PostScript mode syntax table") + +(defvar ps-balanced-string-syntax-p + (let ((b (current-buffer)) + (loser (generate-new-buffer "x"))) + (unwind-protect + (progn + (set-buffer loser) + (set-syntax-table (copy-syntax-table)) + (modify-syntax-entry ?\( "\"\)") + (insert "((") + (let ((v (parse-partial-sexp (point-min) (point-max)))) + (if (elt v 3) + ;; New syntax code think's we're still inside a string + t + nil))) + (set-buffer b) + (kill-buffer loser)))) + +(defconst postscript-font-lock-keywords (purecopy + (list + ;; Proper rule for Postscript strings + '("(\\([^)]\\|\\\\.\\|\\\\\n\\)*)" . font-lock-string-face) + ;; Make any line beginning with a / be a ``keyword'' + '("^/[^\n%]*" . font-lock-keyword-face) + ;; Make brackets of all forms be keywords + '("[][<>{}]+" . font-lock-keyword-face) + ;; Keywords + (list (concat + "[][ \t\f\n\r()<>{}/%]" ;delimiter + "\\(" + (mapconcat 'identity + '("begin" "end" + "save" "restore" "gsave" "grestore" + ;; Any delimited name ending in 'def' + "[a-zA-Z0-9-._]*def" + "[Dd]efine[a-zA-Z0-9-._]*") + "\\|") + "\\)" + "\\([][ \t\f\n\r()<>{}/%]\\|$\\)" ;delimiter + ) + 1 'font-lock-keyword-face))) + "Expressions to highlight in Postscript buffers.") +(put 'postscript-mode 'font-lock-defaults '(postscript-font-lock-keywords)) + +(if ps-mode-syntax-table + nil + (let ((i 0)) + (setq ps-mode-syntax-table (copy-syntax-table nil)) + (while (< i 256) + (or (= (char-syntax i ps-mode-syntax-table) ?w) + (modify-syntax-entry i "_" ps-mode-syntax-table)) + (setq i (1+ i))) + (modify-syntax-entry ?\ " " ps-mode-syntax-table) + (modify-syntax-entry ?\t " " ps-mode-syntax-table) + (modify-syntax-entry ?\f " " ps-mode-syntax-table) + (modify-syntax-entry ?\r " " ps-mode-syntax-table) + (modify-syntax-entry ?\% "<" ps-mode-syntax-table) + (modify-syntax-entry ?\n ">" ps-mode-syntax-table) + (modify-syntax-entry ?\\ "\\" ps-mode-syntax-table) + (modify-syntax-entry ?? "_" ps-mode-syntax-table) + (modify-syntax-entry ?_ "_" ps-mode-syntax-table) + (modify-syntax-entry ?. "_" ps-mode-syntax-table) + (modify-syntax-entry ?/ "'" ps-mode-syntax-table) + (if ps-balanced-string-syntax-p + (progn + (modify-syntax-entry ?\( "\"\)" ps-mode-syntax-table) + (modify-syntax-entry ?\) "\"\(" ps-mode-syntax-table)) + (progn + ;; This isn't correct, but Emacs syntax stuff + ;; has no way to deal with string syntax which uses + ;; different open and close characters. Sigh. + (modify-syntax-entry ?\( "(" ps-mode-syntax-table) + (modify-syntax-entry ?\) ")" ps-mode-syntax-table))) + (modify-syntax-entry ?\[ "(\]" ps-mode-syntax-table) + (modify-syntax-entry ?\] ")\[" ps-mode-syntax-table) + (modify-syntax-entry ?\{ "\(\}" ps-mode-syntax-table) + (modify-syntax-entry ?\} "\)\}" ps-mode-syntax-table) + (modify-syntax-entry ?/ "' p" ps-mode-syntax-table) + )) + + +;;;###autoload +(defun postscript-mode () + "Major mode for editing PostScript files. + +\\[ps-execute-buffer] will send the contents of the buffer to the NeWS +server using psh(1). \\[ps-execute-region] sends the current region. +\\[ps-shell] starts an interactive psh(1) window which will be used for +subsequent \\[ps-execute-buffer] or \\[ps-execute-region] commands. + +In this mode, TAB and \\[indent-region] attempt to indent code +based on the position of {}, [], and begin/end pairs. The variable +ps-indent-level controls the amount of indentation used inside +arrays and begin/end pairs. + +\\{ps-mode-map} + +\\[postscript-mode] calls the value of the variable postscript-mode-hook +with no args, if that value is non-nil." + (interactive) + (kill-all-local-variables) + (use-local-map ps-mode-map) + (set-syntax-table ps-mode-syntax-table) + (make-local-variable 'comment-start) + (setq comment-start "% ") + (make-local-variable 'comment-start-skip) + (setq comment-start-skip "%+ *") + (make-local-variable 'comment-column) + (setq comment-column 40) + (make-local-variable 'indent-line-function) + (setq indent-line-function 'ps-indent-line) + (make-local-variable 'tab-stop-list) + (setq tab-stop-list ps-tab-stop-list) + (make-local-variable 'page-delimiter) + (setq page-delimiter "^showpage") + (make-local-variable 'parse-sexp-ignore-comments) + (setq parse-sexp-ignore-comments t) + (setq mode-name "PostScript") + (setq major-mode 'postscript-mode) + (run-hooks 'ps-mode-hook) ; bad name! Kept for compatibility. + (run-hooks 'postscript-mode-hook) + ) + +(defun ps-tab () + "Command assigned to the TAB key in PostScript mode." + (interactive) + (if (save-excursion (skip-chars-backward " \t") (bolp)) + (ps-indent-line) + (save-excursion + (ps-indent-line)))) + +(defun ps-indent-line () + "Indents a line of PostScript code." + (interactive) + (beginning-of-line) + (delete-horizontal-space) + (if (not (or (looking-at "%%") ; "%%" comments stay at left margin + (ps-top-level-p))) + (if (and (< (point) (point-max)) + (eq ?\) (char-syntax (char-after (point))))) + (ps-indent-close) ; indent close-delimiter + (if (looking-at "\\(dict\\|class\\)?end\\|cdef\\|grestore\\|>>") + (ps-indent-end) ; indent end token + (ps-indent-in-block))))) ; indent line after open delimiter + +;(defun ps-open () +; (interactive) +; (insert last-command-char)) + +(defun ps-insert-d-char (arg) + "Awful hack to make \"end\" and \"cdef\" keywords indent themselves." + (interactive "p") + (insert-char last-command-char arg) + (save-excursion + (beginning-of-line) + (if (looking-at "^[ \t]*\\(\\(dict\\|class\\)?end\\|cdef\\|grestore\\)") + (progn + (delete-horizontal-space) + (ps-indent-end))))) + +(defun ps-close () + "Inserts and indents a close delimiter." + (interactive) + (insert last-command-char) + (backward-char 1) + (ps-indent-close) + (forward-char 1) + (blink-matching-open)) + +(defun ps-indent-close () + "Internal function to indent a line containing a an array close delimiter." + (if (save-excursion (skip-chars-backward " \t") (bolp)) + (let (x (oldpoint (point))) + (forward-char) (backward-sexp) ;XXX + (if (and (eq 1 (count-lines (point) oldpoint)) + (> 1 (- oldpoint (point)))) + (goto-char oldpoint) + (beginning-of-line) + (skip-chars-forward " \t") + (setq x (current-column)) + (goto-char oldpoint) + (delete-horizontal-space) + (indent-to x))))) + +(defun ps-indent-end () + "Indent an \"end\" token or array close delimiter." + (let ((goal (ps-block-start))) + (if (not goal) + (indent-relative) + (setq goal (save-excursion + (goto-char goal) (back-to-indentation) (current-column))) + (indent-to goal)))) + +(defun ps-indent-in-block () + "Indent a line which does not open or close a block." + (let ((goal (ps-block-start))) + (setq goal (save-excursion + (goto-char goal) + (back-to-indentation) + (if (bolp) + ps-indent-level + (back-to-indentation) + (+ (current-column) ps-indent-level)))) + (indent-to goal))) + +;;; returns nil if at top-level, or char pos of beginning of current block +(defun ps-block-start () + "Returns the character position of the character following the nearest +enclosing `[' `{' or `begin' keyword." + (save-excursion + (let ((open (condition-case nil + (save-excursion + (backward-up-list 1) + (1+ (point))) + (error nil)))) + (ps-begin-end-hack open)))) + +(defun ps-begin-end-hack (start) + "Search backwards from point to START for enclosing `begin' and returns the +character number of the character following `begin' or START if not found." + (save-excursion + (let ((depth 1)) + (while (and (> depth 0) + (or (re-search-backward "^[ \t]*\\(dict\\|class\\)?\\(end\\|grestore\\|>>\\)\\|\\(begin\\|gsave\\|<<\\)[ \t]*\\(%.*\\)*$" + start t) + (re-search-backward "^[ \t]*cdef.*$" start t))) + (setq depth (if (looking-at "[ \t]*\\(dict\\|class\\)?\\(end\\|grestore\\|>>\\)") + (1+ depth) (1- depth)))) + (if (not (eq 0 depth)) + start + (forward-word 1) + (point))))) + +(defun ps-top-level-p () + "Awful test to see whether we are inside some sort of PostScript block." + (and (condition-case nil + (not (scan-lists (point) -1 1)) + (error t)) + (not (ps-begin-end-hack nil)))) + +;;; initialize the keymap if it doesn't already exist +(if (null ps-mode-map) + (progn + (setq ps-mode-map (make-sparse-keymap)) + (set-keymap-name ps-mode-map 'ps-mode-map) + ;;(define-key ps-mode-map "d" 'ps-insert-d-char) + ;;(define-key ps-mode-map "f" 'ps-insert-d-char) + ;;(define-key ps-mode-map "{" 'ps-open) + ;;(define-key ps-mode-map "}" 'ps-close) + ;;(define-key ps-mode-map "[" 'ps-open) + ;;(define-key ps-mode-map "]" 'ps-close) + (define-key ps-mode-map "\t" 'ps-tab) + (define-key ps-mode-map "\C-c\C-c" 'ps-execute-buffer) + (define-key ps-mode-map "\C-c|" 'ps-execute-region) + ;; make up yout mind! -- the below or the above? + (define-key ps-mode-map "\C-c!" 'ps-shell) + )) + +(defun ps-execute-buffer () + "Send the contents of the buffer to a printer or NeWS server." + (interactive) + (save-excursion + (mark-whole-buffer) + (ps-execute-region (point-min) (point-max)))) + +(defun ps-execute-region (start end) + "Send the region between START and END to a printer or NeWS server. +You should kill any existing *PostScript* buffer unless you want the +PostScript text to be executed in that process." + (interactive "r") + (let ((start (min (point) (mark))) + (end (max (point) (mark)))) + (condition-case nil + (process-send-string "PostScript" (buffer-substring start end)) + (error (shell-command-on-region + start end + (mapconcat 'identity ps-postscript-command " ") + nil))))) + +(defun ps-shell () + "Start a shell communicating with a PostScript printer or NeWS server." + (interactive) + (require 'shell) + (switch-to-buffer-other-window + (apply 'make-comint + "PostScript" + (car ps-postscript-command) + nil + (cdr ps-postscript-command))) + (make-local-variable 'shell-prompt-pattern) +; (setq shell-prompt-pattern "PS>") + (setq shell-prompt-pattern "GS>") +; (process-send-string "PostScript" "executive\n") + )