]> code.delx.au - gnu-emacs/blob - lisp/progmodes/prolog.el
5c96253f6beb770e0eb48d9eb5bb4204c5feff40
[gnu-emacs] / 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,
4 ;; 2008, 2009, 2010, 2011 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 of the License, or
14 ;; (at your option) 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. If not, see <http://www.gnu.org/licenses/>.
23
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
28 ;; Prolog interpreter process. Font locking is tuned towards GNU Prolog.
29
30 ;;; Code:
31
32 (defvar comint-prompt-regexp)
33 (defvar comint-process-echoes)
34
35 (defgroup prolog nil
36 "Major mode for editing and running Prolog under Emacs."
37 :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
38 :group 'languages)
39
40
41 (defcustom prolog-program-name
42 (let ((names '("prolog" "gprolog" "swipl")))
43 (while (and names
44 (not (executable-find (car names))))
45 (setq names (cdr names)))
46 (or (car names) "prolog"))
47 "Program name for invoking an inferior Prolog with `run-prolog'."
48 :type 'string
49 :group 'prolog)
50
51 (defcustom prolog-consult-string "reconsult(user).\n"
52 "(Re)Consult mode (for C-Prolog and Quintus Prolog). "
53 :type 'string
54 :group 'prolog)
55
56 (defcustom prolog-compile-string "compile(user).\n"
57 "Compile mode (for Quintus Prolog)."
58 :type 'string
59 :group 'prolog)
60
61 (defcustom prolog-eof-string "end_of_file.\n"
62 "String that represents end of file for Prolog.
63 When nil, send actual operating system end of file."
64 :type 'string
65 :group 'prolog)
66
67 (defcustom prolog-indent-width 4
68 "Level of indentation in Prolog buffers."
69 :type 'integer
70 :group 'prolog)
71
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
82 (defvar prolog-mode-syntax-table
83 (let ((table (make-syntax-table)))
84 (modify-syntax-entry ?_ "w" table)
85 (modify-syntax-entry ?\\ "\\" table)
86 (modify-syntax-entry ?/ ". 14" table)
87 (modify-syntax-entry ?* ". 23" table)
88 (modify-syntax-entry ?+ "." table)
89 (modify-syntax-entry ?- "." table)
90 (modify-syntax-entry ?= "." table)
91 (modify-syntax-entry ?% "<" table)
92 (modify-syntax-entry ?\n ">" table)
93 (modify-syntax-entry ?< "." table)
94 (modify-syntax-entry ?> "." table)
95 (modify-syntax-entry ?\' "\"" table)
96 table))
97
98 (defvar prolog-mode-abbrev-table nil)
99 (define-abbrev-table 'prolog-mode-abbrev-table ())
100
101 (defun prolog-mode-variables ()
102 (make-local-variable 'paragraph-separate)
103 (setq paragraph-separate (concat "%%\\|$\\|" page-delimiter)) ;'%%..'
104 (make-local-variable 'paragraph-ignore-fill-prefix)
105 (setq paragraph-ignore-fill-prefix t)
106 (make-local-variable 'imenu-generic-expression)
107 (setq imenu-generic-expression '((nil "^\\sw+" 0)))
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)
113 (setq comment-start-skip "\\(?:%+\\|/\\*+\\)[ \t]*")
114 (make-local-variable 'comment-end-skip)
115 (setq comment-end-skip "[ \t]*\\(\n\\|\\*+/\\)")
116 (make-local-variable 'comment-column)
117 (setq comment-column 48))
118
119 (defvar prolog-mode-map
120 (let ((map (make-sparse-keymap)))
121 (define-key map "\e\C-x" 'prolog-consult-region)
122 (define-key map "\C-c\C-l" 'inferior-prolog-load-file)
123 (define-key map "\C-c\C-z" 'switch-to-prolog)
124 map))
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 ))
137
138 ;;;###autoload
139 (defun prolog-mode ()
140 "Major mode for editing Prolog code for Prologs.
141 Blank lines and `%%...' separate paragraphs. `%'s start comments.
142 Commands:
143 \\{prolog-mode-map}
144 Entry to this mode calls the value of `prolog-mode-hook'
145 if that value is non-nil."
146 (interactive)
147 (kill-all-local-variables)
148 (use-local-map prolog-mode-map)
149 (set-syntax-table prolog-mode-syntax-table)
150 (setq major-mode 'prolog-mode)
151 (setq mode-name "Prolog")
152 (prolog-mode-variables)
153 (set (make-local-variable 'comment-add) 1)
154 ;; font lock
155 (setq font-lock-defaults '(prolog-font-lock-keywords
156 nil nil nil
157 beginning-of-line))
158 (run-mode-hooks 'prolog-mode-hook))
159
160 (defun prolog-indent-line ()
161 "Indent current line as Prolog code.
162 With argument, indent any additional lines of the same clause
163 rigidly along with this one (not yet)."
164 (interactive "p")
165 (let ((indent (prolog-indent-level))
166 (pos (- (point-max) (point))))
167 (beginning-of-line)
168 (indent-line-to indent)
169 (if (> (- (point-max) pos) (point))
170 (goto-char (- (point-max) pos)))))
171
172 (defun prolog-indent-level ()
173 "Compute Prolog indentation level."
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")))
226 \f
227 ;;;
228 ;;; Inferior prolog mode
229 ;;;
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.
234 (define-key map [remap self-insert-command]
235 'inferior-prolog-self-insert-command)
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)
240
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
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" ())
250 (defvar compilation-error-regexp-alist)
251
252 (define-derived-mode inferior-prolog-mode comint-mode "Inferior Prolog"
253 "Major mode for interacting with an inferior Prolog process.
254
255 The following commands are available:
256 \\{inferior-prolog-mode-map}
257
258 Entry to this mode calls the value of `prolog-mode-hook' with no arguments,
259 if that value is non-nil. Likewise with the value of `comint-mode-hook'.
260 `prolog-mode-hook' is called after `comint-mode-hook'.
261
262 You can send text to the inferior Prolog from other buffers using the commands
263 `process-send-region', `process-send-string' and \\[prolog-consult-region].
264
265 Commands:
266 Tab indents for Prolog; with argument, shifts rest
267 of expression rigidly with the current line.
268 Paragraphs are separated only by blank lines and '%%'.
269 '%'s start comments.
270
271 Return at end of buffer sends line as input.
272 Return 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."
276 (setq comint-prompt-regexp "^| [ ?][- ] *")
277 (set (make-local-variable 'compilation-error-regexp-alist)
278 inferior-prolog-error-regexp-alist)
279 (compilation-shell-minor-mode)
280 (prolog-mode-variables))
281
282 (defvar inferior-prolog-buffer nil)
283
284 (defvar inferior-prolog-flavor 'unknown
285 "Either a symbol or a buffer position offset by one.
286 If a buffer position, the flavor has not been determined yet and
287 it is expected that the process's output has been or will
288 be inserted at that position plus one.")
289
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
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
338 ;;;###autoload
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*.
343 With 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))
355
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 " \\? ")))
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.
373 (comint-send-string proc (string last-command-event))
374 (call-interactively 'self-insert-command))))
375
376 (defun prolog-consult-region (compile beg end)
377 "Send the region to the Prolog process made by \"M-x run-prolog\".
378 If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode."
379 (interactive "P\nr")
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
386 (if prolog-eof-string
387 (comint-send-string proc prolog-eof-string)
388 (with-current-buffer (process-buffer proc)
389 (comint-send-eof))))) ;Send eof to prolog process.
390
391 (defun prolog-consult-region-and-go (compile beg end)
392 "Send the region to the inferior Prolog, and switch to *prolog* buffer.
393 If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode."
394 (interactive "P\nr")
395 (prolog-consult-region compile beg end)
396 (pop-to-buffer inferior-prolog-buffer))
397
398 ;; inferior-prolog-mode uses the autoloaded compilation-shell-minor-mode.
399 (declare-function compilation-forget-errors "compile" ())
400
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)
408 (compilation-forget-errors)
409 (comint-send-string proc (concat "['" (file-relative-name file) "'].\n"))
410 (pop-to-buffer (current-buffer)))))
411
412 (provide 'prolog)
413
414 ;; arch-tag: f3ec6748-1272-4ab6-8826-c50cb1607636
415 ;;; prolog.el ends here