handa@etlhit.etl.go.jp (Kenichi Handa) (07/09/90)
I've modified the original prolog.el to provide richer indentation facility. Because 'diff -c' has produced a file of almost the same length as whole source codes, I'll post the source. --- Ken'ichi HANDA handa@etl.go.jp ---------------------------------------------------------------------- ;; Major mode for editing Prolog, and for running Prolog under Emacs ;; Copyright (C) 1986, 1987 Free Software Foundation, Inc. ;; Author Masanobu UMEDA (umerin@flab.flab.fujitsu.junet) ;; Modified for rich indentation in Prolog mode inspired by c-mode.el ;; Ken'ichi HANDA (handa@etl.go.jp) 1990.6.13 ;; This file is part of GNU Emacs. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY. No author or distributor ;; accepts responsibility to anyone for the consequences of using it ;; or for whether it serves any particular purpose or works at all, ;; unless he says so in writing. Refer to the GNU Emacs General Public ;; License for full details. ;; Everyone is granted permission to copy, modify and redistribute ;; GNU Emacs, but only under the conditions described in the ;; GNU Emacs General Public License. A copy of this license is ;; supposed to have been given to you along with GNU Emacs so you ;; can know your rights and responsibilities. It should be in a ;; file named COPYING. Among other things, the copyright notice ;; and this notice must be preserved on all copies. (defvar prolog-mode-syntax-table nil) (defvar prolog-mode-abbrev-table nil) (defvar prolog-mode-map nil) (defvar prolog-consult-string "reconsult(user).\n" "*(Re)Consult mode (for C-Prolog and Quintus Prolog). ") (defvar prolog-compile-string "compile(user).\n" "*Compile mode (for Quintus Prolog).") (defvar prolog-eof-string "end_of_file.\n" "*String that represents end of file for prolog. nil means send actual operating system end of file.") ;;; Customizable variables for indentation (defvar prolog-indent-level 4 "*Indentation of Prolog statements with respect to containing block.") (defvar prolog-paren-offset 0 "*Extra indentation for parens, compared with other text in same context.") (defvar prolog-then-else-offset 2 "*Offset of Prolog '->' or ';' lines relative to start of current block.") (defvar prolog-continued-statement-offset 4 "*Extra indent for lines not starting new statements.") (defvar prolog-first-argument-offset 4 "*Extra indent for the first argument relative to a head.") (defvar prolog-term-separator ".*\\(\\.\\|,\\|:-\\|;\\|->\\)\\s *\\($\\|%\\)" "*Regexp to test if the last term of current line is terminated.") (defvar prolog-tab-always-indent t "*Non-nil means TAB in Prolog mode should always reindent the current line, regardless of where in the line point is when the TAB command is used.") (if prolog-mode-syntax-table () (let ((table (make-syntax-table))) (modify-syntax-entry ?_ "w" table) (modify-syntax-entry ?\\ "\\" table) (modify-syntax-entry ?+ "." table) (modify-syntax-entry ?- "." table) (modify-syntax-entry ?= "." table) (modify-syntax-entry ?% "<" table) (modify-syntax-entry ?< "." table) (modify-syntax-entry ?> "." table) (modify-syntax-entry ?\' "\"" table) (modify-syntax-entry ?\n ">" table) (modify-syntax-entry ?\f ">" table) (modify-syntax-entry ?/ ". 14" table) (modify-syntax-entry ?* ". 23" table) (setq prolog-mode-syntax-table table))) (define-abbrev-table 'prolog-mode-abbrev-table ()) (defun prolog-mode-variables () (set-syntax-table prolog-mode-syntax-table) (setq local-abbrev-table prolog-mode-abbrev-table) (make-local-variable 'paragraph-start) (setq paragraph-start (concat "^%%\\|^$\\|" page-delimiter)) ;'%%..' (make-local-variable 'paragraph-separate) (setq paragraph-separate paragraph-start) (make-local-variable 'paragraph-ignore-fill-prefix) (setq paragraph-ignore-fill-prefix t) (make-local-variable 'indent-line-function) (setq indent-line-function 'prolog-indent-line) (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 48) (make-local-variable 'comment-indent-hook) (setq comment-indent-hook 'prolog-comment-indent)) (defun prolog-mode-commands (map) (define-key map "\t" 'prolog-indent-line) (define-key map "\e\C-x" 'prolog-consult-region) (define-key map "(" 'electric-prolog-paren) (define-key map ")" 'electric-prolog-paren) (define-key map ";" 'electric-prolog-paren) (define-key map ":" 'electric-prolog-paren) (define-key map ">" 'electric-prolog-gt) (define-key map "\e\C-q" 'prolog-indent-current-clause)) (if prolog-mode-map nil (setq prolog-mode-map (make-sparse-keymap)) (prolog-mode-commands prolog-mode-map)) (defun prolog-mode () "Major mode for editing Prolog code for Prologs. Blank lines and `%%...' separate paragraphs. `%'s start comments. Commands: \\{prolog-mode-map} Entry to this mode calls the value of prolog-mode-hook if that value is non-nil." (interactive) (kill-all-local-variables) (use-local-map prolog-mode-map) (setq major-mode 'prolog-mode) (setq mode-name "Prolog") (prolog-mode-variables) (run-hooks 'prolog-mode-hook)) (defun electric-prolog-paren (arg) "Insert character and correct line's indentation." (interactive "P") (if (and (not arg) (save-excursion (skip-chars-backward " \t") (bolp))) (progn (insert last-command-char) (prolog-indent-line) (delete-char -1))) (self-insert-command (prefix-numeric-value arg))) (defun electric-prolog-gt (arg) "Insert character and correct line's indentation." (interactive "P") (if (and (not arg) (save-excursion (beginning-of-line) (looking-at "^[ \t]*->$"))) (progn (insert last-command-char) (prolog-indent-line)) (self-insert-command (prefix-numeric-value arg)))) (defun prolog-comment-indent () "Compute Prolog comment indentation." (cond ((looking-at "%%%") 0) ((or (looking-at "%%") (looking-at "/\\*")) (let ((indent (calculate-prolog-indent))) (if (consp indent) (car indent) indent))) (t (save-excursion (skip-chars-backward " \t") (max (1+ (current-column)) ;Insert one space at least comment-column))) )) (defun beginning-of-clause () "Move backward to beginning of current or previous clause." (interactive) (if (re-search-backward "^\\w\\|^\\s_" (point-min) 'mv) (let ((p (point))) (prolog-backward-to-noncomment (point-min)) (if (and (not (bobp)) (or (/= (preceding-char) ?.) (/= (following-char) ?\n))) (beginning-of-clause) (goto-char p))))) (defun end-of-clause () "Move forward to end of current or next clause." (interactive) (let ((p (point))) (while (and (re-search-forward "\\.\n" nil 'move) (/= (1- (point)) (save-excursion (prolog-backward-to-noncomment p) (point))))))) (defun prolog-indent-line () "Indent current line as Prolog code. Return the amount the indentation changed by." (interactive) (let ((indent (calculate-prolog-indent nil)) start-of-block beg shift-amt (case-fold-search nil) (pos (- (point-max) (point)))) (if (listp indent) (progn (setq start-of-block (cdr indent)) (setq indent (car indent))) (setq start-of-block 0)) (beginning-of-line) (setq beg (point)) (setq indent (cond ((eq indent nil) (current-indentation)) ((eq indent t) (calculate-prolog-indent-within-comment)) (t (skip-chars-forward " \t") (cond ((or (looking-at "->") (looking-at ";")) (+ start-of-block prolog-then-else-offset)) ((looking-at "%%%") 0) ((looking-at "%%") indent) ((= (following-char) ?%) comment-column) ((= (following-char) ?\)) start-of-block) ((= (following-char) ?\() (+ indent prolog-paren-offset)) (t indent))))) (skip-chars-forward " \t") (setq shift-amt (- indent (current-column))) (if (zerop shift-amt) (if (> (- (point-max) pos) (point)) (goto-char (- (point-max) pos))) (delete-region beg (point)) (indent-to indent) ;; If initial point was within line's indentation, ;; position after the indentation. Else stay at same point in text. (if (> (- (point-max) pos) (point)) (goto-char (- (point-max) pos)))) shift-amt)) (defun calculate-prolog-indent (&optional parse-start) "Return appropriate indentation for current line as Prolog code. In usual case returns an integer: the column to indent to. Returns nil if line starts inside a string, t if in a comment, \(indent . start-of-block\) if line is within a paren block." (save-excursion (beginning-of-line) (if (= (following-char) ?%) nil (let ((indent-point (point)) (case-fold-search nil) state containing-sexp (following-character (save-excursion (skip-chars-forward " \t") (following-char)))) (if parse-start (goto-char parse-start) (beginning-of-clause)) (while (< (point) indent-point) (setq parse-start (point)) (setq state (parse-partial-sexp (point) indent-point 0)) (setq containing-sexp (car (cdr state)))) (cond ((or (nth 3 state) (nth 4 state)) ;; return nil or t if should not change this line (nth 4 state)) ((null containing-sexp) ;; Line is at top level. ;; Look at previous line that's at column 0 ;; to determine whether we are in top-level decls ;; or within a clause. Set basic-indent accordingly. (goto-char indent-point) (prolog-backward-to-noncomment (or parse-start (point-min))) (let (basic-indent p1 p2) (setq p1 (save-excursion (re-search-backward "\\.$" nil 'mv) (while (save-excursion (beginning-of-line) (and (not (bobp)) (looking-at ".*%.*$"))) (re-search-backward "\\.$" nil 'mv)) (point))) (setq p2 (save-excursion (if (re-search-backward "^\\(\\w\\|\\s_\\)+\\((\\|\\s *:-\\)" nil 'mv) (point) 0))) (setq basic-indent (if (> p1 p2) 0 prolog-indent-level)) ;; Now add a little if this is a continuation line. (+ basic-indent (if (or (bobp) (progn (beginning-of-line) (looking-at prolog-term-separator))) 0 prolog-continued-statement-offset)))) ((or (/= (char-after containing-sexp) ?\() (= (char-syntax (char-after (1- containing-sexp))) ?w)) ;; line is argument, not statement. Return a list. (cons (if (nth 2 state) ;; If not the start of first argument, ;; indent the same amount as the first argument (progn (goto-char (1+ containing-sexp)) (skip-chars-forward " \t\n" (point-max)) (+ (current-column) (progn (goto-char indent-point) (prolog-backward-to-noncomment containing-sexp) (if (= (preceding-char) ?,) 0 prolog-continued-statement-offset)))) ;; the first argument ;; indent to the start of predicate + alpha (goto-char (1- containing-sexp)) (re-search-backward "\\S_\\<" nil 'mv) (forward-char 1) (+ (current-column) prolog-first-argument-offset)) (save-excursion (goto-char containing-sexp) (current-column)))) (t ;; Statement level. Return a list. (let (current-block-indent block-paren) (goto-char containing-sexp) ;; At first, find indentation of current block (setq block-paren (car (cdr (parse-partial-sexp parse-start (point) 0)))) (setq current-block-indent (save-excursion (if (not block-paren) (current-indentation) (goto-char block-paren) (prolog-forward-to-noncomment containing-sexp) (current-column)))) (cons ;; Is line a first statement after an open-paren? (or ;; If no, find that first statement and indent like it. (save-excursion (prolog-forward-to-noncomment indent-point) ;; The first following code counts ;; if it is before the line we want to indent. (and (< (point) indent-point) (+ (current-column) (progn (goto-char indent-point) (forward-line -1) (if (looking-at prolog-term-separator) 0 prolog-continued-statement-offset))))) ;; If no previous statement, ;; indent it relative to line paren is on. (+ current-block-indent prolog-first-argument-offset)) current-block-indent)))))))) (defun calculate-prolog-indent-within-comment () "Return the indentation amount for line, assuming that the current line is to be regarded as part of a block comment." (let (end star-start) (save-excursion (beginning-of-line) (skip-chars-forward " \t") (setq star-start (= (following-char) ?\*)) (skip-chars-backward " \t\n") (setq end (point)) (beginning-of-line) (skip-chars-forward " \t") (and (re-search-forward "/\\*[ \t]*" end t) star-start (goto-char (1+ (match-beginning 0)))) (current-column)))) (defun prolog-backward-to-noncomment (lim) (let (opoint stop) (while (not stop) (skip-chars-backward " \t\n\f" lim) (setq opoint (point)) (if (and (>= (point) (+ 2 lim)) (= (preceding-char) ?/) (= (char-after (- (point) 2)) ?*)) (search-backward "/*" lim 'mv) (let ((p (max lim (save-excursion (beginning-of-line) (point))))) (if (nth 4 (parse-partial-sexp p (point))) (search-backward "%" p 'mv) (goto-char opoint) (setq stop t))))))) (defun prolog-forward-to-noncomment (lim) (forward-char 1) (while (progn (skip-chars-forward " \t\n" lim) (looking-at "%\\|/\\*")) ;; Skip over comments and labels following openparen. (if (= (following-char) ?\%) (forward-line 1) (forward-char 2) (search-forward "*/" lim 'mv)))) (defun mark-prolog-clause () "Put mark at end of current prolog clause, point at beginning." (interactive) (push-mark (point)) (end-of-clause) (push-mark (point)) (beginning-of-clause)) (defun mark-prolog-clauses () "Put mark at end of prolog clause group of the same predicate, point at beginning." (interactive) (push-mark (point)) (if (not (looking-at "^\\(\\sw\\|\\s_\\)+(")) (re-search-backward "^\\(\\sw\\|\\s_\\)+(" nil t)) (let ((predicate (concat "^" (buffer-substring (match-beginning 0) (match-end 0))))) (while (re-search-forward predicate nil t) (end-of-clause)) (push-mark (point)) (while (re-search-backward predicate nil t)))) (defun prolog-indent-current-clause () "Indent all lines in a current Prolog clause." (interactive) (let (p) (save-excursion (end-of-clause) (setq p (point-marker)) (beginning-of-clause) (while (< (point) p) (prolog-indent-line) (forward-line 1))))) ;;; ;;; Inferior prolog mode ;;; (defvar inferior-prolog-mode-map nil) ;; Moved into inferior-prolog-mode ;;(if inferior-prolog-mode-map ;; nil ;; (setq inferior-prolog-mode-map (copy-alist shell-mode-map)) ;; (prolog-mode-commands inferior-prolog-mode-map)) (defun inferior-prolog-mode () "Major mode for interacting with an inferior Prolog process. The following commands are available: \\{inferior-prolog-mode-map} Entry to this mode calls the value of prolog-mode-hook with no arguments, if that value is non-nil. Likewise with the value of shell-mode-hook. prolog-mode-hook is called after shell-mode-hook. You can send text to the inferior Prolog from other buffers using the commands send-region, send-string and \\[prolog-consult-region]. Commands: Tab indents for Prolog; with argument, shifts rest of expression rigidly with the current line. Paragraphs are separated only by blank lines and '%%'. '%'s start comments. Return at end of buffer sends line as input. Return not at end copies rest of line to end and sends it. \\[shell-send-eof] sends end-of-file as input. \\[kill-shell-input] and \\[backward-kill-word] are kill commands, imitating normal Unix input editing. \\[interrupt-shell-subjob] interrupts the shell or its current subjob if any. \\[stop-shell-subjob] stops, likewise. \\[quit-shell-subjob] sends quit signal, likewise." (interactive) (kill-all-local-variables) (setq major-mode 'inferior-prolog-mode) (setq mode-name "Inferior Prolog") (setq mode-line-process '(": %s")) (prolog-mode-variables) (require 'shell) (if inferior-prolog-mode-map nil (setq inferior-prolog-mode-map (copy-keymap shell-mode-map)) (prolog-mode-commands inferior-prolog-mode-map)) (use-local-map inferior-prolog-mode-map) (make-local-variable 'last-input-start) (setq last-input-start (make-marker)) (make-local-variable 'last-input-end) (setq last-input-end (make-marker)) (make-variable-buffer-local 'shell-prompt-pattern) (setq shell-prompt-pattern "^| [ ?][- ] *") ;Set prolog prompt pattern (run-hooks 'shell-mode-hook 'prolog-mode-hook)) (defun run-prolog () "Run an inferior Prolog process, input and output via buffer *prolog*." (interactive) (require 'shell) (switch-to-buffer (make-shell "prolog" "prolog")) (inferior-prolog-mode)) (defun prolog-consult-region (compile beg end) "Send the region to the Prolog process made by M-x run-prolog. If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode." (interactive "P\nr") (save-excursion (if compile (send-string "prolog" prolog-compile-string) (send-string "prolog" prolog-consult-string)) (send-region "prolog" beg end) (send-string "prolog" "\n") ;May be unnecessary (if prolog-eof-string (send-string "prolog" prolog-eof-string) (process-send-eof "prolog")))) ;Send eof to prolog process. (defun prolog-consult-region-and-go (compile beg end) "Send the region to the inferior Prolog, and switch to *prolog* buffer. If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode." (interactive "P\nr") (prolog-consult-region compile beg end) (switch-to-buffer "*prolog*"))