prolog.el 14.2 KB
Newer Older
Eric S. Raymond's avatar
Eric S. Raymond committed
1 2
;;; prolog.el --- major mode for editing and running Prolog under Emacs

Nick Roberts's avatar
Nick Roberts committed
3
;; Copyright (C) 1986, 1987, 2001, 2002, 2003, 2004, 2005, 2006
4
;; Free Software Foundation, Inc.
Eric S. Raymond's avatar
Eric S. Raymond committed
5

Richard M. Stallman's avatar
Richard M. Stallman committed
6
;; Author: Masanobu UMEDA <umerin@mse.kyutech.ac.jp>
Eric S. Raymond's avatar
Eric S. Raymond committed
7
;; Keywords: languages
Eric S. Raymond's avatar
Eric S. Raymond committed
8

root's avatar
root committed
9 10 11 12
;; This file is part of GNU Emacs.

;; 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
Eric S. Raymond's avatar
Eric S. Raymond committed
13
;; the Free Software Foundation; either version 2, or (at your option)
root's avatar
root committed
14 15 16 17 18 19 20 21
;; 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
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
Erik Naggum's avatar
Erik Naggum committed
22
;; along with GNU Emacs; see the file COPYING.  If not, write to the
Lute Kamstra's avatar
Lute Kamstra committed
23 24
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
root's avatar
root committed
25

26 27 28 29
;;; Commentary:

;; This package provides a major mode for editing Prolog.  It knows
;; about Prolog syntax and comments, and can send regions to an inferior
30
;; Prolog interpreter process.  Font locking is tuned towards GNU Prolog.
31

Eric S. Raymond's avatar
Eric S. Raymond committed
32 33
;;; Code:

34 35 36
(defvar comint-prompt-regexp)


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 41
  :group 'languages)

42

43
(defcustom prolog-program-name
Stefan Monnier's avatar
Stefan Monnier committed
44
  (let ((names '("prolog" "gprolog" "swipl")))
45 46 47 48
    (while (and names
		(not (executable-find (car names))))
      (setq names (cdr names)))
    (or (car names) "prolog"))
Stefan Monnier's avatar
Stefan Monnier committed
49
  "Program name for invoking an inferior Prolog with `run-prolog'."
50 51
  :type 'string
  :group 'prolog)
root's avatar
root committed
52

53
(defcustom prolog-consult-string "reconsult(user).\n"
Stefan Monnier's avatar
Stefan Monnier committed
54
  "(Re)Consult mode (for C-Prolog and Quintus Prolog). "
55 56
  :type 'string
  :group 'prolog)
root's avatar
root committed
57

58
(defcustom prolog-compile-string "compile(user).\n"
Stefan Monnier's avatar
Stefan Monnier committed
59
  "Compile mode (for Quintus Prolog)."
60 61
  :type 'string
  :group 'prolog)
root's avatar
root committed
62

63
(defcustom prolog-eof-string "end_of_file.\n"
Stefan Monnier's avatar
Stefan Monnier committed
64
  "String that represents end of file for Prolog.
65
When nil, send actual operating system end of file."
66 67
  :type 'string
  :group 'prolog)
root's avatar
root committed
68

69 70 71 72
(defcustom prolog-indent-width 4
  "Level of indentation in Prolog buffers."
  :type 'integer
  :group 'prolog)
root's avatar
root committed
73

74 75 76 77 78 79 80 81 82 83
(defvar prolog-font-lock-keywords
  '(("\\(#[<=]=>\\|:-\\)\\|\\(#=\\)\\|\\(#[#<>\\/][=\\/]*\\|!\\)"
     0 font-lock-keyword-face)
    ("\\<\\(is\\|write\\|nl\\|read_\\sw+\\)\\>"
     1 font-lock-keyword-face)
    ("^\\(\\sw+\\)\\s-*\\((\\(.+\\))\\)*"
     (1 font-lock-function-name-face)
     (3 font-lock-variable-name-face)))
  "Font-lock keywords for Prolog mode.")

84
(defvar prolog-mode-syntax-table
root's avatar
root committed
85 86 87
  (let ((table (make-syntax-table)))
    (modify-syntax-entry ?_ "w" table)
    (modify-syntax-entry ?\\ "\\" table)
88 89
    (modify-syntax-entry ?/ ". 14" table)
    (modify-syntax-entry ?* ". 23" table)
root's avatar
root committed
90 91 92 93
    (modify-syntax-entry ?+ "." table)
    (modify-syntax-entry ?- "." table)
    (modify-syntax-entry ?= "." table)
    (modify-syntax-entry ?% "<" table)
94
    (modify-syntax-entry ?\n ">" table)
root's avatar
root committed
95 96 97
    (modify-syntax-entry ?< "." table)
    (modify-syntax-entry ?> "." table)
    (modify-syntax-entry ?\' "\"" table)
98
    table))
root's avatar
root committed
99

100
(defvar prolog-mode-abbrev-table nil)
root's avatar
root committed
101 102 103 104
(define-abbrev-table 'prolog-mode-abbrev-table ())

(defun prolog-mode-variables ()
  (make-local-variable 'paragraph-separate)
105
  (setq paragraph-separate (concat "%%\\|$\\|" page-delimiter)) ;'%%..'
root's avatar
root committed
106 107
  (make-local-variable 'paragraph-ignore-fill-prefix)
  (setq paragraph-ignore-fill-prefix t)
Karl Heuer's avatar
Karl Heuer committed
108
  (make-local-variable 'imenu-generic-expression)
109
  (setq imenu-generic-expression '((nil "^\\sw+" 0)))
root's avatar
root committed
110 111 112 113 114
  (make-local-variable 'indent-line-function)
  (setq indent-line-function 'prolog-indent-line)
  (make-local-variable 'comment-start)
  (setq comment-start "%")
  (make-local-variable 'comment-start-skip)
115 116 117
  (setq comment-start-skip "\\(?:%+\\|/\\*+\\)[ \t]*")
  (make-local-variable 'comment-end-skip)
  (setq comment-end-skip "[ \t]*\\(\n\\|\\*+/\\)")
root's avatar
root committed
118
  (make-local-variable 'comment-column)
119
  (setq comment-column 48))
root's avatar
root committed
120

121 122 123
(defvar prolog-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map "\e\C-x" 'prolog-consult-region)
Stefan Monnier's avatar
Stefan Monnier committed
124 125
    (define-key map "\C-c\C-l" 'inferior-prolog-load-file)
    (define-key map "\C-c\C-z" 'switch-to-prolog)
126
    map))
Stefan Monnier's avatar
Stefan Monnier committed
127 128 129 130 131 132 133 134 135 136 137 138
 
(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.
  '("Prolog"
    ["Indent line" indent-according-to-mode t]
    ["Indent region" indent-region t]
    ["Comment region" comment-region t]
    ["Uncomment region" uncomment-region t]
    "--"
    ["Run interactive Prolog session" run-prolog t]
    ))
root's avatar
root committed
139

Jim Blandy's avatar
Jim Blandy committed
140
;;;###autoload
root's avatar
root committed
141 142 143 144 145
(defun prolog-mode ()
  "Major mode for editing Prolog code for Prologs.
Blank lines and `%%...' separate paragraphs.  `%'s start comments.
Commands:
\\{prolog-mode-map}
146
Entry to this mode calls the value of `prolog-mode-hook'
root's avatar
root committed
147 148 149 150
if that value is non-nil."
  (interactive)
  (kill-all-local-variables)
  (use-local-map prolog-mode-map)
151
  (set-syntax-table prolog-mode-syntax-table)
root's avatar
root committed
152 153 154
  (setq major-mode 'prolog-mode)
  (setq mode-name "Prolog")
  (prolog-mode-variables)
Stefan Monnier's avatar
Stefan Monnier committed
155
  (set (make-local-variable 'comment-add) 1)
156 157 158 159
  ;; font lock
  (setq font-lock-defaults '(prolog-font-lock-keywords
                             nil nil nil
                             beginning-of-line))
160
  (run-mode-hooks 'prolog-mode-hook))
root's avatar
root committed
161

Stefan Monnier's avatar
Stefan Monnier committed
162
(defun prolog-indent-line ()
root's avatar
root committed
163 164 165 166 167
  "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))
Stefan Monnier's avatar
Stefan Monnier committed
168
	(pos (- (point-max) (point))))
root's avatar
root committed
169
    (beginning-of-line)
Stefan Monnier's avatar
Stefan Monnier committed
170
    (indent-line-to indent)
root's avatar
root committed
171
    (if (> (- (point-max) pos) (point))
Stefan Monnier's avatar
Stefan Monnier committed
172
	(goto-char (- (point-max) pos)))))
root's avatar
root committed
173 174

(defun prolog-indent-level ()
175
  "Compute Prolog indentation level."
root's avatar
root committed
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
  (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
	  )))
     )))

(defun end-of-prolog-clause ()
  "Go to end of clause in this line."
  (beginning-of-line 1)
  (let* ((eolpos (save-excursion (end-of-line) (point))))
    (if (re-search-forward comment-start-skip eolpos 'move)
	(goto-char (match-beginning 0)))
    (skip-chars-backward " \t")))

;;;
;;; Inferior prolog mode
;;;
232 233 234 235
(defvar inferior-prolog-mode-map
  (let ((map (make-sparse-keymap)))
    ;; This map will inherit from `comint-mode-map' when entering
    ;; inferior-prolog-mode.
236 237
    (define-key map [remap self-insert-command]
      'inferior-prolog-self-insert-command)
238 239 240 241
    map))

(defvar inferior-prolog-mode-syntax-table prolog-mode-syntax-table)
(defvar inferior-prolog-mode-abbrev-table prolog-mode-abbrev-table)
root's avatar
root committed
242

243
(define-derived-mode inferior-prolog-mode comint-mode "Inferior Prolog"
root's avatar
root committed
244 245 246 247 248
  "Major mode for interacting with an inferior Prolog process.

The following commands are available:
\\{inferior-prolog-mode-map}

249 250 251
Entry to this mode calls the value of `prolog-mode-hook' with no arguments,
if that value is non-nil.  Likewise with the value of `comint-mode-hook'.
`prolog-mode-hook' is called after `comint-mode-hook'.
root's avatar
root committed
252

253 254
You can send text to the inferior Prolog from other buffers using the commands
`process-send-region', `process-send-string' and \\[prolog-consult-region].
root's avatar
root committed
255 256 257 258

Commands:
Tab indents for Prolog; with argument, shifts rest
 of expression rigidly with the current line.
259 260
Paragraphs are separated only by blank lines and '%%'.
'%'s start comments.
root's avatar
root committed
261 262 263 264 265 266

Return at end of buffer sends line as input.
Return not at end copies rest of line to end and sends it.
\\[comint-kill-input] and \\[backward-kill-word] are kill commands, imitating normal Unix input editing.
\\[comint-interrupt-subjob] interrupts the shell or its current subjob if any.
\\[comint-stop-subjob] stops. \\[comint-quit-subjob] sends quit signal."
267 268
  (setq comint-prompt-regexp "^| [ ?][- ] *")
  (prolog-mode-variables))
root's avatar
root committed
269

Stefan Monnier's avatar
Stefan Monnier committed
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
(defvar inferior-prolog-buffer nil)

(defun inferior-prolog-run (&optional name)
  (with-current-buffer (make-comint "prolog" (or name prolog-program-name))
    (inferior-prolog-mode)
    (setq-default inferior-prolog-buffer (current-buffer))
    (make-local-variable 'inferior-prolog-buffer)
    (when (and name (not (equal name prolog-program-name)))
      (set (make-local-variable 'prolog-program-name) name))
    (set (make-local-variable 'inferior-prolog-flavor)
         ;; Force re-detection.
         (let* ((proc (get-buffer-process (current-buffer)))
                (pmark (and proc (marker-position (process-mark proc)))))
           (cond
            ((null pmark) (1- (point-min)))
            ;; The use of insert-before-markers in comint.el together with
            ;; the potential use of comint-truncate-buffer in the output
            ;; filter, means that it's difficult to reliably keep track of
            ;; the buffer position where the process's output started.
            ;; If possible we use a marker at "start - 1", so that
            ;; insert-before-marker at `start' won't shift it.  And if not,
            ;; we fall back on using a plain integer.
            ((> pmark (point-min)) (copy-marker (1- pmark)))
            (t (1- pmark)))))
    (add-hook 'comint-output-filter-functions
              'inferior-prolog-guess-flavor nil t)))

(defun inferior-prolog-process (&optional dontstart)
  (or (and (buffer-live-p inferior-prolog-buffer)
           (get-buffer-process inferior-prolog-buffer))
      (unless dontstart
        (inferior-prolog-run)
        ;; Try again.
        (inferior-prolog-process))))

305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
(defvar inferior-prolog-flavor 'unknown
  "Either a symbol or a buffer position offset by one.
If a buffer position, the flavor has not been determined yet and
it is expected that the process's output has been or will
be inserted at that position plus one.")

(defun inferior-prolog-guess-flavor (&optional ignored)
  (save-excursion
    (goto-char (1+ inferior-prolog-flavor))
    (setq inferior-prolog-flavor
          (cond
           ((looking-at "GNU Prolog") 'gnu)
           ((looking-at "Welcome to SWI-Prolog") 'swi)
           ((looking-at ".*\n") 'unknown) ;There's at least one line.
           (t inferior-prolog-flavor))))
  (when (symbolp inferior-prolog-flavor)
    (remove-hook 'comint-output-filter-functions
                 'inferior-prolog-guess-flavor t)
    (if (eq inferior-prolog-flavor 'gnu)
        (set (make-local-variable 'comint-process-echoes) t))))

Jim Blandy's avatar
Jim Blandy committed
326
;;;###autoload
Stefan Monnier's avatar
Stefan Monnier committed
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
(defalias 'run-prolog 'switch-to-prolog)
;;;###autoload
(defun switch-to-prolog (&optional name)
  "Run an inferior Prolog process, input and output via buffer *prolog*.
With prefix argument \\[universal-prefix], prompt for the program to use."
  (interactive
   (list (when current-prefix-arg
           (let ((proc (inferior-prolog-process 'dontstart)))
             (if proc
                 (if (yes-or-no-p "Kill current process before starting new one? ")
                     (kill-process proc)
                   (error "Abort")))
             (read-string "Run Prolog: " prolog-program-name)))))
  (unless (inferior-prolog-process 'dontstart)
    (inferior-prolog-run name))
  (pop-to-buffer inferior-prolog-buffer))
root's avatar
root committed
343

344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
(defun inferior-prolog-self-insert-command ()
  "Insert the char in the buffer or pass it directly to the process."
  (interactive)
  (let* ((proc (get-buffer-process (current-buffer)))
         (pmark (and proc (marker-position (process-mark proc)))))
    (if (and (eq inferior-prolog-flavor 'gnu)
             pmark
             (null current-prefix-arg)
             (eobp)
             (eq (point) pmark)
             (save-excursion
               (goto-char (- pmark 3))
               (looking-at " \\? ")))
        (comint-send-string proc (string last-command-char))
      (call-interactively 'self-insert-command))))

root's avatar
root committed
360
(defun prolog-consult-region (compile beg end)
361 362
  "Send the region to the Prolog process made by \"M-x run-prolog\".
If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode."
root's avatar
root committed
363
  (interactive "P\nr")
Stefan Monnier's avatar
Stefan Monnier committed
364 365 366 367 368 369
  (let ((proc (inferior-prolog-process)))
    (comint-send-string proc
                        (if compile prolog-compile-string
                          prolog-consult-string))
    (comint-send-region proc beg end)
    (comint-send-string proc "\n")		;May be unnecessary
root's avatar
root committed
370
    (if prolog-eof-string
Stefan Monnier's avatar
Stefan Monnier committed
371 372 373
        (comint-send-string proc prolog-eof-string)
      (with-current-buffer (process-buffer proc)
        (comint-send-eof))))) ;Send eof to prolog process.
root's avatar
root committed
374 375 376

(defun prolog-consult-region-and-go (compile beg end)
  "Send the region to the inferior Prolog, and switch to *prolog* buffer.
377
If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode."
root's avatar
root committed
378 379
  (interactive "P\nr")
  (prolog-consult-region compile beg end)
380
  (pop-to-buffer inferior-prolog-buffer))
Eric S. Raymond's avatar
Eric S. Raymond committed
381

Stefan Monnier's avatar
Stefan Monnier committed
382 383 384 385 386 387 388 389 390 391
(defun inferior-prolog-load-file ()
  "Pass the current buffer's file to the inferior prolog process."
  (interactive)
  (save-buffer)
  (let ((file buffer-file-name)
        (proc (inferior-prolog-process)))
    (with-current-buffer (process-buffer proc)
      (comint-send-string proc (concat "['" (file-relative-name file) "'].\n"))
      (pop-to-buffer (current-buffer)))))

Richard M. Stallman's avatar
Richard M. Stallman committed
392 393
(provide 'prolog)

Stefan Monnier's avatar
Stefan Monnier committed
394
;; arch-tag: f3ec6748-1272-4ab6-8826-c50cb1607636
Eric S. Raymond's avatar
Eric S. Raymond committed
395
;;; prolog.el ends here