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