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*"))