Add 2011 to FSF/AIST copyright years.
[bpt/emacs.git] / lisp / progmodes / prolog.el
CommitLineData
6594deb0
ER
1;;; prolog.el --- major mode for editing and running Prolog under Emacs
2
1ba983e8 3;; Copyright (C) 1986, 1987, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
5df4f04c 4;; 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
9750e079 5
0acdb863 6;; Author: Masanobu UMEDA <umerin@mse.kyutech.ac.jp>
d7b4d18f 7;; Keywords: languages
e5167999 8
d8025917 9;; This file is part of GNU Emacs.
10
b1fc2b50 11;; GNU Emacs is free software: you can redistribute it and/or modify
d8025917 12;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
d8025917 15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
b1fc2b50 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
d8025917 23
edbd2f74
ER
24;;; Commentary:
25
26;; This package provides a major mode for editing Prolog. It knows
27;; about Prolog syntax and comments, and can send regions to an inferior
f614a1ae 28;; Prolog interpreter process. Font locking is tuned towards GNU Prolog.
edbd2f74 29
e5167999
ER
30;;; Code:
31
3cd79f62
DN
32(defvar comint-prompt-regexp)
33(defvar comint-process-echoes)
4e186ad5 34
c5292bc8 35(defgroup prolog nil
73efac49 36 "Major mode for editing and running Prolog under Emacs."
8ec3bce0 37 :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
c5292bc8
RS
38 :group 'languages)
39
f614a1ae 40
6cfd4f3a 41(defcustom prolog-program-name
d364dee6 42 (let ((names '("prolog" "gprolog" "swipl")))
6cfd4f3a
SM
43 (while (and names
44 (not (executable-find (car names))))
45 (setq names (cdr names)))
46 (or (car names) "prolog"))
d364dee6 47 "Program name for invoking an inferior Prolog with `run-prolog'."
c5292bc8
RS
48 :type 'string
49 :group 'prolog)
d8025917 50
c5292bc8 51(defcustom prolog-consult-string "reconsult(user).\n"
d364dee6 52 "(Re)Consult mode (for C-Prolog and Quintus Prolog). "
c5292bc8
RS
53 :type 'string
54 :group 'prolog)
d8025917 55
c5292bc8 56(defcustom prolog-compile-string "compile(user).\n"
d364dee6 57 "Compile mode (for Quintus Prolog)."
c5292bc8
RS
58 :type 'string
59 :group 'prolog)
d8025917 60
c5292bc8 61(defcustom prolog-eof-string "end_of_file.\n"
d364dee6 62 "String that represents end of file for Prolog.
c6c5714e 63When nil, send actual operating system end of file."
c5292bc8
RS
64 :type 'string
65 :group 'prolog)
d8025917 66
c5292bc8
RS
67(defcustom prolog-indent-width 4
68 "Level of indentation in Prolog buffers."
69 :type 'integer
70 :group 'prolog)
d8025917 71
f614a1ae
TTN
72(defvar prolog-font-lock-keywords
73 '(("\\(#[<=]=>\\|:-\\)\\|\\(#=\\)\\|\\(#[#<>\\/][=\\/]*\\|!\\)"
74 0 font-lock-keyword-face)
75 ("\\<\\(is\\|write\\|nl\\|read_\\sw+\\)\\>"
76 1 font-lock-keyword-face)
77 ("^\\(\\sw+\\)\\s-*\\((\\(.+\\))\\)*"
78 (1 font-lock-function-name-face)
79 (3 font-lock-variable-name-face)))
80 "Font-lock keywords for Prolog mode.")
81
6cfd4f3a 82(defvar prolog-mode-syntax-table
d8025917 83 (let ((table (make-syntax-table)))
84 (modify-syntax-entry ?_ "w" table)
85 (modify-syntax-entry ?\\ "\\" table)
f614a1ae
TTN
86 (modify-syntax-entry ?/ ". 14" table)
87 (modify-syntax-entry ?* ". 23" table)
d8025917 88 (modify-syntax-entry ?+ "." table)
89 (modify-syntax-entry ?- "." table)
90 (modify-syntax-entry ?= "." table)
91 (modify-syntax-entry ?% "<" table)
673f4fc6 92 (modify-syntax-entry ?\n ">" table)
d8025917 93 (modify-syntax-entry ?< "." table)
94 (modify-syntax-entry ?> "." table)
95 (modify-syntax-entry ?\' "\"" table)
6cfd4f3a 96 table))
d8025917 97
6cfd4f3a 98(defvar prolog-mode-abbrev-table nil)
d8025917 99(define-abbrev-table 'prolog-mode-abbrev-table ())
100
101(defun prolog-mode-variables ()
d8025917 102 (make-local-variable 'paragraph-separate)
6cfd4f3a 103 (setq paragraph-separate (concat "%%\\|$\\|" page-delimiter)) ;'%%..'
d8025917 104 (make-local-variable 'paragraph-ignore-fill-prefix)
105 (setq paragraph-ignore-fill-prefix t)
2c239c80 106 (make-local-variable 'imenu-generic-expression)
d8059b03 107 (setq imenu-generic-expression '((nil "^\\sw+" 0)))
d8025917 108 (make-local-variable 'indent-line-function)
109 (setq indent-line-function 'prolog-indent-line)
110 (make-local-variable 'comment-start)
111 (setq comment-start "%")
112 (make-local-variable 'comment-start-skip)
6cfd4f3a
SM
113 (setq comment-start-skip "\\(?:%+\\|/\\*+\\)[ \t]*")
114 (make-local-variable 'comment-end-skip)
115 (setq comment-end-skip "[ \t]*\\(\n\\|\\*+/\\)")
d8025917 116 (make-local-variable 'comment-column)
6cfd4f3a 117 (setq comment-column 48))
d8025917 118
6cfd4f3a
SM
119(defvar prolog-mode-map
120 (let ((map (make-sparse-keymap)))
121 (define-key map "\e\C-x" 'prolog-consult-region)
d364dee6
SM
122 (define-key map "\C-c\C-l" 'inferior-prolog-load-file)
123 (define-key map "\C-c\C-z" 'switch-to-prolog)
6cfd4f3a 124 map))
d364dee6
SM
125
126(easy-menu-define prolog-mode-menu prolog-mode-map "Menu for Prolog mode."
127 ;; Mostly copied from scheme-mode's menu.
128 ;; Not tremendously useful, but it's a start.
129 '("Prolog"
130 ["Indent line" indent-according-to-mode t]
131 ["Indent region" indent-region t]
132 ["Comment region" comment-region t]
133 ["Uncomment region" uncomment-region t]
134 "--"
135 ["Run interactive Prolog session" run-prolog t]
136 ))
d8025917 137
f9f9507e 138;;;###autoload
d8025917 139(defun prolog-mode ()
140 "Major mode for editing Prolog code for Prologs.
141Blank lines and `%%...' separate paragraphs. `%'s start comments.
142Commands:
143\\{prolog-mode-map}
573f9b32 144Entry to this mode calls the value of `prolog-mode-hook'
d8025917 145if that value is non-nil."
146 (interactive)
147 (kill-all-local-variables)
148 (use-local-map prolog-mode-map)
6cfd4f3a 149 (set-syntax-table prolog-mode-syntax-table)
d8025917 150 (setq major-mode 'prolog-mode)
151 (setq mode-name "Prolog")
152 (prolog-mode-variables)
d364dee6 153 (set (make-local-variable 'comment-add) 1)
f614a1ae
TTN
154 ;; font lock
155 (setq font-lock-defaults '(prolog-font-lock-keywords
156 nil nil nil
157 beginning-of-line))
6cfd4f3a 158 (run-mode-hooks 'prolog-mode-hook))
d8025917 159
d364dee6 160(defun prolog-indent-line ()
d8025917 161 "Indent current line as Prolog code.
162With argument, indent any additional lines of the same clause
163rigidly along with this one (not yet)."
164 (interactive "p")
165 (let ((indent (prolog-indent-level))
d364dee6 166 (pos (- (point-max) (point))))
d8025917 167 (beginning-of-line)
d364dee6 168 (indent-line-to indent)
d8025917 169 (if (> (- (point-max) pos) (point))
d364dee6 170 (goto-char (- (point-max) pos)))))
d8025917 171
172(defun prolog-indent-level ()
c6c5714e 173 "Compute Prolog indentation level."
d8025917 174 (save-excursion
175 (beginning-of-line)
176 (skip-chars-forward " \t")
177 (cond
178 ((looking-at "%%%") 0) ;Large comment starts
179 ((looking-at "%[^%]") comment-column) ;Small comment starts
180 ((bobp) 0) ;Beginning of buffer
181 (t
182 (let ((empty t) ind more less)
183 (if (looking-at ")")
184 (setq less t) ;Find close
185 (setq less nil))
186 ;; See previous indentation
187 (while empty
188 (forward-line -1)
189 (beginning-of-line)
190 (if (bobp)
191 (setq empty nil)
192 (skip-chars-forward " \t")
193 (if (not (or (looking-at "%[^%]") (looking-at "\n")))
194 (setq empty nil))))
195 (if (bobp)
196 (setq ind 0) ;Beginning of buffer
197 (setq ind (current-column))) ;Beginning of clause
198 ;; See its beginning
199 (if (looking-at "%%[^%]")
200 ind
201 ;; Real prolog code
202 (if (looking-at "(")
203 (setq more t) ;Find open
204 (setq more nil))
205 ;; See its tail
206 (end-of-prolog-clause)
207 (or (bobp) (forward-char -1))
208 (cond ((looking-at "[,(;>]")
209 (if (and more (looking-at "[^,]"))
210 (+ ind prolog-indent-width) ;More indentation
211 (max tab-width ind))) ;Same indentation
212 ((looking-at "-") tab-width) ;TAB
213 ((or less (looking-at "[^.]"))
214 (max (- ind prolog-indent-width) 0)) ;Less indentation
215 (t 0)) ;No indentation
216 )))
217 )))
218
219(defun end-of-prolog-clause ()
220 "Go to end of clause in this line."
221 (beginning-of-line 1)
222 (let* ((eolpos (save-excursion (end-of-line) (point))))
223 (if (re-search-forward comment-start-skip eolpos 'move)
224 (goto-char (match-beginning 0)))
225 (skip-chars-backward " \t")))
d8025917 226\f
227;;;
228;;; Inferior prolog mode
229;;;
6cfd4f3a
SM
230(defvar inferior-prolog-mode-map
231 (let ((map (make-sparse-keymap)))
232 ;; This map will inherit from `comint-mode-map' when entering
233 ;; inferior-prolog-mode.
23f2d048
SM
234 (define-key map [remap self-insert-command]
235 'inferior-prolog-self-insert-command)
6cfd4f3a
SM
236 map))
237
238(defvar inferior-prolog-mode-syntax-table prolog-mode-syntax-table)
239(defvar inferior-prolog-mode-abbrev-table prolog-mode-abbrev-table)
d8025917 240
28930e39
SM
241(defvar inferior-prolog-error-regexp-alist
242 ;; GNU Prolog used to not follow the GNU standard format.
243 '(("^\\(.*?\\):\\([0-9]+\\) error: .*(char:\\([0-9]+\\)" 1 2 3)
244 gnu))
245
5cec3056
GM
246(declare-function comint-mode "comint")
247(declare-function comint-send-string "comint" (process string))
248(declare-function comint-send-region "comint" (process start end))
249(declare-function comint-send-eof "comint" ())
a569b480 250(defvar compilation-error-regexp-alist)
153ef845 251
6cfd4f3a 252(define-derived-mode inferior-prolog-mode comint-mode "Inferior Prolog"
d8025917 253 "Major mode for interacting with an inferior Prolog process.
254
255The following commands are available:
256\\{inferior-prolog-mode-map}
257
573f9b32
RS
258Entry to this mode calls the value of `prolog-mode-hook' with no arguments,
259if that value is non-nil. Likewise with the value of `comint-mode-hook'.
260`prolog-mode-hook' is called after `comint-mode-hook'.
d8025917 261
c647adda
JB
262You can send text to the inferior Prolog from other buffers using the commands
263`process-send-region', `process-send-string' and \\[prolog-consult-region].
d8025917 264
265Commands:
266Tab indents for Prolog; with argument, shifts rest
267 of expression rigidly with the current line.
573f9b32
RS
268Paragraphs are separated only by blank lines and '%%'.
269'%'s start comments.
d8025917 270
271Return at end of buffer sends line as input.
272Return not at end copies rest of line to end and sends it.
273\\[comint-kill-input] and \\[backward-kill-word] are kill commands, imitating normal Unix input editing.
274\\[comint-interrupt-subjob] interrupts the shell or its current subjob if any.
275\\[comint-stop-subjob] stops. \\[comint-quit-subjob] sends quit signal."
6cfd4f3a 276 (setq comint-prompt-regexp "^| [ ?][- ] *")
28930e39
SM
277 (set (make-local-variable 'compilation-error-regexp-alist)
278 inferior-prolog-error-regexp-alist)
279 (compilation-shell-minor-mode)
6cfd4f3a 280 (prolog-mode-variables))
d8025917 281
d364dee6
SM
282(defvar inferior-prolog-buffer nil)
283
7caf6803
DN
284(defvar inferior-prolog-flavor 'unknown
285 "Either a symbol or a buffer position offset by one.
286If a buffer position, the flavor has not been determined yet and
287it is expected that the process's output has been or will
288be inserted at that position plus one.")
289
d364dee6
SM
290(defun inferior-prolog-run (&optional name)
291 (with-current-buffer (make-comint "prolog" (or name prolog-program-name))
292 (inferior-prolog-mode)
293 (setq-default inferior-prolog-buffer (current-buffer))
294 (make-local-variable 'inferior-prolog-buffer)
295 (when (and name (not (equal name prolog-program-name)))
296 (set (make-local-variable 'prolog-program-name) name))
297 (set (make-local-variable 'inferior-prolog-flavor)
298 ;; Force re-detection.
299 (let* ((proc (get-buffer-process (current-buffer)))
300 (pmark (and proc (marker-position (process-mark proc)))))
301 (cond
302 ((null pmark) (1- (point-min)))
303 ;; The use of insert-before-markers in comint.el together with
304 ;; the potential use of comint-truncate-buffer in the output
305 ;; filter, means that it's difficult to reliably keep track of
306 ;; the buffer position where the process's output started.
307 ;; If possible we use a marker at "start - 1", so that
308 ;; insert-before-marker at `start' won't shift it. And if not,
309 ;; we fall back on using a plain integer.
310 ((> pmark (point-min)) (copy-marker (1- pmark)))
311 (t (1- pmark)))))
312 (add-hook 'comint-output-filter-functions
313 'inferior-prolog-guess-flavor nil t)))
314
315(defun inferior-prolog-process (&optional dontstart)
316 (or (and (buffer-live-p inferior-prolog-buffer)
317 (get-buffer-process inferior-prolog-buffer))
318 (unless dontstart
319 (inferior-prolog-run)
320 ;; Try again.
321 (inferior-prolog-process))))
322
23f2d048
SM
323(defun inferior-prolog-guess-flavor (&optional ignored)
324 (save-excursion
325 (goto-char (1+ inferior-prolog-flavor))
326 (setq inferior-prolog-flavor
327 (cond
328 ((looking-at "GNU Prolog") 'gnu)
329 ((looking-at "Welcome to SWI-Prolog") 'swi)
330 ((looking-at ".*\n") 'unknown) ;There's at least one line.
331 (t inferior-prolog-flavor))))
332 (when (symbolp inferior-prolog-flavor)
333 (remove-hook 'comint-output-filter-functions
334 'inferior-prolog-guess-flavor t)
335 (if (eq inferior-prolog-flavor 'gnu)
336 (set (make-local-variable 'comint-process-echoes) t))))
337
f9f9507e 338;;;###autoload
d364dee6
SM
339(defalias 'run-prolog 'switch-to-prolog)
340;;;###autoload
341(defun switch-to-prolog (&optional name)
342 "Run an inferior Prolog process, input and output via buffer *prolog*.
343With prefix argument \\[universal-prefix], prompt for the program to use."
344 (interactive
345 (list (when current-prefix-arg
346 (let ((proc (inferior-prolog-process 'dontstart)))
347 (if proc
348 (if (yes-or-no-p "Kill current process before starting new one? ")
349 (kill-process proc)
350 (error "Abort")))
351 (read-string "Run Prolog: " prolog-program-name)))))
352 (unless (inferior-prolog-process 'dontstart)
353 (inferior-prolog-run name))
354 (pop-to-buffer inferior-prolog-buffer))
d8025917 355
23f2d048
SM
356(defun inferior-prolog-self-insert-command ()
357 "Insert the char in the buffer or pass it directly to the process."
358 (interactive)
359 (let* ((proc (get-buffer-process (current-buffer)))
360 (pmark (and proc (marker-position (process-mark proc)))))
361 (if (and (eq inferior-prolog-flavor 'gnu)
362 pmark
363 (null current-prefix-arg)
364 (eobp)
365 (eq (point) pmark)
366 (save-excursion
367 (goto-char (- pmark 3))
368 (looking-at " \\? ")))
28930e39
SM
369 ;; This is GNU prolog waiting to know whether you want more answers
370 ;; or not (or abort, etc...). The answer is a single char, not
371 ;; a line, so pass this char directly rather than wait for RET to
372 ;; send a whole line.
1ba983e8 373 (comint-send-string proc (string last-command-event))
23f2d048
SM
374 (call-interactively 'self-insert-command))))
375
d8025917 376(defun prolog-consult-region (compile beg end)
573f9b32
RS
377 "Send the region to the Prolog process made by \"M-x run-prolog\".
378If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode."
d8025917 379 (interactive "P\nr")
d364dee6
SM
380 (let ((proc (inferior-prolog-process)))
381 (comint-send-string proc
382 (if compile prolog-compile-string
383 prolog-consult-string))
384 (comint-send-region proc beg end)
385 (comint-send-string proc "\n") ;May be unnecessary
d8025917 386 (if prolog-eof-string
d364dee6
SM
387 (comint-send-string proc prolog-eof-string)
388 (with-current-buffer (process-buffer proc)
389 (comint-send-eof))))) ;Send eof to prolog process.
d8025917 390
391(defun prolog-consult-region-and-go (compile beg end)
392 "Send the region to the inferior Prolog, and switch to *prolog* buffer.
573f9b32 393If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode."
d8025917 394 (interactive "P\nr")
395 (prolog-consult-region compile beg end)
23f2d048 396 (pop-to-buffer inferior-prolog-buffer))
6594deb0 397
a569b480
GM
398;; inferior-prolog-mode uses the autoloaded compilation-shell-minor-mode.
399(declare-function compilation-forget-errors "compile" ())
400
d364dee6
SM
401(defun inferior-prolog-load-file ()
402 "Pass the current buffer's file to the inferior prolog process."
403 (interactive)
404 (save-buffer)
405 (let ((file buffer-file-name)
406 (proc (inferior-prolog-process)))
407 (with-current-buffer (process-buffer proc)
28930e39 408 (compilation-forget-errors)
d364dee6
SM
409 (comint-send-string proc (concat "['" (file-relative-name file) "'].\n"))
410 (pop-to-buffer (current-buffer)))))
411
896546cd
RS
412(provide 'prolog)
413
d364dee6 414;; arch-tag: f3ec6748-1272-4ab6-8826-c50cb1607636
6594deb0 415;;; prolog.el ends here