w32-fns.el (w32-convert-standard-filename): Doc fix.
[bpt/emacs.git] / lisp / progmodes / prolog.el
index 2ad1ee2..64277dc 100644 (file)
@@ -1,17 +1,17 @@
 ;;; prolog.el --- major mode for editing and running Prolog under Emacs
 
-;; Copyright (C) 1986, 1987, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
-;; Free Software Foundation, Inc.
+;; Copyright (C) 1986, 1987, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+;;   2008, 2009, 2010  Free Software Foundation, Inc.
 
 ;; Author: Masanobu UMEDA <umerin@mse.kyutech.ac.jp>
 ;; Keywords: languages
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs 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 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -19,9 +19,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -33,6 +31,7 @@
 
 (defvar comint-prompt-regexp)
 (defvar comint-process-echoes)
+(defvar smie-indent-basic)
 
 (defgroup prolog nil
   "Major mode for editing and running Prolog under Emacs."
@@ -100,6 +99,61 @@ When nil, send actual operating system end of file."
 (defvar prolog-mode-abbrev-table nil)
 (define-abbrev-table 'prolog-mode-abbrev-table ())
 
+(defconst prolog-smie-op-levels
+  ;; Rather than construct the operator levels table from the BNF,
+  ;; we directly provide the operator precedences from GNU Prolog's
+  ;; manual.  The only problem is that GNU Prolog's manual uses
+  ;; precedence levels in the opposite sense (higher numbers bind less
+  ;; tightly) than SMIE, so we use negative numbers.
+  '(("." -10000 -10000)
+    (":-" -1200 -1200)
+    ("-->" -1200 -1200)
+    (";" -1100 -1100)
+    ("->" -1050 -1050)
+    ("," -1000 -1000)
+    ("\\+" -900 -900)
+    ("=" -700 -700)
+    ("\\=" -700 -700)
+    ("=.." -700 -700)
+    ("==" -700 -700)
+    ("\\==" -700 -700)
+    ("@<" -700 -700)
+    ("@=<" -700 -700)
+    ("@>" -700 -700)
+    ("@>=" -700 -700)
+    ("is" -700 -700)
+    ("=:=" -700 -700)
+    ("=\\=" -700 -700)
+    ("<" -700 -700)
+    ("=<" -700 -700)
+    (">" -700 -700)
+    (">=" -700 -700)
+    (":" -600 -600)
+    ("+" -500 -500)
+    ("-" -500 -500)
+    ("/\\" -500 -500)
+    ("\\/" -500 -500)
+    ("*" -400 -400)
+    ("/" -400 -400)
+    ("//" -400 -400)
+    ("rem" -400 -400)
+    ("mod" -400 -400)
+    ("<<" -400 -400)
+    (">>" -400 -400)
+    ("**" -200 -200)
+    ("^" -200 -200)
+    ;; Prefix
+    ;; ("+" 200 200)
+    ;; ("-" 200 200)
+    ;; ("\\" 200 200)
+    )
+  "Precedence levels of infix operators.")
+
+(defconst prolog-smie-indent-rules
+  '((":-")
+    ("->"))
+  "Prolog indentation rules.")
+
 (defun prolog-mode-variables ()
   (make-local-variable 'paragraph-separate)
   (setq paragraph-separate (concat "%%\\|$\\|" page-delimiter)) ;'%%..'
@@ -107,8 +161,10 @@ When nil, send actual operating system end of file."
   (setq paragraph-ignore-fill-prefix t)
   (make-local-variable 'imenu-generic-expression)
   (setq imenu-generic-expression '((nil "^\\sw+" 0)))
-  (make-local-variable 'indent-line-function)
-  (setq indent-line-function 'prolog-indent-line)
+  (smie-setup prolog-smie-op-levels prolog-smie-indent-rules)
+  (set (make-local-variable 'forward-sexp-function)
+       'smie-forward-sexp-command)
+  (set (make-local-variable 'smie-indent-basic) prolog-indent-width)
   (make-local-variable 'comment-start)
   (setq comment-start "%")
   (make-local-variable 'comment-start-skip)
@@ -124,7 +180,7 @@ When nil, send actual operating system end of file."
     (define-key map "\C-c\C-l" 'inferior-prolog-load-file)
     (define-key map "\C-c\C-z" 'switch-to-prolog)
     map))
+
 (easy-menu-define prolog-mode-menu prolog-mode-map "Menu for Prolog mode."
   ;; Mostly copied from scheme-mode's menu.
   ;; Not tremendously useful, but it's a start.
@@ -138,85 +194,18 @@ When nil, send actual operating system end of file."
     ))
 
 ;;;###autoload
-(defun prolog-mode ()
+(define-derived-mode prolog-mode prog-mode "Prolog"
   "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)
-  (set-syntax-table prolog-mode-syntax-table)
-  (setq major-mode 'prolog-mode)
-  (setq mode-name "Prolog")
   (prolog-mode-variables)
   (set (make-local-variable 'comment-add) 1)
-  ;; font lock
   (setq font-lock-defaults '(prolog-font-lock-keywords
                              nil nil nil
-                             beginning-of-line))
-  (run-mode-hooks 'prolog-mode-hook))
-
-(defun prolog-indent-line ()
-  "Indent current line as Prolog code.
-With argument, indent any additional lines of the same clause
-rigidly along with this one (not yet)."
-  (interactive "p")
-  (let ((indent (prolog-indent-level))
-       (pos (- (point-max) (point))))
-    (beginning-of-line)
-    (indent-line-to indent)
-    (if (> (- (point-max) pos) (point))
-       (goto-char (- (point-max) pos)))))
-
-(defun prolog-indent-level ()
-  "Compute Prolog indentation level."
-  (save-excursion
-    (beginning-of-line)
-    (skip-chars-forward " \t")
-    (cond
-     ((looking-at "%%%") 0)            ;Large comment starts
-     ((looking-at "%[^%]") comment-column) ;Small comment starts
-     ((bobp) 0)                                ;Beginning of buffer
-     (t
-      (let ((empty t) ind more less)
-       (if (looking-at ")")
-           (setq less t)               ;Find close
-         (setq less nil))
-       ;; See previous indentation
-       (while empty
-         (forward-line -1)
-         (beginning-of-line)
-         (if (bobp)
-             (setq empty nil)
-           (skip-chars-forward " \t")
-           (if (not (or (looking-at "%[^%]") (looking-at "\n")))
-               (setq empty nil))))
-       (if (bobp)
-           (setq ind 0)                ;Beginning of buffer
-         (setq ind (current-column)))  ;Beginning of clause
-       ;; See its beginning
-       (if (looking-at "%%[^%]")
-           ind
-         ;; Real prolog code
-         (if (looking-at "(")
-             (setq more t)             ;Find open
-           (setq more nil))
-         ;; See its tail
-         (end-of-prolog-clause)
-         (or (bobp) (forward-char -1))
-         (cond ((looking-at "[,(;>]")
-                (if (and more (looking-at "[^,]"))
-                    (+ ind prolog-indent-width) ;More indentation
-                  (max tab-width ind))) ;Same indentation
-               ((looking-at "-") tab-width) ;TAB
-               ((or less (looking-at "[^.]"))
-                (max (- ind prolog-indent-width) 0)) ;Less indentation
-               (t 0))                  ;No indentation
-         )))
-     )))
+                             beginning-of-line)))
 
 (defun end-of-prolog-clause ()
   "Go to end of clause in this line."
@@ -240,10 +229,16 @@ rigidly along with this one (not yet)."
 (defvar inferior-prolog-mode-syntax-table prolog-mode-syntax-table)
 (defvar inferior-prolog-mode-abbrev-table prolog-mode-abbrev-table)
 
+(defvar inferior-prolog-error-regexp-alist
+  ;; GNU Prolog used to not follow the GNU standard format.
+  '(("^\\(.*?\\):\\([0-9]+\\) error: .*(char:\\([0-9]+\\)" 1 2 3)
+    gnu))
+
 (declare-function comint-mode "comint")
 (declare-function comint-send-string "comint" (process string))
 (declare-function comint-send-region "comint" (process start end))
 (declare-function comint-send-eof "comint" ())
+(defvar compilation-error-regexp-alist)
 
 (define-derived-mode inferior-prolog-mode comint-mode "Inferior Prolog"
   "Major mode for interacting with an inferior Prolog process.
@@ -270,6 +265,9 @@ Return not at end copies rest of line to end and sends it.
 \\[comint-interrupt-subjob] interrupts the shell or its current subjob if any.
 \\[comint-stop-subjob] stops. \\[comint-quit-subjob] sends quit signal."
   (setq comint-prompt-regexp "^| [ ?][- ] *")
+  (set (make-local-variable 'compilation-error-regexp-alist)
+       inferior-prolog-error-regexp-alist)
+  (compilation-shell-minor-mode)
   (prolog-mode-variables))
 
 (defvar inferior-prolog-buffer nil)
@@ -359,7 +357,11 @@ With prefix argument \\[universal-prefix], prompt for the program to use."
              (save-excursion
                (goto-char (- pmark 3))
                (looking-at " \\? ")))
-        (comint-send-string proc (string last-command-char))
+        ;; This is GNU prolog waiting to know whether you want more answers
+        ;; or not (or abort, etc...).  The answer is a single char, not
+        ;; a line, so pass this char directly rather than wait for RET to
+        ;; send a whole line.
+        (comint-send-string proc (string last-command-event))
       (call-interactively 'self-insert-command))))
 
 (defun prolog-consult-region (compile beg end)
@@ -384,6 +386,9 @@ If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode."
   (prolog-consult-region compile beg end)
   (pop-to-buffer inferior-prolog-buffer))
 
+;; inferior-prolog-mode uses the autoloaded compilation-shell-minor-mode.
+(declare-function compilation-forget-errors "compile" ())
+
 (defun inferior-prolog-load-file ()
   "Pass the current buffer's file to the inferior prolog process."
   (interactive)
@@ -391,6 +396,7 @@ If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode."
   (let ((file buffer-file-name)
         (proc (inferior-prolog-process)))
     (with-current-buffer (process-buffer proc)
+      (compilation-forget-errors)
       (comint-send-string proc (concat "['" (file-relative-name file) "'].\n"))
       (pop-to-buffer (current-buffer)))))