Switch to recommended form of GPLv3 permissions notice.
[bpt/emacs.git] / lisp / progmodes / prolog.el
CommitLineData
6594deb0
ER
1;;; prolog.el --- major mode for editing and running Prolog under Emacs
2
4e643dd2 3;; Copyright (C) 1986, 1987, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
034babe1 4;; 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
5cec3056
GM
241(declare-function comint-mode "comint")
242(declare-function comint-send-string "comint" (process string))
243(declare-function comint-send-region "comint" (process start end))
244(declare-function comint-send-eof "comint" ())
153ef845 245
6cfd4f3a 246(define-derived-mode inferior-prolog-mode comint-mode "Inferior Prolog"
d8025917 247 "Major mode for interacting with an inferior Prolog process.
248
249The following commands are available:
250\\{inferior-prolog-mode-map}
251
573f9b32
RS
252Entry to this mode calls the value of `prolog-mode-hook' with no arguments,
253if that value is non-nil. Likewise with the value of `comint-mode-hook'.
254`prolog-mode-hook' is called after `comint-mode-hook'.
d8025917 255
c647adda
JB
256You can send text to the inferior Prolog from other buffers using the commands
257`process-send-region', `process-send-string' and \\[prolog-consult-region].
d8025917 258
259Commands:
260Tab indents for Prolog; with argument, shifts rest
261 of expression rigidly with the current line.
573f9b32
RS
262Paragraphs are separated only by blank lines and '%%'.
263'%'s start comments.
d8025917 264
265Return at end of buffer sends line as input.
266Return not at end copies rest of line to end and sends it.
267\\[comint-kill-input] and \\[backward-kill-word] are kill commands, imitating normal Unix input editing.
268\\[comint-interrupt-subjob] interrupts the shell or its current subjob if any.
269\\[comint-stop-subjob] stops. \\[comint-quit-subjob] sends quit signal."
6cfd4f3a
SM
270 (setq comint-prompt-regexp "^| [ ?][- ] *")
271 (prolog-mode-variables))
d8025917 272
d364dee6
SM
273(defvar inferior-prolog-buffer nil)
274
7caf6803
DN
275(defvar inferior-prolog-flavor 'unknown
276 "Either a symbol or a buffer position offset by one.
277If a buffer position, the flavor has not been determined yet and
278it is expected that the process's output has been or will
279be inserted at that position plus one.")
280
d364dee6
SM
281(defun inferior-prolog-run (&optional name)
282 (with-current-buffer (make-comint "prolog" (or name prolog-program-name))
283 (inferior-prolog-mode)
284 (setq-default inferior-prolog-buffer (current-buffer))
285 (make-local-variable 'inferior-prolog-buffer)
286 (when (and name (not (equal name prolog-program-name)))
287 (set (make-local-variable 'prolog-program-name) name))
288 (set (make-local-variable 'inferior-prolog-flavor)
289 ;; Force re-detection.
290 (let* ((proc (get-buffer-process (current-buffer)))
291 (pmark (and proc (marker-position (process-mark proc)))))
292 (cond
293 ((null pmark) (1- (point-min)))
294 ;; The use of insert-before-markers in comint.el together with
295 ;; the potential use of comint-truncate-buffer in the output
296 ;; filter, means that it's difficult to reliably keep track of
297 ;; the buffer position where the process's output started.
298 ;; If possible we use a marker at "start - 1", so that
299 ;; insert-before-marker at `start' won't shift it. And if not,
300 ;; we fall back on using a plain integer.
301 ((> pmark (point-min)) (copy-marker (1- pmark)))
302 (t (1- pmark)))))
303 (add-hook 'comint-output-filter-functions
304 'inferior-prolog-guess-flavor nil t)))
305
306(defun inferior-prolog-process (&optional dontstart)
307 (or (and (buffer-live-p inferior-prolog-buffer)
308 (get-buffer-process inferior-prolog-buffer))
309 (unless dontstart
310 (inferior-prolog-run)
311 ;; Try again.
312 (inferior-prolog-process))))
313
23f2d048
SM
314(defun inferior-prolog-guess-flavor (&optional ignored)
315 (save-excursion
316 (goto-char (1+ inferior-prolog-flavor))
317 (setq inferior-prolog-flavor
318 (cond
319 ((looking-at "GNU Prolog") 'gnu)
320 ((looking-at "Welcome to SWI-Prolog") 'swi)
321 ((looking-at ".*\n") 'unknown) ;There's at least one line.
322 (t inferior-prolog-flavor))))
323 (when (symbolp inferior-prolog-flavor)
324 (remove-hook 'comint-output-filter-functions
325 'inferior-prolog-guess-flavor t)
326 (if (eq inferior-prolog-flavor 'gnu)
327 (set (make-local-variable 'comint-process-echoes) t))))
328
f9f9507e 329;;;###autoload
d364dee6
SM
330(defalias 'run-prolog 'switch-to-prolog)
331;;;###autoload
332(defun switch-to-prolog (&optional name)
333 "Run an inferior Prolog process, input and output via buffer *prolog*.
334With prefix argument \\[universal-prefix], prompt for the program to use."
335 (interactive
336 (list (when current-prefix-arg
337 (let ((proc (inferior-prolog-process 'dontstart)))
338 (if proc
339 (if (yes-or-no-p "Kill current process before starting new one? ")
340 (kill-process proc)
341 (error "Abort")))
342 (read-string "Run Prolog: " prolog-program-name)))))
343 (unless (inferior-prolog-process 'dontstart)
344 (inferior-prolog-run name))
345 (pop-to-buffer inferior-prolog-buffer))
d8025917 346
23f2d048
SM
347(defun inferior-prolog-self-insert-command ()
348 "Insert the char in the buffer or pass it directly to the process."
349 (interactive)
350 (let* ((proc (get-buffer-process (current-buffer)))
351 (pmark (and proc (marker-position (process-mark proc)))))
352 (if (and (eq inferior-prolog-flavor 'gnu)
353 pmark
354 (null current-prefix-arg)
355 (eobp)
356 (eq (point) pmark)
357 (save-excursion
358 (goto-char (- pmark 3))
359 (looking-at " \\? ")))
360 (comint-send-string proc (string last-command-char))
361 (call-interactively 'self-insert-command))))
362
d8025917 363(defun prolog-consult-region (compile beg end)
573f9b32
RS
364 "Send the region to the Prolog process made by \"M-x run-prolog\".
365If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode."
d8025917 366 (interactive "P\nr")
d364dee6
SM
367 (let ((proc (inferior-prolog-process)))
368 (comint-send-string proc
369 (if compile prolog-compile-string
370 prolog-consult-string))
371 (comint-send-region proc beg end)
372 (comint-send-string proc "\n") ;May be unnecessary
d8025917 373 (if prolog-eof-string
d364dee6
SM
374 (comint-send-string proc prolog-eof-string)
375 (with-current-buffer (process-buffer proc)
376 (comint-send-eof))))) ;Send eof to prolog process.
d8025917 377
378(defun prolog-consult-region-and-go (compile beg end)
379 "Send the region to the inferior Prolog, and switch to *prolog* buffer.
573f9b32 380If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode."
d8025917 381 (interactive "P\nr")
382 (prolog-consult-region compile beg end)
23f2d048 383 (pop-to-buffer inferior-prolog-buffer))
6594deb0 384
d364dee6
SM
385(defun inferior-prolog-load-file ()
386 "Pass the current buffer's file to the inferior prolog process."
387 (interactive)
388 (save-buffer)
389 (let ((file buffer-file-name)
390 (proc (inferior-prolog-process)))
391 (with-current-buffer (process-buffer proc)
392 (comint-send-string proc (concat "['" (file-relative-name file) "'].\n"))
393 (pop-to-buffer (current-buffer)))))
394
896546cd
RS
395(provide 'prolog)
396
d364dee6 397;; arch-tag: f3ec6748-1272-4ab6-8826-c50cb1607636
6594deb0 398;;; prolog.el ends here