]> code.delx.au - gnu-emacs/blob - lisp/progmodes/prolog.el
Convert consecutive FSF copyright years to ranges.
[gnu-emacs] / lisp / progmodes / prolog.el
1 ;;; prolog.el --- major mode for editing and running Prolog (and Mercury) code
2
3 ;; Copyright (C) 1986-1987, 1997-1999, 2002-2003, 2011
4 ;; Free Software Foundation, Inc.
5
6 ;; Authors: Emil Åström <emil_astrom(at)hotmail(dot)com>
7 ;; Milan Zamazal <pdm(at)freesoft(dot)cz>
8 ;; Stefan Bruda <stefan(at)bruda(dot)ca> (current maintainer)
9 ;; * See below for more details
10 ;; Keywords: prolog major mode sicstus swi mercury
11
12 (defvar prolog-mode-version "1.22"
13 "Prolog mode version number.")
14
15 ;; This file is part of GNU Emacs.
16
17 ;; GNU Emacs is free software: you can redistribute it and/or modify
18 ;; it under the terms of the GNU General Public License as published by
19 ;; the Free Software Foundation, either version 3 of the License, or
20 ;; (at your option) any later version.
21
22 ;; GNU Emacs is distributed in the hope that it will be useful,
23 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
24 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 ;; GNU General Public License for more details.
26
27 ;; You should have received a copy of the GNU General Public License
28 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
29
30 ;; Original author: Masanobu UMEDA <umerin(at)mse(dot)kyutech(dot)ac(dot)jp>
31 ;; Parts of this file was taken from a modified version of the original
32 ;; by Johan Andersson, Peter Olin, Mats Carlsson, Johan Bevemyr, Stefan
33 ;; Andersson, and Per Danielsson (all SICS people), and Henrik Båkman
34 ;; at Uppsala University, Sweden.
35 ;;
36 ;; Some ideas and also a few lines of code have been borrowed (not stolen ;-)
37 ;; from Oz.el, the Emacs major mode for the Oz programming language,
38 ;; Copyright (C) 1993 DFKI GmbH, Germany, with permission.
39 ;; Authors: Ralf Scheidhauer and Michael Mehl ([scheidhr|mehl](at)dfki(dot)uni-sb(dot)de)
40 ;;
41 ;; More ideas and code have been taken from the SICStus debugger mode
42 ;; (http://www.csd.uu.se/~perm/source_debug/index.shtml -- broken link
43 ;; as of Mon May 5 08:23:48 EDT 2003) by Per Mildner.
44 ;;
45 ;; Additions for ECLiPSe and other helpful suggestions: Stephan Heuel
46 ;; <heuel(at)ipb(dot)uni-bonn(dot)de>
47
48 ;;; Commentary:
49 ;;
50 ;; This package provides a major mode for editing Prolog code, with
51 ;; all the bells and whistles one would expect, including syntax
52 ;; highlighting and auto indentation. It can also send regions to an
53 ;; inferior Prolog process.
54 ;;
55 ;; The code requires the comint, easymenu, info, imenu, and font-lock
56 ;; libraries. These are normally distributed with GNU Emacs and
57 ;; XEmacs.
58
59 ;;; Installation:
60 ;;
61 ;; Insert the following lines in your init file--typically ~/.emacs
62 ;; (GNU Emacs and XEmacs <21.4), or ~/.xemacs/init.el (XEmacs
63 ;; 21.4)--to use this mode when editing Prolog files under Emacs:
64 ;;
65 ;; (setq load-path (cons "/usr/lib/xemacs/site-lisp" load-path))
66 ;; (autoload 'run-prolog "prolog" "Start a Prolog sub-process." t)
67 ;; (autoload 'prolog-mode "prolog" "Major mode for editing Prolog programs." t)
68 ;; (autoload 'mercury-mode "prolog" "Major mode for editing Mercury programs." t)
69 ;; (setq prolog-system 'swi) ; optional, the system you are using;
70 ;; ; see `prolog-system' below for possible values
71 ;; (setq auto-mode-alist (append '(("\\.pl$" . prolog-mode)
72 ;; ("\\.m$" . mercury-mode))
73 ;; auto-mode-alist))
74 ;;
75 ;; where the path in the first line is the file system path to this file.
76 ;; MSDOS paths can be written like "d:/programs/emacs-19.34/site-lisp".
77 ;; Note: In XEmacs, either `/usr/lib/xemacs/site-lisp' (RPM default in
78 ;; Red Hat-based distributions) or `/usr/local/lib/xemacs/site-lisp'
79 ;; (default when compiling from sources) are automatically added to
80 ;; `load-path', so the first line is not necessary provided that you
81 ;; put this file in the appropriate place.
82 ;;
83 ;; The last s-expression above makes sure that files ending with .pl
84 ;; are assumed to be Prolog files and not Perl, which is the default
85 ;; Emacs setting. If this is not wanted, remove this line. It is then
86 ;; necessary to either
87 ;;
88 ;; o insert in your Prolog files the following comment as the first line:
89 ;;
90 ;; % -*- Mode: Prolog -*-
91 ;;
92 ;; and then the file will be open in Prolog mode no matter its
93 ;; extension, or
94 ;;
95 ;; o manually switch to prolog mode after opening a Prolog file, by typing
96 ;; M-x prolog-mode.
97 ;;
98 ;; If the command to start the prolog process ('sicstus', 'pl' or
99 ;; 'swipl' for SWI prolog, etc.) is not available in the default path,
100 ;; then it is necessary to set the value of the environment variable
101 ;; EPROLOG to a shell command to invoke the prolog process. In XEmacs
102 ;; and Emacs 20+ you can also customize the variable
103 ;; `prolog-program-name' (in the group `prolog-inferior') and provide
104 ;; a full path for your Prolog system (swi, scitus, etc.).
105 ;;
106 ;; Note: I (Stefan, the current maintainer) work under XEmacs. Future
107 ;; developments will thus be biased towards XEmacs (OK, I admit it,
108 ;; I am biased towards XEmacs in general), though I will do my best
109 ;; to keep the GNU Emacs compatibility. So if you work under Emacs
110 ;; and see something that does not work do drop me a line, as I have
111 ;; a smaller chance to notice this kind of bugs otherwise.
112
113 ;; Changelog:
114
115 ;; Version 1.22:
116 ;; o Allowed both 'swipl' and 'pl' as names for the SWI Prolog
117 ;; interpreter.
118 ;; o Atoms that start a line are not blindly coloured as
119 ;; predicates. Instead we check that they are followed by ( or
120 ;; :- first. Patch suggested by Guy Wiener.
121 ;; Version 1.21:
122 ;; o Cleaned up the code that defines faces. The missing face
123 ;; warnings on some Emacsen should disappear.
124 ;; Version 1.20:
125 ;; o Improved the handling of clause start detection and multi-line
126 ;; comments: `prolog-clause-start' no longer finds non-predicate
127 ;; (e.g., capitalized strings) beginning of clauses.
128 ;; `prolog-tokenize' recognizes when the end point is within a
129 ;; multi-line comment.
130 ;; Version 1.19:
131 ;; o Minimal changes for Aquamacs inclusion and in general for
132 ;; better coping with finding the Prolog executable. Patch
133 ;; provided by David Reitter
134 ;; Version 1.18:
135 ;; o Fixed syntax highlighting for clause heads that do not begin at
136 ;; the beginning of the line.
137 ;; o Fixed compilation warnings under Emacs.
138 ;; o Updated the email address of the current maintainer.
139 ;; Version 1.17:
140 ;; o Minor indentation fix (patch by Markus Triska)
141 ;; o `prolog-underscore-wordchar-flag' defaults now to nil (more
142 ;; consistent to other Emacs modes)
143 ;; Version 1.16:
144 ;; o Eliminated a possible compilation warning.
145 ;; Version 1.15:
146 ;; o Introduced three new customizable variables: electric colon
147 ;; (`prolog-electric-colon-flag', default nil), electric dash
148 ;; (`prolog-electric-dash-flag', default nil), and a possibility
149 ;; to prevent the predicate template insertion from adding commata
150 ;; (`prolog-electric-dot-full-predicate-template', defaults to t
151 ;; since it seems quicker to me to just type those commata). A
152 ;; trivial adaptation of a patch by Markus Triska.
153 ;; o Improved the behaviour of electric if-then-else to only skip
154 ;; forward if the parenthesis/semicolon is preceded by
155 ;; whitespace. Once more a trivial adaptation of a patch by
156 ;; Markus Triska.
157 ;; Version 1.14:
158 ;; o Cleaned up align code. `prolog-align-flag' is eliminated (since
159 ;; on a second thought it does not do anything useful). Added key
160 ;; binding (C-c C-a) and menu entry for alignment.
161 ;; o Condensed regular expressions for lower and upper case
162 ;; characters (GNU Emacs seems to go over the regexp length limit
163 ;; with the original form). My code on the matter was improved
164 ;; considerably by Markus Triska.
165 ;; o Fixed `prolog-insert-spaces-after-paren' (which used an
166 ;; unitialized variable).
167 ;; o Minor changes to clean up the code and avoid some implicit
168 ;; package requirements.
169 ;; Version 1.13:
170 ;; o Removed the use of `map-char-table' in `prolog-build-case-strings'
171 ;; which appears to cause prblems in (at least) Emacs 23.0.0.1.
172 ;; o Added if-then-else indentation + corresponding electric
173 ;; characters. New customization: `prolog-electric-if-then-else-flag'
174 ;; o Align support (requires `align'). New customization:
175 ;; `prolog-align-flag'.
176 ;; o Temporary consult files have now the same name throughout the
177 ;; session. This prevents issues with reconsulting a buffer
178 ;; (this event is no longer passed to Prolog as a request to
179 ;; consult a new file).
180 ;; o Adaptive fill mode is now turned on. Comment indentation is
181 ;; still worse than it could be though, I am working on it.
182 ;; o Improved filling and auto-filling capabilities. Now block
183 ;; comments should be [auto-]filled correctly most of the time;
184 ;; the following pattern in particular is worth noting as being
185 ;; filled correctly:
186 ;; <some code here> % some comment here that goes beyond the
187 ;; % rightmost column, possibly combined with
188 ;; % subsequent comment lines
189 ;; o `prolog-char-quote-workaround' now defaults to nil.
190 ;; o Note: Many of the above improvements have been suggested by
191 ;; Markus Triska, who also provided useful patches on the matter
192 ;; when he realized that I was slow in responding. Many thanks.
193 ;; Version 1.11 / 1.12
194 ;; o GNU Emacs compatibility fix for paragraph filling (fixed
195 ;; incorrectly in 1.11, fix fixed in 1.12).
196 ;; Version 1.10
197 ;; o Added paragraph filling in comment blocks and also correct auto
198 ;; filling for comments.
199 ;; o Fixed the possible "Regular expression too big" error in
200 ;; `prolog-electric-dot'.
201 ;; Version 1.9
202 ;; o Parenthesis expressions are now indented by default so that
203 ;; components go one underneath the other, just as for compound
204 ;; terms. You can use the old style (the second and subsequent
205 ;; lines being indented to the right in a parenthesis expression)
206 ;; by setting the customizable variable `prolog-paren-indent-p'
207 ;; (group "Prolog Indentation") to t.
208 ;; o (Somehow awkward) handling of the 0' character escape
209 ;; sequence. I am looking into a better way of doing it but
210 ;; prospects look bleak. If this breaks things for you please let
211 ;; me know and also set the `prolog-char-quote-workaround' (group
212 ;; "Prolog Other") to nil.
213 ;; Version 1.8
214 ;; o Key binding fix.
215 ;; Version 1.7
216 ;; o Fixed a number of issues with the syntax of single quotes,
217 ;; including Debian bug #324520.
218 ;; Version 1.6
219 ;; o Fixed mercury mode menu initialization (Debian bug #226121).
220 ;; o Fixed (i.e., eliminated) Delete remapping (Debian bug #229636).
221 ;; o Corrected indentation for clauses defining quoted atoms.
222 ;; Version 1.5:
223 ;; o Keywords fontifying should work in console mode so this is
224 ;; enabled everywhere.
225 ;; Version 1.4:
226 ;; o Now supports GNU Prolog--minor adaptation of a patch by Stefan
227 ;; Moeding.
228 ;; Version 1.3:
229 ;; o Info-follow-nearest-node now called correctly under Emacs too
230 ;; (thanks to Nicolas Pelletier). Should be implemented more
231 ;; elegantly (i.e., without compilation warnings) in the future.
232 ;; Version 1.2:
233 ;; o Another prompt fix, still in SWI mode (people seem to have
234 ;; changed the prompt of SWI Prolog).
235 ;; Version 1.1:
236 ;; o Fixed dots in the end of line comments causing indentation
237 ;; problems. The following code is now correctly indented (note
238 ;; the dot terminating the comment):
239 ;; a(X) :- b(X),
240 ;; c(X). % comment here.
241 ;; a(X).
242 ;; and so is this (and variants):
243 ;; a(X) :- b(X),
244 ;; c(X). /* comment here. */
245 ;; a(X).
246 ;; Version 1.0:
247 ;; o Revamped the menu system.
248 ;; o Yet another prompt recognition fix (SWI mode).
249 ;; o This is more of a renumbering than a new edition. I promoted
250 ;; the mode to version 1.0 to emphasize the fact that it is now
251 ;; mature and stable enough to be considered production (in my
252 ;; opinion anyway).
253 ;; Version 0.1.41:
254 ;; o GNU Emacs compatibility fixes.
255 ;; Version 0.1.40:
256 ;; o prolog-get-predspec is now suitable to be called as
257 ;; imenu-extract-index-name-function. The predicate index works.
258 ;; o Since imenu works now as advertised, prolog-imenu-flag is t
259 ;; by default.
260 ;; o Eliminated prolog-create-predicate-index since the imenu
261 ;; utilities now work well. Actually, this function is also
262 ;; buggy, and I see no reason to fix it since we do not need it
263 ;; anyway.
264 ;; o Fixed prolog-pred-start, prolog-clause-start, prolog-clause-info.
265 ;; o Fix for prolog-build-case-strings; now prolog-upper-case-string
266 ;; and prolog-lower-case-string are correctly initialized,
267 ;; o Various font-lock changes; most importantly, block comments (/*
268 ;; ... */) are now correctly fontified in XEmacs even when they
269 ;; extend on multiple lines.
270 ;; Version 0.1.36:
271 ;; o The debug prompt of SWI Prolog is now correctly recognized.
272 ;; Version 0.1.35:
273 ;; o Minor font-lock bug fixes.
274
275 ;;; TODO:
276
277 ;; Replace ":type 'sexp" with more precise Custom types.
278 \f
279 ;;; Code:
280
281 (eval-when-compile
282 (require 'compile)
283 (require 'font-lock)
284 ;; We need imenu everywhere because of the predicate index!
285 (require 'imenu)
286 ;)
287 (require 'info)
288 (require 'shell)
289 )
290
291 (require 'comint)
292 (require 'easymenu)
293 (require 'align)
294
295
296 (defgroup prolog nil
297 "Major modes for editing and running Prolog and Mercury files."
298 :group 'languages)
299
300 (defgroup prolog-faces nil
301 "Prolog mode specific faces."
302 :group 'font-lock)
303
304 (defgroup prolog-indentation nil
305 "Prolog mode indentation configuration."
306 :group 'prolog)
307
308 (defgroup prolog-font-lock nil
309 "Prolog mode font locking patterns."
310 :group 'prolog)
311
312 (defgroup prolog-keyboard nil
313 "Prolog mode keyboard flags."
314 :group 'prolog)
315
316 (defgroup prolog-inferior nil
317 "Inferior Prolog mode options."
318 :group 'prolog)
319
320 (defgroup prolog-other nil
321 "Other Prolog mode options."
322 :group 'prolog)
323
324 \f
325 ;;-------------------------------------------------------------------
326 ;; User configurable variables
327 ;;-------------------------------------------------------------------
328
329 ;; General configuration
330
331 (defcustom prolog-system nil
332 "*Prolog interpreter/compiler used.
333 The value of this variable is nil or a symbol.
334 If it is a symbol, it determines default values of other configuration
335 variables with respect to properties of the specified Prolog
336 interpreter/compiler.
337
338 Currently recognized symbol values are:
339 eclipse - Eclipse Prolog
340 mercury - Mercury
341 sicstus - SICStus Prolog
342 swi - SWI Prolog
343 gnu - GNU Prolog"
344 :group 'prolog
345 :type '(choice (const :tag "SICStus" :value sicstus)
346 (const :tag "SWI Prolog" :value swi)
347 (const :tag "Default" :value nil)))
348 (make-variable-buffer-local 'prolog-system)
349
350 ;; NB: This alist can not be processed in prolog-mode-variables to
351 ;; create a prolog-system-version-i variable since it is needed
352 ;; prior to the call to prolog-mode-variables.
353 (defcustom prolog-system-version
354 '((sicstus (3 . 6))
355 (swi (0 . 0))
356 (mercury (0 . 0))
357 (eclipse (3 . 7))
358 (gnu (0 . 0)))
359 "*Alist of Prolog system versions.
360 The version numbers are of the format (Major . Minor)."
361 :group 'prolog)
362
363 ;; Indentation
364
365 (defcustom prolog-indent-width 4
366 "*The indentation width used by the editing buffer."
367 :group 'prolog-indentation
368 :type 'integer)
369
370 (defcustom prolog-align-comments-flag t
371 "*Non-nil means automatically align comments when indenting."
372 :group 'prolog-indentation
373 :type 'boolean)
374
375 (defcustom prolog-indent-mline-comments-flag t
376 "*Non-nil means indent contents of /* */ comments.
377 Otherwise leave such lines as they are."
378 :group 'prolog-indentation
379 :type 'boolean)
380
381 (defcustom prolog-object-end-to-0-flag t
382 "*Non-nil means indent closing '}' in SICStus object definitions to level 0.
383 Otherwise indent to `prolog-indent-width'."
384 :group 'prolog-indentation
385 :type 'boolean)
386
387 (defcustom prolog-left-indent-regexp "\\(;\\|\\*?->\\)"
388 "*Regexp for character sequences after which next line is indented.
389 Next line after such a regexp is indented to the opening paranthesis level."
390 :group 'prolog-indentation
391 :type 'regexp)
392
393 (defcustom prolog-paren-indent-p nil
394 "*If non-nil, increase indentation for parenthesis expressions.
395 The second and subsequent line in a parenthesis expression other than
396 a compound term can either be indented `prolog-paren-indent' to the
397 right (if this variable is non-nil) or in the same way as for compound
398 terms (if this variable is nil, default)."
399 :group 'prolog-indentation
400 :type 'boolean)
401
402 (defcustom prolog-paren-indent 4
403 "*The indentation increase for parenthesis expressions.
404 Only used in ( If -> Then ; Else) and ( Disj1 ; Disj2 ) style expressions."
405 :group 'prolog-indentation
406 :type 'integer)
407
408 (defcustom prolog-parse-mode 'beg-of-clause
409 "*The parse mode used (decides from which point parsing is done).
410 Legal values:
411 'beg-of-line - starts parsing at the beginning of a line, unless the
412 previous line ends with a backslash. Fast, but has
413 problems detecting multiline /* */ comments.
414 'beg-of-clause - starts parsing at the beginning of the current clause.
415 Slow, but copes better with /* */ comments."
416 :group 'prolog-indentation
417 :type '(choice (const :value beg-of-line)
418 (const :value beg-of-clause)))
419
420 ;; Font locking
421
422 (defcustom prolog-keywords
423 '((eclipse
424 ("use_module" "begin_module" "module_interface" "dynamic"
425 "external" "export" "dbgcomp" "nodbgcomp" "compile"))
426 (mercury
427 ("all" "else" "end_module" "equality" "external" "fail" "func" "if"
428 "implementation" "import_module" "include_module" "inst" "instance"
429 "interface" "mode" "module" "not" "pragma" "pred" "some" "then" "true"
430 "type" "typeclass" "use_module" "where"))
431 (sicstus
432 ("block" "dynamic" "mode" "module" "multifile" "meta_predicate"
433 "parallel" "public" "sequential" "volatile"))
434 (swi
435 ("discontiguous" "dynamic" "ensure_loaded" "export" "export_list" "import"
436 "meta_predicate" "module" "module_transparent" "multifile" "require"
437 "use_module" "volatile"))
438 (gnu
439 ("built_in" "char_conversion" "discontiguous" "dynamic" "ensure_linked"
440 "ensure_loaded" "foreign" "include" "initialization" "multifile" "op"
441 "public" "set_prolog_flag"))
442 (t
443 ;; FIXME: Shouldn't we just use the union of all the above here?
444 ("dynamic" "module")))
445 "*Alist of Prolog keywords which is used for font locking of directives."
446 :group 'prolog-font-lock
447 :type 'sexp)
448
449 (defcustom prolog-types
450 '((mercury
451 ("char" "float" "int" "io__state" "string" "univ"))
452 (t nil))
453 "*Alist of Prolog types used by font locking."
454 :group 'prolog-font-lock
455 :type 'sexp)
456
457 (defcustom prolog-mode-specificators
458 '((mercury
459 ("bound" "di" "free" "ground" "in" "mdi" "mui" "muo" "out" "ui" "uo"))
460 (t nil))
461 "*Alist of Prolog mode specificators used by font locking."
462 :group 'prolog-font-lock
463 :type 'sexp)
464
465 (defcustom prolog-determinism-specificators
466 '((mercury
467 ("cc_multi" "cc_nondet" "det" "erroneous" "failure" "multi" "nondet"
468 "semidet"))
469 (t nil))
470 "*Alist of Prolog determinism specificators used by font locking."
471 :group 'prolog-font-lock
472 :type 'sexp)
473
474 (defcustom prolog-directives
475 '((mercury
476 ("^#[0-9]+"))
477 (t nil))
478 "*Alist of Prolog source code directives used by font locking."
479 :group 'prolog-font-lock
480 :type 'sexp)
481
482
483 ;; Keyboard
484
485 (defcustom prolog-electric-newline-flag (not (fboundp 'electric-indent-mode))
486 "*Non-nil means automatically indent the next line when the user types RET."
487 :group 'prolog-keyboard
488 :type 'boolean)
489
490 (defcustom prolog-hungry-delete-key-flag nil
491 "*Non-nil means delete key consumes all preceding spaces."
492 :group 'prolog-keyboard
493 :type 'boolean)
494
495 (defcustom prolog-electric-dot-flag nil
496 "*Non-nil means make dot key electric.
497 Electric dot appends newline or inserts head of a new clause.
498 If dot is pressed at the end of a line where at least one white space
499 precedes the point, it inserts a recursive call to the current predicate.
500 If dot is pressed at the beginning of an empty line, it inserts the head
501 of a new clause for the current predicate. It does not apply in strings
502 and comments.
503 It does not apply in strings and comments."
504 :group 'prolog-keyboard
505 :type 'boolean)
506
507 (defcustom prolog-electric-dot-full-predicate-template nil
508 "*If nil, electric dot inserts only the current predicate's name and `('
509 for recursive calls or new clause heads. Non-nil means to also
510 insert enough commata to cover the predicate's arity and `)',
511 and dot and newline for recursive calls."
512 :group 'prolog-keyboard
513 :type 'boolean)
514
515 (defcustom prolog-electric-underscore-flag nil
516 "*Non-nil means make underscore key electric.
517 Electric underscore replaces the current variable with underscore.
518 If underscore is pressed not on a variable then it behaves as usual."
519 :group 'prolog-keyboard
520 :type 'boolean)
521
522 (defcustom prolog-electric-tab-flag nil
523 "*Non-nil means make TAB key electric.
524 Electric TAB inserts spaces after parentheses, ->, and ;
525 in ( If -> Then ; Else) and ( Disj1 ; Disj2 ) style expressions."
526 :group 'prolog-keyboard
527 :type 'boolean)
528
529 (defcustom prolog-electric-if-then-else-flag nil
530 "*Non-nil makes `(', `>' and `;' electric
531 to automatically indent if-then-else constructs."
532 :group 'prolog-keyboard
533 :type 'boolean)
534
535 (defcustom prolog-electric-colon-flag nil
536 "*Makes `:' electric (inserts `:-' on a new line).
537 If non-nil, pressing `:' at the end of a line that starts in
538 the first column (i.e., clause heads) inserts ` :-' and newline."
539 :group 'prolog-keyboard
540 :type 'boolean)
541
542 (defcustom prolog-electric-dash-flag nil
543 "*Makes `-' electric (inserts a `-->' on a new line).
544 If non-nil, pressing `-' at the end of a line that starts in
545 the first column (i.e., DCG heads) inserts ` -->' and newline."
546 :group 'prolog-keyboard
547 :type 'boolean)
548
549 (defcustom prolog-old-sicstus-keys-flag nil
550 "*Non-nil means old SICStus Prolog mode keybindings are used."
551 :group 'prolog-keyboard
552 :type 'boolean)
553
554 ;; Inferior mode
555
556 (defcustom prolog-program-name
557 `(((getenv "EPROLOG") (eval (getenv "EPROLOG")))
558 (eclipse "eclipse")
559 (mercury nil)
560 (sicstus "sicstus")
561 (swi ,(if (not (executable-find "swipl")) "pl" "swipl"))
562 (gnu "gprolog")
563 (t ,(let ((names '("prolog" "gprolog" "swipl" "pl")))
564 (while (and names
565 (not (executable-find (car names))))
566 (setq names (cdr names)))
567 (or (car names) "prolog"))))
568 "*Alist of program names for invoking an inferior Prolog with `run-prolog'."
569 :group 'prolog-inferior
570 :type 'sexp)
571
572 (defcustom prolog-program-switches
573 '((sicstus ("-i"))
574 (t nil))
575 "*Alist of switches given to inferior Prolog run with `run-prolog'."
576 :group 'prolog-inferior
577 :type 'sexp)
578
579 (defcustom prolog-consult-string
580 '((eclipse "[%f].")
581 (mercury nil)
582 (sicstus (eval (if (prolog-atleast-version '(3 . 7))
583 "prolog:zap_file(%m,%b,consult,%l)."
584 "prolog:zap_file(%m,%b,consult).")))
585 (swi "[%f].")
586 (gnu "[%f].")
587 (t "reconsult(%f)."))
588 "*Alist of strings defining predicate for reconsulting.
589
590 Some parts of the string are replaced:
591 `%f' by the name of the consulted file (can be a temporary file)
592 `%b' by the file name of the buffer to consult
593 `%m' by the module name and name of the consulted file separated by colon
594 `%l' by the line offset into the file. This is 0 unless consulting a
595 region of a buffer, in which case it is the number of lines before
596 the region."
597 :group 'prolog-inferior
598 :type 'sexp)
599
600 (defcustom prolog-compile-string
601 '((eclipse "[%f].")
602 (mercury "mmake ")
603 (sicstus (eval (if (prolog-atleast-version '(3 . 7))
604 "prolog:zap_file(%m,%b,compile,%l)."
605 "prolog:zap_file(%m,%b,compile).")))
606 (swi "[%f].")
607 (t "compile(%f)."))
608 "*Alist of strings and lists defining predicate for recompilation.
609
610 Some parts of the string are replaced:
611 `%f' by the name of the compiled file (can be a temporary file)
612 `%b' by the file name of the buffer to compile
613 `%m' by the module name and name of the compiled file separated by colon
614 `%l' by the line offset into the file. This is 0 unless compiling a
615 region of a buffer, in which case it is the number of lines before
616 the region.
617
618 If `prolog-program-name' is non-nil, it is a string sent to a Prolog process.
619 If `prolog-program-name' is nil, it is an argument to the `compile' function."
620 :group 'prolog-inferior
621 :type 'sexp)
622
623 (defcustom prolog-eof-string "end_of_file.\n"
624 "*Alist of strings that represent end of file for prolog.
625 nil means send actual operating system end of file."
626 :group 'prolog-inferior
627 :type 'sexp)
628
629 (defcustom prolog-prompt-regexp
630 '((eclipse "^[a-zA-Z0-9()]* *\\?- \\|^\\[[a-zA-Z]* [0-9]*\\]:")
631 (sicstus "| [ ?][- ] *")
632 (swi "^\\(\\[[a-zA-Z]*\\] \\)?[1-9]?[0-9]*[ ]?\\?- \\|^| +")
633 (t "^ *\\?-"))
634 "*Alist of prompts of the prolog system command line."
635 :group 'prolog-inferior
636 :type 'sexp)
637
638 (defcustom prolog-continued-prompt-regexp
639 '((sicstus "^\\(| +\\| +\\)")
640 (t "^|: +"))
641 "*Alist of regexps matching the prompt when consulting `user'."
642 :group 'prolog-inferior
643 :type 'sexp)
644
645 (defcustom prolog-debug-on-string "debug.\n"
646 "*Predicate for enabling debug mode."
647 :group 'prolog-inferior
648 :type 'string)
649
650 (defcustom prolog-debug-off-string "nodebug.\n"
651 "*Predicate for disabling debug mode."
652 :group 'prolog-inferior
653 :type 'string)
654
655 (defcustom prolog-trace-on-string "trace.\n"
656 "*Predicate for enabling tracing."
657 :group 'prolog-inferior
658 :type 'string)
659
660 (defcustom prolog-trace-off-string "notrace.\n"
661 "*Predicate for disabling tracing."
662 :group 'prolog-inferior
663 :type 'string)
664
665 (defcustom prolog-zip-on-string "zip.\n"
666 "*Predicate for enabling zip mode for SICStus."
667 :group 'prolog-inferior
668 :type 'string)
669
670 (defcustom prolog-zip-off-string "nozip.\n"
671 "*Predicate for disabling zip mode for SICStus."
672 :group 'prolog-inferior
673 :type 'string)
674
675 (defcustom prolog-use-standard-consult-compile-method-flag t
676 "*Non-nil means use the standard compilation method.
677 Otherwise the new compilation method will be used. This
678 utilises a special compilation buffer with the associated
679 features such as parsing of error messages and automatically
680 jumping to the source code responsible for the error.
681
682 Warning: the new method is so far only experimental and
683 does contain bugs. The recommended setting for the novice user
684 is non-nil for this variable."
685 :group 'prolog-inferior
686 :type 'boolean)
687
688
689 ;; Miscellaneous
690
691 (defcustom prolog-use-prolog-tokenizer-flag
692 (not (fboundp 'syntax-propertize-rules))
693 "*Non-nil means use the internal prolog tokenizer for indentation etc.
694 Otherwise use `parse-partial-sexp' which is faster but sometimes incorrect."
695 :group 'prolog-other
696 :type 'boolean)
697
698 (defcustom prolog-imenu-flag t
699 "*Non-nil means add a clause index menu for all prolog files."
700 :group 'prolog-other
701 :type 'boolean)
702
703 (defcustom prolog-imenu-max-lines 3000
704 "*The maximum number of lines of the file for imenu to be enabled.
705 Relevant only when `prolog-imenu-flag' is non-nil."
706 :group 'prolog-other
707 :type 'integer)
708
709 (defcustom prolog-info-predicate-index
710 "(sicstus)Predicate Index"
711 "*The info node for the SICStus predicate index."
712 :group 'prolog-other
713 :type 'string)
714
715 (defcustom prolog-underscore-wordchar-flag nil
716 "*Non-nil means underscore (_) is a word-constituent character."
717 :group 'prolog-other
718 :type 'boolean)
719
720 (defcustom prolog-use-sicstus-sd nil
721 "*If non-nil, use the source level debugger of SICStus 3#7 and later."
722 :group 'prolog-other
723 :type 'boolean)
724
725 (defcustom prolog-char-quote-workaround nil
726 "*If non-nil, declare 0 as a quote character to handle 0'<char>.
727 This is really kludgy, and unneeded (i.e. obsolete) in Emacs>=24."
728 :group 'prolog-other
729 :type 'boolean)
730
731 \f
732 ;;-------------------------------------------------------------------
733 ;; Internal variables
734 ;;-------------------------------------------------------------------
735
736 ;;(defvar prolog-temp-filename "") ; Later set by `prolog-temporary-file'
737
738 (defvar prolog-mode-syntax-table
739 ;; The syntax accepted varies depending on the implementation used.
740 ;; Here are some of the differences:
741 ;; - SWI-Prolog accepts nested /*..*/ comments.
742 ;; - Edinburgh-style Prologs take <radix>'<number> for non-decimal number,
743 ;; whereas ISO-style Prologs use 0[obx]<number> instead.
744 ;; - In atoms \x<hex> sometimes needs a terminating \ (ISO-style)
745 ;; and sometimes not.
746 (let ((table (make-syntax-table)))
747 (if prolog-underscore-wordchar-flag
748 (modify-syntax-entry ?_ "w" table)
749 (modify-syntax-entry ?_ "_" table))
750
751 (modify-syntax-entry ?+ "." table)
752 (modify-syntax-entry ?- "." table)
753 (modify-syntax-entry ?= "." table)
754 (modify-syntax-entry ?< "." table)
755 (modify-syntax-entry ?> "." table)
756 (modify-syntax-entry ?| "." table)
757 (modify-syntax-entry ?\' "\"" table)
758
759 ;; Any better way to handle the 0'<char> construct?!?
760 (when prolog-char-quote-workaround
761 (modify-syntax-entry ?0 "\\" table))
762
763 (modify-syntax-entry ?% "<" table)
764 (modify-syntax-entry ?\n ">" table)
765 (if (featurep 'xemacs)
766 (progn
767 (modify-syntax-entry ?* ". 67" table)
768 (modify-syntax-entry ?/ ". 58" table)
769 )
770 ;; Emacs wants to see this it seems:
771 (modify-syntax-entry ?* ". 23b" table)
772 (modify-syntax-entry ?/ ". 14" table)
773 )
774 table))
775 (defvar prolog-mode-abbrev-table nil)
776 (defvar prolog-upper-case-string ""
777 "A string containing all upper case characters.
778 Set by prolog-build-case-strings.")
779 (defvar prolog-lower-case-string ""
780 "A string containing all lower case characters.
781 Set by prolog-build-case-strings.")
782
783 (defvar prolog-atom-char-regexp ""
784 "Set by prolog-set-atom-regexps.")
785 ;; "Regexp specifying characters which constitute atoms without quoting.")
786 (defvar prolog-atom-regexp ""
787 "Set by prolog-set-atom-regexps.")
788
789 (defconst prolog-left-paren "[[({]"
790 "The characters used as left parentheses for the indentation code.")
791 (defconst prolog-right-paren "[])}]"
792 "The characters used as right parentheses for the indentation code.")
793
794 (defconst prolog-quoted-atom-regexp
795 "\\(^\\|[^0-9]\\)\\('\\([^\n']\\|\\\\'\\)*'\\)"
796 "Regexp matching a quoted atom.")
797 (defconst prolog-string-regexp
798 "\\(\"\\([^\n\"]\\|\\\\\"\\)*\"\\)"
799 "Regexp matching a string.")
800 (defconst prolog-head-delimiter "\\(:-\\|\\+:\\|-:\\|\\+\\?\\|-\\?\\|-->\\)"
801 "A regexp for matching on the end delimiter of a head (e.g. \":-\").")
802
803 (defvar prolog-compilation-buffer "*prolog-compilation*"
804 "Name of the output buffer for Prolog compilation/consulting.")
805
806 (defvar prolog-temporary-file-name nil)
807 (defvar prolog-keywords-i nil)
808 (defvar prolog-types-i nil)
809 (defvar prolog-mode-specificators-i nil)
810 (defvar prolog-determinism-specificators-i nil)
811 (defvar prolog-directives-i nil)
812 (defvar prolog-program-name-i nil)
813 (defvar prolog-program-switches-i nil)
814 (defvar prolog-consult-string-i nil)
815 (defvar prolog-compile-string-i nil)
816 (defvar prolog-eof-string-i nil)
817 (defvar prolog-prompt-regexp-i nil)
818 (defvar prolog-continued-prompt-regexp-i nil)
819 (defvar prolog-help-function-i nil)
820
821 (defvar prolog-align-rules
822 (eval-when-compile
823 (mapcar
824 (lambda (x)
825 (let ((name (car x))
826 (sym (cdr x)))
827 `(,(intern (format "prolog-%s" name))
828 (regexp . ,(format "\\(\\s-*\\)%s\\(\\s-*\\)" sym))
829 (tab-stop . nil)
830 (modes . '(prolog-mode))
831 (group . (1 2)))))
832 '(("dcg" . "-->") ("rule" . ":-") ("simplification" . "<=>")
833 ("propagation" . "==>")))))
834
835
836 \f
837 ;;-------------------------------------------------------------------
838 ;; Prolog mode
839 ;;-------------------------------------------------------------------
840
841 ;; Example: (prolog-atleast-version '(3 . 6))
842 (defun prolog-atleast-version (version)
843 "Return t if the version of the current prolog system is VERSION or later.
844 VERSION is of the format (Major . Minor)"
845 ;; Version.major < major or
846 ;; Version.major = major and Version.minor <= minor
847 (let* ((thisversion (prolog-find-value-by-system prolog-system-version))
848 (thismajor (car thisversion))
849 (thisminor (cdr thisversion)))
850 (or (< (car version) thismajor)
851 (and (= (car version) thismajor)
852 (<= (cdr version) thisminor)))
853 ))
854
855 (define-abbrev-table 'prolog-mode-abbrev-table ())
856
857 (defun prolog-find-value-by-system (alist)
858 "Get value from ALIST according to `prolog-system'."
859 (if (listp alist)
860 (let (result
861 id)
862 (while alist
863 (setq id (car (car alist)))
864 (if (or (eq id prolog-system)
865 (eq id t)
866 (and (listp id)
867 (eval id)))
868 (progn
869 (setq result (car (cdr (car alist))))
870 (if (and (listp result)
871 (eq (car result) 'eval))
872 (setq result (eval (car (cdr result)))))
873 (setq alist nil))
874 (setq alist (cdr alist))))
875 result)
876 alist))
877
878 (defconst prolog-syntax-propertize-function
879 (when (fboundp 'syntax-propertize-rules)
880 (syntax-propertize-rules
881 ;; GNU Prolog only accepts 0'\' rather than 0'', but the only
882 ;; possible meaning of 0'' is rather clear.
883 ("\\<0\\(''?\\)"
884 (1 (unless (save-excursion (nth 8 (syntax-ppss (match-beginning 0))))
885 (string-to-syntax "_"))))
886 ;; We could check that we're not inside an atom, but I don't think
887 ;; that 'foo 8'z could be a valid syntax anyway, so why bother?
888 ("\\<[1-9][0-9]*\\('\\)[0-9a-zA-Z]" (1 "_"))
889 ;; Supposedly, ISO-Prolog wants \NNN\ for octal and \xNNN\ for hexadecimal
890 ;; escape sequences in atoms, so be careful not to let the terminating \
891 ;; escape a subsequent quote.
892 ("\\\\[x0-7][0-9a-fA-F]*\\(\\\\\\)" (1 "_"))
893 )))
894
895 (defun prolog-mode-variables ()
896 "Set some common variables to Prolog code specific values."
897 (setq local-abbrev-table prolog-mode-abbrev-table)
898 (set (make-local-variable 'paragraph-start)
899 (concat "[ \t]*$\\|" page-delimiter)) ;'%%..'
900 (set (make-local-variable 'paragraph-separate) paragraph-start)
901 (set (make-local-variable 'paragraph-ignore-fill-prefix) t)
902 (set (make-local-variable 'normal-auto-fill-function) 'prolog-do-auto-fill)
903 (set (make-local-variable 'indent-line-function) 'prolog-indent-line)
904 (set (make-local-variable 'comment-start) "%")
905 (set (make-local-variable 'comment-end) "")
906 (set (make-local-variable 'comment-add) 1)
907 (set (make-local-variable 'comment-start-skip)
908 ;; This complex regexp makes sure that comments cannot start
909 ;; inside quoted atoms or strings
910 (format "^\\(\\(%s\\|%s\\|[^\n\'\"%%]\\)*\\)\\(/\\*+ *\\|%%+ *\\)"
911 prolog-quoted-atom-regexp prolog-string-regexp))
912 (set (make-local-variable 'comment-indent-function) 'prolog-comment-indent)
913 (set (make-local-variable 'parens-require-spaces) nil)
914 ;; Initialize Prolog system specific variables
915 (dolist (var '(prolog-keywords prolog-types prolog-mode-specificators
916 prolog-determinism-specificators prolog-directives
917 prolog-program-name prolog-program-switches
918 prolog-consult-string prolog-compile-string prolog-eof-string
919 prolog-prompt-regexp prolog-continued-prompt-regexp
920 prolog-help-function))
921 (set (intern (concat (symbol-name var) "-i"))
922 (prolog-find-value-by-system (symbol-value var))))
923 (when (null prolog-program-name-i)
924 (set (make-local-variable 'compile-command) prolog-compile-string-i))
925 (set (make-local-variable 'font-lock-defaults)
926 '(prolog-font-lock-keywords nil nil ((?_ . "w"))))
927 (set (make-local-variable 'syntax-propertize-function)
928 prolog-syntax-propertize-function)
929 )
930
931 (defun prolog-mode-keybindings-common (map)
932 "Define keybindings common to both Prolog modes in MAP."
933 (define-key map "\C-c?" 'prolog-help-on-predicate)
934 (define-key map "\C-c/" 'prolog-help-apropos)
935 (define-key map "\C-c\C-d" 'prolog-debug-on)
936 (define-key map "\C-c\C-t" 'prolog-trace-on)
937 (if (and (eq prolog-system 'sicstus)
938 (prolog-atleast-version '(3 . 7)))
939 (define-key map "\C-c\C-z" 'prolog-zip-on))
940 (define-key map "\C-c\r" 'run-prolog))
941
942 (defun prolog-mode-keybindings-edit (map)
943 "Define keybindings for Prolog mode in MAP."
944 (define-key map "\M-a" 'prolog-beginning-of-clause)
945 (define-key map "\M-e" 'prolog-end-of-clause)
946 (define-key map "\M-q" 'prolog-fill-paragraph)
947 (define-key map "\C-c\C-a" 'align)
948 (define-key map "\C-\M-a" 'prolog-beginning-of-predicate)
949 (define-key map "\C-\M-e" 'prolog-end-of-predicate)
950 (define-key map "\M-\C-c" 'prolog-mark-clause)
951 (define-key map "\M-\C-h" 'prolog-mark-predicate)
952 (define-key map "\M-\C-n" 'prolog-forward-list)
953 (define-key map "\M-\C-p" 'prolog-backward-list)
954 (define-key map "\C-c\C-n" 'prolog-insert-predicate-template)
955 (define-key map "\C-c\C-s" 'prolog-insert-predspec)
956 (define-key map "\M-\r" 'prolog-insert-next-clause)
957 (define-key map "\C-c\C-va" 'prolog-variables-to-anonymous)
958 (define-key map "\C-c\C-v\C-s" 'prolog-view-predspec)
959
960 (define-key map [Backspace] 'prolog-electric-delete)
961 (define-key map "." 'prolog-electric-dot)
962 (define-key map "_" 'prolog-electric-underscore)
963 (define-key map "(" 'prolog-electric-if-then-else)
964 (define-key map ";" 'prolog-electric-if-then-else)
965 (define-key map ">" 'prolog-electric-if-then-else)
966 (define-key map ":" 'prolog-electric-colon)
967 (define-key map "-" 'prolog-electric-dash)
968 (if prolog-electric-newline-flag
969 (define-key map "\r" 'newline-and-indent))
970
971 ;; If we're running SICStus, then map C-c C-c e/d to enabling
972 ;; and disabling of the source-level debugging facilities.
973 ;(if (and (eq prolog-system 'sicstus)
974 ; (prolog-atleast-version '(3 . 7)))
975 ; (progn
976 ; (define-key map "\C-c\C-ce" 'prolog-enable-sicstus-sd)
977 ; (define-key map "\C-c\C-cd" 'prolog-disable-sicstus-sd)
978 ; ))
979
980 (if prolog-old-sicstus-keys-flag
981 (progn
982 (define-key map "\C-c\C-c" 'prolog-consult-predicate)
983 (define-key map "\C-cc" 'prolog-consult-region)
984 (define-key map "\C-cC" 'prolog-consult-buffer)
985 (define-key map "\C-c\C-k" 'prolog-compile-predicate)
986 (define-key map "\C-ck" 'prolog-compile-region)
987 (define-key map "\C-cK" 'prolog-compile-buffer))
988 (define-key map "\C-c\C-p" 'prolog-consult-predicate)
989 (define-key map "\C-c\C-r" 'prolog-consult-region)
990 (define-key map "\C-c\C-b" 'prolog-consult-buffer)
991 (define-key map "\C-c\C-f" 'prolog-consult-file)
992 (define-key map "\C-c\C-cp" 'prolog-compile-predicate)
993 (define-key map "\C-c\C-cr" 'prolog-compile-region)
994 (define-key map "\C-c\C-cb" 'prolog-compile-buffer)
995 (define-key map "\C-c\C-cf" 'prolog-compile-file))
996
997 ;; Inherited from the old prolog.el.
998 (define-key map "\e\C-x" 'prolog-consult-region)
999 (define-key map "\C-c\C-l" 'prolog-consult-file)
1000 (define-key map "\C-c\C-z" 'switch-to-prolog))
1001
1002 (defun prolog-mode-keybindings-inferior (map)
1003 "Define keybindings for inferior Prolog mode in MAP."
1004 ;; No inferior mode specific keybindings now.
1005 )
1006
1007 (defvar prolog-mode-map
1008 (let ((map (make-sparse-keymap)))
1009 (prolog-mode-keybindings-common map)
1010 (prolog-mode-keybindings-edit map)
1011 map))
1012
1013
1014 (defvar prolog-mode-hook nil
1015 "List of functions to call after the prolog mode has initialised.")
1016
1017 (unless (fboundp 'prog-mode)
1018 (defalias 'prog-mode 'fundamental-mode))
1019 ;;;###autoload
1020 (define-derived-mode prolog-mode prog-mode "Prolog"
1021 "Major mode for editing Prolog code.
1022
1023 Blank lines and `%%...' separate paragraphs. `%'s starts a comment
1024 line and comments can also be enclosed in /* ... */.
1025
1026 If an optional argument SYSTEM is non-nil, set up mode for the given system.
1027
1028 To find out what version of Prolog mode you are running, enter
1029 `\\[prolog-mode-version]'.
1030
1031 Commands:
1032 \\{prolog-mode-map}
1033 Entry to this mode calls the value of `prolog-mode-hook'
1034 if that value is non-nil."
1035 (setq mode-name (concat "Prolog"
1036 (cond
1037 ((eq prolog-system 'eclipse) "[ECLiPSe]")
1038 ((eq prolog-system 'sicstus) "[SICStus]")
1039 ((eq prolog-system 'swi) "[SWI]")
1040 ((eq prolog-system 'gnu) "[GNU]")
1041 (t ""))))
1042 (prolog-mode-variables)
1043 (prolog-build-case-strings)
1044 (prolog-set-atom-regexps)
1045 (dolist (ar prolog-align-rules) (add-to-list 'align-rules-list ar))
1046
1047 ;; imenu entry moved to the appropriate hook for consistency
1048
1049 ;; Load SICStus debugger if suitable
1050 (if (and (eq prolog-system 'sicstus)
1051 (prolog-atleast-version '(3 . 7))
1052 prolog-use-sicstus-sd)
1053 (prolog-enable-sicstus-sd))
1054
1055 (prolog-menu))
1056
1057 (defvar mercury-mode-map
1058 (let ((map (make-sparse-keymap)))
1059 (set-keymap-parent map prolog-mode-map)
1060 map))
1061
1062 ;;;###autoload
1063 (define-derived-mode mercury-mode prolog-mode "Prolog[Mercury]"
1064 "Major mode for editing Mercury programs.
1065 Actually this is just customized `prolog-mode'."
1066 (set (make-local-variable 'prolog-system) 'mercury))
1067
1068 \f
1069 ;;-------------------------------------------------------------------
1070 ;; Inferior prolog mode
1071 ;;-------------------------------------------------------------------
1072
1073 (defvar prolog-inferior-mode-map
1074 (let ((map (make-sparse-keymap)))
1075 (prolog-mode-keybindings-common map)
1076 (prolog-mode-keybindings-inferior map)
1077 map))
1078
1079 (defvar prolog-inferior-mode-hook nil
1080 "List of functions to call after the inferior prolog mode has initialised.")
1081
1082 (define-derived-mode prolog-inferior-mode comint-mode "Inferior Prolog"
1083 "Major mode for interacting with an inferior Prolog process.
1084
1085 The following commands are available:
1086 \\{prolog-inferior-mode-map}
1087
1088 Entry to this mode calls the value of `prolog-mode-hook' with no arguments,
1089 if that value is non-nil. Likewise with the value of `comint-mode-hook'.
1090 `prolog-mode-hook' is called after `comint-mode-hook'.
1091
1092 You can send text to the inferior Prolog from other buffers
1093 using the commands `send-region', `send-string' and \\[prolog-consult-region].
1094
1095 Commands:
1096 Tab indents for Prolog; with argument, shifts rest
1097 of expression rigidly with the current line.
1098 Paragraphs are separated only by blank lines and '%%'. '%'s start comments.
1099
1100 Return at end of buffer sends line as input.
1101 Return not at end copies rest of line to end and sends it.
1102 \\[comint-delchar-or-maybe-eof] sends end-of-file as input.
1103 \\[comint-kill-input] and \\[backward-kill-word] are kill commands,
1104 imitating normal Unix input editing.
1105 \\[comint-interrupt-subjob] interrupts the shell or its current subjob if any.
1106 \\[comint-stop-subjob] stops, likewise.
1107 \\[comint-quit-subjob] sends quit signal, likewise.
1108
1109 To find out what version of Prolog mode you are running, enter
1110 `\\[prolog-mode-version]'."
1111 (setq comint-input-filter 'prolog-input-filter)
1112 (setq mode-line-process '(": %s"))
1113 (prolog-mode-variables)
1114 (setq comint-prompt-regexp prolog-prompt-regexp-i)
1115 (set (make-local-variable 'shell-dirstack-query) "pwd.")
1116 (prolog-inferior-menu))
1117
1118 (defun prolog-input-filter (str)
1119 (cond ((string-match "\\`\\s *\\'" str) nil) ;whitespace
1120 ((not (eq major-mode 'prolog-inferior-mode)) t)
1121 ((= (length str) 1) nil) ;one character
1122 ((string-match "\\`[rf] *[0-9]*\\'" str) nil) ;r(edo) or f(ail)
1123 (t t)))
1124
1125 ;;;###autoload
1126 (defun run-prolog (arg)
1127 "Run an inferior Prolog process, input and output via buffer *prolog*.
1128 With prefix argument ARG, restart the Prolog process if running before."
1129 (interactive "P")
1130 (if (and arg (get-process "prolog"))
1131 (progn
1132 (process-send-string "prolog" "halt.\n")
1133 (while (get-process "prolog") (sit-for 0.1))))
1134 (let ((buff (buffer-name)))
1135 (if (not (string= buff "*prolog*"))
1136 (prolog-goto-prolog-process-buffer))
1137 ;; Load SICStus debugger if suitable
1138 (if (and (eq prolog-system 'sicstus)
1139 (prolog-atleast-version '(3 . 7))
1140 prolog-use-sicstus-sd)
1141 (prolog-enable-sicstus-sd))
1142 (prolog-mode-variables)
1143 (prolog-ensure-process)
1144 ))
1145
1146 (defun prolog-ensure-process (&optional wait)
1147 "If Prolog process is not running, run it.
1148 If the optional argument WAIT is non-nil, wait for Prolog prompt specified by
1149 the variable `prolog-prompt-regexp'."
1150 (if (null prolog-program-name-i)
1151 (error "This Prolog system has defined no interpreter."))
1152 (if (comint-check-proc "*prolog*")
1153 ()
1154 (apply 'make-comint "prolog" prolog-program-name-i nil
1155 prolog-program-switches-i)
1156 (with-current-buffer "*prolog*"
1157 (prolog-inferior-mode)
1158 (if wait
1159 (progn
1160 (goto-char (point-max))
1161 (while
1162 (save-excursion
1163 (not
1164 (re-search-backward
1165 (concat "\\(" prolog-prompt-regexp-i "\\)" "\\=")
1166 nil t)))
1167 (sit-for 0.1)))))))
1168
1169 (defun prolog-process-insert-string (process string)
1170 "Insert STRING into inferior Prolog buffer running PROCESS."
1171 ;; Copied from elisp manual, greek to me
1172 (with-current-buffer (process-buffer process)
1173 ;; FIXME: Use window-point-insertion-type instead.
1174 (let ((moving (= (point) (process-mark process))))
1175 (save-excursion
1176 ;; Insert the text, moving the process-marker.
1177 (goto-char (process-mark process))
1178 (insert string)
1179 (set-marker (process-mark process) (point)))
1180 (if moving (goto-char (process-mark process))))))
1181 \f
1182 ;;------------------------------------------------------------
1183 ;; Old consulting and compiling functions
1184 ;;------------------------------------------------------------
1185
1186 (defun prolog-old-process-region (compilep start end)
1187 "Process the region limited by START and END positions.
1188 If COMPILEP is non-nil then use compilation, otherwise consulting."
1189 (prolog-ensure-process)
1190 ;(let ((tmpfile prolog-temp-filename)
1191 (let ((tmpfile (prolog-bsts (prolog-temporary-file)))
1192 ;(process (get-process "prolog"))
1193 (first-line (1+ (count-lines
1194 (point-min)
1195 (save-excursion
1196 (goto-char start)
1197 (point))))))
1198 (write-region start end tmpfile)
1199 (process-send-string
1200 "prolog" (prolog-build-prolog-command
1201 compilep tmpfile (prolog-bsts buffer-file-name)
1202 first-line))
1203 (prolog-goto-prolog-process-buffer)))
1204
1205 (defun prolog-old-process-predicate (compilep)
1206 "Process the predicate around point.
1207 If COMPILEP is non-nil then use compilation, otherwise consulting."
1208 (prolog-old-process-region
1209 compilep (prolog-pred-start) (prolog-pred-end)))
1210
1211 (defun prolog-old-process-buffer (compilep)
1212 "Process the entire buffer.
1213 If COMPILEP is non-nil then use compilation, otherwise consulting."
1214 (prolog-old-process-region compilep (point-min) (point-max)))
1215
1216 (defun prolog-old-process-file (compilep)
1217 "Process the file of the current buffer.
1218 If COMPILEP is non-nil then use compilation, otherwise consulting."
1219 (save-some-buffers)
1220 (prolog-ensure-process)
1221 (let ((filename (prolog-bsts buffer-file-name)))
1222 (process-send-string
1223 "prolog" (prolog-build-prolog-command
1224 compilep filename filename))
1225 (prolog-goto-prolog-process-buffer)))
1226
1227 \f
1228 ;;------------------------------------------------------------
1229 ;; Consulting and compiling
1230 ;;------------------------------------------------------------
1231
1232 ;;; Interactive interface functions, used by both the standard
1233 ;;; and the experimental consultation and compilation functions
1234 (defun prolog-consult-file ()
1235 "Consult file of current buffer."
1236 (interactive)
1237 (if prolog-use-standard-consult-compile-method-flag
1238 (prolog-old-process-file nil)
1239 (prolog-consult-compile-file nil)))
1240
1241 (defun prolog-consult-buffer ()
1242 "Consult buffer."
1243 (interactive)
1244 (if prolog-use-standard-consult-compile-method-flag
1245 (prolog-old-process-buffer nil)
1246 (prolog-consult-compile-buffer nil)))
1247
1248 (defun prolog-consult-region (beg end)
1249 "Consult region between BEG and END."
1250 (interactive "r")
1251 (if prolog-use-standard-consult-compile-method-flag
1252 (prolog-old-process-region nil beg end)
1253 (prolog-consult-compile-region nil beg end)))
1254
1255 (defun prolog-consult-predicate ()
1256 "Consult the predicate around current point."
1257 (interactive)
1258 (if prolog-use-standard-consult-compile-method-flag
1259 (prolog-old-process-predicate nil)
1260 (prolog-consult-compile-predicate nil)))
1261
1262 (defun prolog-compile-file ()
1263 "Compile file of current buffer."
1264 (interactive)
1265 (if prolog-use-standard-consult-compile-method-flag
1266 (prolog-old-process-file t)
1267 (prolog-consult-compile-file t)))
1268
1269 (defun prolog-compile-buffer ()
1270 "Compile buffer."
1271 (interactive)
1272 (if prolog-use-standard-consult-compile-method-flag
1273 (prolog-old-process-buffer t)
1274 (prolog-consult-compile-buffer t)))
1275
1276 (defun prolog-compile-region (beg end)
1277 "Compile region between BEG and END."
1278 (interactive "r")
1279 (if prolog-use-standard-consult-compile-method-flag
1280 (prolog-old-process-region t beg end)
1281 (prolog-consult-compile-region t beg end)))
1282
1283 (defun prolog-compile-predicate ()
1284 "Compile the predicate around current point."
1285 (interactive)
1286 (if prolog-use-standard-consult-compile-method-flag
1287 (prolog-old-process-predicate t)
1288 (prolog-consult-compile-predicate t)))
1289
1290 (defun prolog-buffer-module ()
1291 "Select Prolog module name appropriate for current buffer.
1292 Bases decision on buffer contents (-*- line)."
1293 ;; Look for -*- ... module: MODULENAME; ... -*-
1294 (let (beg end)
1295 (save-excursion
1296 (goto-char (point-min))
1297 (skip-chars-forward " \t")
1298 (and (search-forward "-*-" (line-end-position) t)
1299 (progn
1300 (skip-chars-forward " \t")
1301 (setq beg (point))
1302 (search-forward "-*-" (line-end-position) t))
1303 (progn
1304 (forward-char -3)
1305 (skip-chars-backward " \t")
1306 (setq end (point))
1307 (goto-char beg)
1308 (and (let ((case-fold-search t))
1309 (search-forward "module:" end t))
1310 (progn
1311 (skip-chars-forward " \t")
1312 (setq beg (point))
1313 (if (search-forward ";" end t)
1314 (forward-char -1)
1315 (goto-char end))
1316 (skip-chars-backward " \t")
1317 (buffer-substring beg (point)))))))))
1318
1319 (defun prolog-build-prolog-command (compilep file buffername
1320 &optional first-line)
1321 "Make Prolog command for FILE compilation/consulting.
1322 If COMPILEP is non-nil, consider compilation, otherwise consulting."
1323 (let* ((compile-string
1324 (if compilep prolog-compile-string-i prolog-consult-string-i))
1325 (module (prolog-buffer-module))
1326 (file-name (concat "'" file "'"))
1327 (module-name (if module (concat "'" module "'")))
1328 (module-file (if module
1329 (concat module-name ":" file-name)
1330 file-name))
1331 strbeg strend
1332 (lineoffset (if first-line
1333 (- first-line 1)
1334 0)))
1335
1336 ;; Assure that there is a buffer name
1337 (if (not buffername)
1338 (error "The buffer is not saved"))
1339
1340 (if (not (string-match "\\`'.*'\\'" buffername)) ; Add quotes
1341 (setq buffername (concat "'" buffername "'")))
1342 (while (string-match "%m" compile-string)
1343 (setq strbeg (substring compile-string 0 (match-beginning 0)))
1344 (setq strend (substring compile-string (match-end 0)))
1345 (setq compile-string (concat strbeg module-file strend)))
1346 ;; FIXME: The code below will %-expand any %[fbl] that appears in
1347 ;; module-file.
1348 (while (string-match "%f" compile-string)
1349 (setq strbeg (substring compile-string 0 (match-beginning 0)))
1350 (setq strend (substring compile-string (match-end 0)))
1351 (setq compile-string (concat strbeg file-name strend)))
1352 (while (string-match "%b" compile-string)
1353 (setq strbeg (substring compile-string 0 (match-beginning 0)))
1354 (setq strend (substring compile-string (match-end 0)))
1355 (setq compile-string (concat strbeg buffername strend)))
1356 (while (string-match "%l" compile-string)
1357 (setq strbeg (substring compile-string 0 (match-beginning 0)))
1358 (setq strend (substring compile-string (match-end 0)))
1359 (setq compile-string (concat strbeg (format "%d" lineoffset) strend)))
1360 (concat compile-string "\n")))
1361
1362 ;;; The rest of this page is experimental code!
1363
1364 ;; Global variables for process filter function
1365 (defvar prolog-process-flag nil
1366 "Non-nil means that a prolog task (i.e. a consultation or compilation job)
1367 is running.")
1368 (defvar prolog-consult-compile-output ""
1369 "Hold the unprocessed output from the current prolog task.")
1370 (defvar prolog-consult-compile-first-line 1
1371 "The number of the first line of the file to consult/compile.
1372 Used for temporary files.")
1373 (defvar prolog-consult-compile-file nil
1374 "The file to compile/consult (can be a temporary file).")
1375 (defvar prolog-consult-compile-real-file nil
1376 "The file name of the buffer to compile/consult.")
1377
1378 (defun prolog-consult-compile (compilep file &optional first-line)
1379 "Consult/compile FILE.
1380 If COMPILEP is non-nil, perform compilation, otherwise perform CONSULTING.
1381 COMMAND is a string described by the variables `prolog-consult-string'
1382 and `prolog-compile-string'.
1383 Optional argument FIRST-LINE is the number of the first line in the compiled
1384 region.
1385
1386 This function must be called from the source code buffer."
1387 (if prolog-process-flag
1388 (error "Another Prolog task is running."))
1389 (prolog-ensure-process t)
1390 (let* ((buffer (get-buffer-create prolog-compilation-buffer))
1391 (real-file buffer-file-name)
1392 (command-string (prolog-build-prolog-command compilep file
1393 real-file first-line))
1394 (process (get-process "prolog"))
1395 (old-filter (process-filter process)))
1396 (with-current-buffer buffer
1397 (delete-region (point-min) (point-max))
1398 (compilation-mode)
1399 ;; Setting up font-locking for this buffer
1400 (set (make-local-variable 'font-lock-defaults)
1401 '(prolog-font-lock-keywords nil nil ((?_ . "w"))))
1402 (if (eq prolog-system 'sicstus)
1403 (progn
1404 (set (make-local-variable 'compilation-parse-errors-function)
1405 'prolog-parse-sicstus-compilation-errors)))
1406 (toggle-read-only 0)
1407 (insert command-string "\n"))
1408 (save-selected-window
1409 (pop-to-buffer buffer))
1410 (setq prolog-process-flag t
1411 prolog-consult-compile-output ""
1412 prolog-consult-compile-first-line (if first-line (1- first-line) 0)
1413 prolog-consult-compile-file file
1414 prolog-consult-compile-real-file (if (string=
1415 file buffer-file-name)
1416 nil
1417 real-file))
1418 (with-current-buffer buffer
1419 (goto-char (point-max))
1420 (set-process-filter process 'prolog-consult-compile-filter)
1421 (process-send-string "prolog" command-string)
1422 ;; (prolog-build-prolog-command compilep file real-file first-line))
1423 (while (and prolog-process-flag
1424 (accept-process-output process 10)) ; 10 secs is ok?
1425 (sit-for 0.1)
1426 (unless (get-process "prolog")
1427 (setq prolog-process-flag nil)))
1428 (insert (if compilep
1429 "\nCompilation finished.\n"
1430 "\nConsulted.\n"))
1431 (set-process-filter process old-filter))))
1432
1433 (defun prolog-parse-sicstus-compilation-errors (limit)
1434 "Parse the prolog compilation buffer for errors.
1435 Argument LIMIT is a buffer position limiting searching.
1436 For use with the `compilation-parse-errors-function' variable."
1437 (setq compilation-error-list nil)
1438 (message "Parsing SICStus error messages...")
1439 (let (filepath dir file errorline)
1440 (while
1441 (re-search-backward
1442 "{\\([a-zA-Z ]* ERROR\\|Warning\\):.* in line[s ]*\\([0-9]+\\)"
1443 limit t)
1444 (setq errorline (string-to-number (match-string 2)))
1445 (save-excursion
1446 (re-search-backward
1447 "{\\(consulting\\|compiling\\|processing\\) \\(.*\\)\\.\\.\\.}"
1448 limit t)
1449 (setq filepath (match-string 2)))
1450
1451 ;; ###### Does this work with SICStus under Windows (i.e. backslahes and stuff?)
1452 (if (string-match "\\(.*/\\)\\([^/]*\\)$" filepath)
1453 (progn
1454 (setq dir (match-string 1 filepath))
1455 (setq file (match-string 2 filepath))))
1456
1457 (setq compilation-error-list
1458 (cons
1459 (cons (save-excursion
1460 (beginning-of-line)
1461 (point-marker))
1462 (list (list file dir) errorline))
1463 compilation-error-list)
1464 ))
1465 ))
1466
1467 (defun prolog-consult-compile-filter (process output)
1468 "Filter function for Prolog compilation PROCESS.
1469 Argument OUTPUT is a name of the output file."
1470 ;;(message "start")
1471 (setq prolog-consult-compile-output
1472 (concat prolog-consult-compile-output output))
1473 ;;(message "pccf1: %s" prolog-consult-compile-output)
1474 ;; Iterate through the lines of prolog-consult-compile-output
1475 (let (outputtype)
1476 (while (and prolog-process-flag
1477 (or
1478 ;; Trace question
1479 (progn
1480 (setq outputtype 'trace)
1481 (and (eq prolog-system 'sicstus)
1482 (string-match
1483 "^[ \t]*[0-9]+[ \t]*[0-9]+[ \t]*Call:.*? "
1484 prolog-consult-compile-output)))
1485
1486 ;; Match anything
1487 (progn
1488 (setq outputtype 'normal)
1489 (string-match "^.*\n" prolog-consult-compile-output))
1490 ))
1491 ;;(message "outputtype: %s" outputtype)
1492
1493 (setq output (match-string 0 prolog-consult-compile-output))
1494 ;; remove the text in output from prolog-consult-compile-output
1495 (setq prolog-consult-compile-output
1496 (substring prolog-consult-compile-output (length output)))
1497 ;;(message "pccf2: %s" prolog-consult-compile-output)
1498
1499 ;; If temporary files were used, then we change the error
1500 ;; messages to point to the original source file.
1501 (cond
1502
1503 ;; If the prolog process was in trace mode then it requires
1504 ;; user input
1505 ((and (eq prolog-system 'sicstus)
1506 (eq outputtype 'trace))
1507 (let ((input (concat (read-string output) "\n")))
1508 (process-send-string process input)
1509 (setq output (concat output input))))
1510
1511 ((eq prolog-system 'sicstus)
1512 (if (and prolog-consult-compile-real-file
1513 (string-match
1514 "\\({.*:.* in line[s ]*\\)\\([0-9]+\\)-\\([0-9]+\\)" output))
1515 (setq output (replace-match
1516 ;; Adds a {processing ...} line so that
1517 ;; `prolog-parse-sicstus-compilation-errors'
1518 ;; finds the real file instead of the temporary one.
1519 ;; Also fixes the line numbers.
1520 (format "Added by Emacs: {processing %s...}\n%s%d-%d"
1521 prolog-consult-compile-real-file
1522 (match-string 1 output)
1523 (+ prolog-consult-compile-first-line
1524 (string-to-number
1525 (match-string 2 output)))
1526 (+ prolog-consult-compile-first-line
1527 (string-to-number
1528 (match-string 3 output))))
1529 t t output)))
1530 )
1531
1532 ((eq prolog-system 'swi)
1533 (if (and prolog-consult-compile-real-file
1534 (string-match (format
1535 "%s\\([ \t]*:[ \t]*\\)\\([0-9]+\\)"
1536 prolog-consult-compile-file)
1537 output))
1538 (setq output (replace-match
1539 ;; Real filename + text + fixed linenum
1540 (format "%s%s%d"
1541 prolog-consult-compile-real-file
1542 (match-string 1 output)
1543 (+ prolog-consult-compile-first-line
1544 (string-to-number
1545 (match-string 2 output))))
1546 t t output)))
1547 )
1548
1549 (t ())
1550 )
1551 ;; Write the output in the *prolog-compilation* buffer
1552 (insert output)))
1553
1554 ;; If the prompt is visible, then the task is finished
1555 (if (string-match prolog-prompt-regexp-i prolog-consult-compile-output)
1556 (setq prolog-process-flag nil)))
1557
1558 (defun prolog-consult-compile-file (compilep)
1559 "Consult/compile file of current buffer.
1560 If COMPILEP is non-nil, compile, otherwise consult."
1561 (let ((file buffer-file-name))
1562 (if file
1563 (progn
1564 (save-some-buffers)
1565 (prolog-consult-compile compilep file))
1566 (prolog-consult-compile-region compilep (point-min) (point-max)))))
1567
1568 (defun prolog-consult-compile-buffer (compilep)
1569 "Consult/compile current buffer.
1570 If COMPILEP is non-nil, compile, otherwise consult."
1571 (prolog-consult-compile-region compilep (point-min) (point-max)))
1572
1573 (defun prolog-consult-compile-region (compilep beg end)
1574 "Consult/compile region between BEG and END.
1575 If COMPILEP is non-nil, compile, otherwise consult."
1576 ;(let ((file prolog-temp-filename)
1577 (let ((file (prolog-bsts (prolog-temporary-file)))
1578 (lines (count-lines 1 beg)))
1579 (write-region beg end file nil 'no-message)
1580 (write-region "\n" nil file t 'no-message)
1581 (prolog-consult-compile compilep file
1582 (if (looking-at "^") (1+ lines) lines))
1583 (delete-file file)))
1584
1585 (defun prolog-consult-compile-predicate (compilep)
1586 "Consult/compile the predicate around current point.
1587 If COMPILEP is non-nil, compile, otherwise consult."
1588 (prolog-consult-compile-region
1589 compilep (prolog-pred-start) (prolog-pred-end)))
1590
1591 \f
1592 ;;-------------------------------------------------------------------
1593 ;; Font-lock stuff
1594 ;;-------------------------------------------------------------------
1595
1596 ;; Auxilliary functions
1597 (defun prolog-make-keywords-regexp (keywords &optional protect)
1598 "Create regexp from the list of strings KEYWORDS.
1599 If PROTECT is non-nil, surround the result regexp by word breaks."
1600 (let ((regexp
1601 (if (fboundp 'regexp-opt)
1602 ;; Emacs 20
1603 ;; Avoid compile warnings under earlier versions by using eval
1604 (eval '(regexp-opt keywords))
1605 ;; Older Emacsen
1606 (concat (mapconcat 'regexp-quote keywords "\\|")))
1607 ))
1608 (if protect
1609 (concat "\\<\\(" regexp "\\)\\>")
1610 regexp)))
1611
1612 (defun prolog-font-lock-object-matcher (bound)
1613 "Find SICStus objects method name for font lock.
1614 Argument BOUND is a buffer position limiting searching."
1615 (let (point
1616 (case-fold-search nil))
1617 (while (and (not point)
1618 (re-search-forward "\\(::[ \t\n]*{\\|&\\)[ \t]*"
1619 bound t))
1620 (while (or (re-search-forward "\\=\n[ \t]*" bound t)
1621 (re-search-forward "\\=%.*" bound t)
1622 (and (re-search-forward "\\=/\\*" bound t)
1623 (re-search-forward "\\*/[ \t]*" bound t))))
1624 (setq point (re-search-forward
1625 (format "\\=\\(%s\\)" prolog-atom-regexp)
1626 bound t)))
1627 point))
1628
1629 (defsubst prolog-face-name-p (facename)
1630 ;; Return t if FACENAME is the name of a face. This method is
1631 ;; necessary since facep in XEmacs only returns t for the actual
1632 ;; face objects (while it's only their names that are used just
1633 ;; about anywhere else) without providing a predicate that tests
1634 ;; face names. This function (including the above commentary) is
1635 ;; borrowed from cc-mode.
1636 (memq facename (face-list)))
1637
1638 ;; Set everything up
1639 (defun prolog-font-lock-keywords ()
1640 "Set up font lock keywords for the current Prolog system."
1641 ;(when window-system
1642 (require 'font-lock)
1643
1644 ;; Define Prolog faces
1645 (defface prolog-redo-face
1646 '((((class grayscale)) (:italic t))
1647 (((class color)) (:foreground "darkorchid"))
1648 (t (:italic t)))
1649 "Prolog mode face for highlighting redo trace lines."
1650 :group 'prolog-faces)
1651 (defface prolog-exit-face
1652 '((((class grayscale)) (:underline t))
1653 (((class color) (background dark)) (:foreground "green"))
1654 (((class color) (background light)) (:foreground "ForestGreen"))
1655 (t (:underline t)))
1656 "Prolog mode face for highlighting exit trace lines."
1657 :group 'prolog-faces)
1658 (defface prolog-exception-face
1659 '((((class grayscale)) (:bold t :italic t :underline t))
1660 (((class color)) (:bold t :foreground "black" :background "Khaki"))
1661 (t (:bold t :italic t :underline t)))
1662 "Prolog mode face for highlighting exception trace lines."
1663 :group 'prolog-faces)
1664 (defface prolog-warning-face
1665 '((((class grayscale)) (:underline t))
1666 (((class color) (background dark)) (:foreground "blue"))
1667 (((class color) (background light)) (:foreground "MidnightBlue"))
1668 (t (:underline t)))
1669 "Face name to use for compiler warnings."
1670 :group 'prolog-faces)
1671 (defface prolog-builtin-face
1672 '((((class color) (background light)) (:foreground "Purple"))
1673 (((class color) (background dark)) (:foreground "Cyan"))
1674 (((class grayscale) (background light)) (:foreground "LightGray" :bold t))
1675 (((class grayscale) (background dark)) (:foreground "DimGray" :bold t))
1676 (t (:bold t)))
1677 "Face name to use for compiler warnings."
1678 :group 'prolog-faces)
1679 (defvar prolog-warning-face
1680 (if (prolog-face-name-p 'font-lock-warning-face)
1681 'font-lock-warning-face
1682 'prolog-warning-face)
1683 "Face name to use for built in predicates.")
1684 (defvar prolog-builtin-face
1685 (if (prolog-face-name-p 'font-lock-builtin-face)
1686 'font-lock-builtin-face
1687 'prolog-builtin-face)
1688 "Face name to use for built in predicates.")
1689 (defvar prolog-redo-face 'prolog-redo-face
1690 "Face name to use for redo trace lines.")
1691 (defvar prolog-exit-face 'prolog-exit-face
1692 "Face name to use for exit trace lines.")
1693 (defvar prolog-exception-face 'prolog-exception-face
1694 "Face name to use for exception trace lines.")
1695
1696 ;; Font Lock Patterns
1697 (let (
1698 ;; "Native" Prolog patterns
1699 (head-predicates
1700 (list (format "^\\(%s\\)\\((\\|[ \t]*:-\\)" prolog-atom-regexp)
1701 1 font-lock-function-name-face))
1702 ;(list (format "^%s" prolog-atom-regexp)
1703 ; 0 font-lock-function-name-face))
1704 (head-predicates-1
1705 (list (format "\\.[ \t]*\\(%s\\)" prolog-atom-regexp)
1706 1 font-lock-function-name-face) )
1707 (variables
1708 '("\\<\\([_A-Z][a-zA-Z0-9_]*\\)"
1709 1 font-lock-variable-name-face))
1710 (important-elements
1711 (list (if (eq prolog-system 'mercury)
1712 "[][}{;|]\\|\\\\[+=]\\|<?=>?"
1713 "[][}{!;|]\\|\\*->")
1714 0 'font-lock-keyword-face))
1715 (important-elements-1
1716 '("[^-*]\\(->\\)" 1 font-lock-keyword-face))
1717 (predspecs ; module:predicate/cardinality
1718 (list (format "\\<\\(%s:\\|\\)%s/[0-9]+"
1719 prolog-atom-regexp prolog-atom-regexp)
1720 0 font-lock-function-name-face 'prepend))
1721 (keywords ; directives (queries)
1722 (list
1723 (if (eq prolog-system 'mercury)
1724 (concat
1725 "\\<\\("
1726 (prolog-make-keywords-regexp prolog-keywords-i)
1727 "\\|"
1728 (prolog-make-keywords-regexp
1729 prolog-determinism-specificators-i)
1730 "\\)\\>")
1731 (concat
1732 "^[?:]- *\\("
1733 (prolog-make-keywords-regexp prolog-keywords-i)
1734 "\\)\\>"))
1735 1 prolog-builtin-face))
1736 (quoted_atom (list prolog-quoted-atom-regexp
1737 2 'font-lock-string-face 'append))
1738 (string (list prolog-string-regexp
1739 1 'font-lock-string-face 'append))
1740 ;; SICStus specific patterns
1741 (sicstus-object-methods
1742 (if (eq prolog-system 'sicstus)
1743 '(prolog-font-lock-object-matcher
1744 1 font-lock-function-name-face)))
1745 ;; Mercury specific patterns
1746 (types
1747 (if (eq prolog-system 'mercury)
1748 (list
1749 (prolog-make-keywords-regexp prolog-types-i t)
1750 0 'font-lock-type-face)))
1751 (modes
1752 (if (eq prolog-system 'mercury)
1753 (list
1754 (prolog-make-keywords-regexp prolog-mode-specificators-i t)
1755 0 'font-lock-reference-face)))
1756 (directives
1757 (if (eq prolog-system 'mercury)
1758 (list
1759 (prolog-make-keywords-regexp prolog-directives-i t)
1760 0 'prolog-warning-face)))
1761 ;; Inferior mode specific patterns
1762 (prompt
1763 (list prolog-prompt-regexp-i 0 'font-lock-keyword-face))
1764 (trace-exit
1765 (cond
1766 ((eq prolog-system 'sicstus)
1767 '("[ \t]*[0-9]+[ \t]+[0-9]+[ \t]*\\(Exit\\):"
1768 1 prolog-exit-face))
1769 ((eq prolog-system 'swi)
1770 '("[ \t]*\\(Exit\\):[ \t]*([ \t0-9]*)" 1 prolog-exit-face))
1771 (t nil)))
1772 (trace-fail
1773 (cond
1774 ((eq prolog-system 'sicstus)
1775 '("[ \t]*[0-9]+[ \t]+[0-9]+[ \t]*\\(Fail\\):"
1776 1 prolog-warning-face))
1777 ((eq prolog-system 'swi)
1778 '("[ \t]*\\(Fail\\):[ \t]*([ \t0-9]*)" 1 prolog-warning-face))
1779 (t nil)))
1780 (trace-redo
1781 (cond
1782 ((eq prolog-system 'sicstus)
1783 '("[ \t]*[0-9]+[ \t]+[0-9]+[ \t]*\\(Redo\\):"
1784 1 prolog-redo-face))
1785 ((eq prolog-system 'swi)
1786 '("[ \t]*\\(Redo\\):[ \t]*([ \t0-9]*)" 1 prolog-redo-face))
1787 (t nil)))
1788 (trace-call
1789 (cond
1790 ((eq prolog-system 'sicstus)
1791 '("[ \t]*[0-9]+[ \t]+[0-9]+[ \t]*\\(Call\\):"
1792 1 font-lock-function-name-face))
1793 ((eq prolog-system 'swi)
1794 '("[ \t]*\\(Call\\):[ \t]*([ \t0-9]*)"
1795 1 font-lock-function-name-face))
1796 (t nil)))
1797 (trace-exception
1798 (cond
1799 ((eq prolog-system 'sicstus)
1800 '("[ \t]*[0-9]+[ \t]+[0-9]+[ \t]*\\(Exception\\):"
1801 1 prolog-exception-face))
1802 ((eq prolog-system 'swi)
1803 '("[ \t]*\\(Exception\\):[ \t]*([ \t0-9]*)"
1804 1 prolog-exception-face))
1805 (t nil)))
1806 (error-message-identifier
1807 (cond
1808 ((eq prolog-system 'sicstus)
1809 '("{\\([A-Z]* ?ERROR:\\)" 1 prolog-exception-face prepend))
1810 ((eq prolog-system 'swi)
1811 '("^[[]\\(WARNING:\\)" 1 prolog-builtin-face prepend))
1812 (t nil)))
1813 (error-whole-messages
1814 (cond
1815 ((eq prolog-system 'sicstus)
1816 '("{\\([A-Z]* ?ERROR:.*\\)}[ \t]*$"
1817 1 font-lock-comment-face append))
1818 ((eq prolog-system 'swi)
1819 '("^[[]WARNING:[^]]*[]]$" 0 font-lock-comment-face append))
1820 (t nil)))
1821 (error-warning-messages
1822 ;; Mostly errors that SICStus asks the user about how to solve,
1823 ;; such as "NAME CLASH:" for example.
1824 (cond
1825 ((eq prolog-system 'sicstus)
1826 '("^[A-Z ]*[A-Z]+:" 0 prolog-warning-face))
1827 (t nil)))
1828 (warning-messages
1829 (cond
1830 ((eq prolog-system 'sicstus)
1831 '("\\({ ?\\(Warning\\|WARNING\\) ?:.*}\\)[ \t]*$"
1832 2 prolog-warning-face prepend))
1833 (t nil))))
1834
1835 ;; Make font lock list
1836 (delq
1837 nil
1838 (cond
1839 ((eq major-mode 'prolog-mode)
1840 (list
1841 head-predicates
1842 head-predicates-1
1843 quoted_atom
1844 string
1845 variables
1846 important-elements
1847 important-elements-1
1848 predspecs
1849 keywords
1850 sicstus-object-methods
1851 types
1852 modes
1853 directives))
1854 ((eq major-mode 'prolog-inferior-mode)
1855 (list
1856 prompt
1857 error-message-identifier
1858 error-whole-messages
1859 error-warning-messages
1860 warning-messages
1861 predspecs
1862 trace-exit
1863 trace-fail
1864 trace-redo
1865 trace-call
1866 trace-exception))
1867 ((eq major-mode 'compilation-mode)
1868 (list
1869 error-message-identifier
1870 error-whole-messages
1871 error-warning-messages
1872 warning-messages
1873 predspecs))))
1874 ))
1875
1876 \f
1877 ;;-------------------------------------------------------------------
1878 ;; Indentation stuff
1879 ;;-------------------------------------------------------------------
1880
1881 ;; NB: This function *MUST* have this optional argument since XEmacs
1882 ;; assumes it. This does not mean we have to use it...
1883 (defun prolog-indent-line (&optional whole-exp)
1884 "Indent current line as Prolog code.
1885 With argument, indent any additional lines of the same clause
1886 rigidly along with this one (not yet)."
1887 (interactive "p")
1888 (let ((indent (prolog-indent-level))
1889 (pos (- (point-max) (point))) beg)
1890 (beginning-of-line)
1891 (setq beg (point))
1892 (skip-chars-forward " \t")
1893 (indent-line-to indent)
1894 (if (> (- (point-max) pos) (point))
1895 (goto-char (- (point-max) pos)))
1896
1897 ;; Align comments
1898 (if (and prolog-align-comments-flag
1899 (save-excursion
1900 (line-beginning-position)
1901 ;; (let ((start (comment-search-forward (line-end-position) t)))
1902 ;; (and start ;There's a comment to indent.
1903 ;; ;; If it's first on the line, we've indented it already
1904 ;; ;; and prolog-goto-comment-column would inf-loop.
1905 ;; (progn (goto-char start) (skip-chars-backward " \t")
1906 ;; (not (bolp)))))))
1907 (and (looking-at comment-start-skip)
1908 ;; The definition of comment-start-skip used in this
1909 ;; mode is unusual in that it only matches at BOL.
1910 (progn (skip-chars-forward " \t")
1911 (not (eq (point) (match-end 1)))))))
1912 (save-excursion
1913 (prolog-goto-comment-column t)))
1914
1915 ;; Insert spaces if needed
1916 (if (or prolog-electric-tab-flag prolog-electric-if-then-else-flag)
1917 (prolog-insert-spaces-after-paren))
1918 ))
1919
1920 (defun prolog-comment-indent ()
1921 "Compute prolog comment indentation."
1922 ;; FIXME: Only difference with default behavior is that %%% is not
1923 ;; flushed to column 0 but just left where the user put it.
1924 (cond ((looking-at "%%%") (prolog-indentation-level-of-line))
1925 ((looking-at "%%") (prolog-indent-level))
1926 (t
1927 (save-excursion
1928 (skip-chars-backward " \t")
1929 ;; Insert one space at least, except at left margin.
1930 (max (+ (current-column) (if (bolp) 0 1))
1931 comment-column)))
1932 ))
1933
1934 (defun prolog-indent-level ()
1935 "Compute prolog indentation level."
1936 (save-excursion
1937 (beginning-of-line)
1938 (let ((totbal (prolog-region-paren-balance
1939 (prolog-clause-start t) (point)))
1940 (oldpoint (point)))
1941 (skip-chars-forward " \t")
1942 (cond
1943 ((looking-at "%%%") (prolog-indentation-level-of-line))
1944 ;Large comment starts
1945 ((looking-at "%[^%]") comment-column) ;Small comment starts
1946 ((bobp) 0) ;Beginning of buffer
1947
1948 ;; If we found '}' then we must check if it's the
1949 ;; end of an object declaration or something else.
1950 ((and (looking-at "}")
1951 (save-excursion
1952 (forward-char 1)
1953 ;; Goto to matching {
1954 (if prolog-use-prolog-tokenizer-flag
1955 (prolog-backward-list)
1956 (backward-list))
1957 (skip-chars-backward " \t")
1958 (backward-char 2)
1959 (looking-at "::")))
1960 ;; It was an object
1961 (if prolog-object-end-to-0-flag
1962 0
1963 prolog-indent-width))
1964
1965 ;;End of /* */ comment
1966 ((looking-at "\\*/")
1967 (save-excursion
1968 (prolog-find-start-of-mline-comment)
1969 (skip-chars-backward " \t")
1970 (- (current-column) 2)))
1971
1972 ;; Here we check if the current line is within a /* */ pair
1973 ((and (looking-at "[^%/]")
1974 (eq (prolog-in-string-or-comment) 'cmt))
1975 (if prolog-indent-mline-comments-flag
1976 (prolog-find-start-of-mline-comment)
1977 ;; Same as before
1978 (prolog-indentation-level-of-line)))
1979
1980 (t
1981 (let ((empty t) ind linebal)
1982 ;; See previous indentation
1983 (while empty
1984 (forward-line -1)
1985 (beginning-of-line)
1986 (if (bobp)
1987 (setq empty nil)
1988 (skip-chars-forward " \t")
1989 (if (not (or (not (member (prolog-in-string-or-comment)
1990 '(nil txt)))
1991 (looking-at "%")
1992 (looking-at "\n")))
1993 (setq empty nil))))
1994
1995 ;; Store this line's indentation
1996 (setq ind (if (bobp)
1997 0 ;Beginning of buffer.
1998 (current-column))) ;Beginning of clause.
1999
2000 ;; Compute the balance of the line
2001 (setq linebal (prolog-paren-balance))
2002 ;;(message "bal of previous line %d totbal %d" linebal totbal)
2003 (if (< linebal 0)
2004 (progn
2005 ;; Add 'indent-level' mode to find-unmatched-paren instead?
2006 (end-of-line)
2007 (setq ind (prolog-find-indent-of-matching-paren))))
2008
2009 ;;(message "ind %d" ind)
2010 (beginning-of-line)
2011
2012 ;; Check if the line ends with ":-", ".", ":: {", "}" (might be
2013 ;; unnecessary), "&" or ")" (The last four concerns SICStus objects)
2014 (cond
2015 ;; If the last char of the line is a '&' then set the indent level
2016 ;; to prolog-indent-width (used in SICStus objects)
2017 ((and (eq prolog-system 'sicstus)
2018 (looking-at ".+&[ \t]*\\(%.*\\|\\)$"))
2019 (setq ind prolog-indent-width))
2020
2021 ;; Increase indentation if the previous line was the head of a rule
2022 ;; and does not contain a '.'
2023 ((and (looking-at (format ".*%s[^\\.]*[ \t]*\\(%%.*\\|\\)$"
2024 prolog-head-delimiter))
2025 ;; We must check that the match is at a paren balance of 0.
2026 (save-excursion
2027 (let ((p (point)))
2028 (re-search-forward prolog-head-delimiter)
2029 (>= 0 (prolog-region-paren-balance p (point))))))
2030 (let ((headindent
2031 (if (< (prolog-paren-balance) 0)
2032 (save-excursion
2033 (end-of-line)
2034 (prolog-find-indent-of-matching-paren))
2035 (prolog-indentation-level-of-line))))
2036 (setq ind (+ headindent prolog-indent-width))))
2037
2038 ;; The previous line was the head of an object
2039 ((looking-at ".+ *::.*{[ \t]*$")
2040 (setq ind prolog-indent-width))
2041
2042 ;; If a '.' is found at the end of the previous line, then
2043 ;; decrease the indentation. (The \\(%.*\\|\\) part of the
2044 ;; regexp is for comments at the end of the line)
2045 ((and (looking-at "^.+\\.[ \t]*\\(%.*\\|\\)$")
2046 ;; Make sure that the '.' found is not in a comment or string
2047 (save-excursion
2048 (end-of-line)
2049 (re-search-backward "\\.[ \t]*\\(%.*\\|\\)$" (point-min))
2050 ;; Guard against the real '.' being followed by a
2051 ;; commented '.'.
2052 (if (eq (prolog-in-string-or-comment) 'cmt)
2053 ;; commented out '.'
2054 (let ((here (line-beginning-position)))
2055 (end-of-line)
2056 (re-search-backward "\\.[ \t]*%.*$" here t))
2057 (not (prolog-in-string-or-comment))
2058 )
2059 ))
2060 (setq ind 0))
2061
2062 ;; If a '.' is found at the end of the previous line, then
2063 ;; decrease the indentation. (The /\\*.*\\*/ part of the
2064 ;; regexp is for C-like comments at the end of the
2065 ;; line--can we merge with the case above?).
2066 ((and (looking-at "^.+\\.[ \t]*\\(/\\*.*\\|\\)$")
2067 ;; Make sure that the '.' found is not in a comment or string
2068 (save-excursion
2069 (end-of-line)
2070 (re-search-backward "\\.[ \t]*\\(/\\*.*\\|\\)$" (point-min))
2071 ;; Guard against the real '.' being followed by a
2072 ;; commented '.'.
2073 (if (eq (prolog-in-string-or-comment) 'cmt)
2074 ;; commented out '.'
2075 (let ((here (line-beginning-position)))
2076 (end-of-line)
2077 (re-search-backward "\\.[ \t]*/\\*.*$" here t))
2078 (not (prolog-in-string-or-comment))
2079 )
2080 ))
2081 (setq ind 0))
2082
2083 )
2084
2085 ;; If the last non comment char is a ',' or left paren or a left-
2086 ;; indent-regexp then indent to open parenthesis level
2087 (if (and
2088 (> totbal 0)
2089 ;; SICStus objects have special syntax rules if point is
2090 ;; not inside additional parens (objects are defined
2091 ;; within {...})
2092 (not (and (eq prolog-system 'sicstus)
2093 (= totbal 1)
2094 (prolog-in-object))))
2095 (if (looking-at
2096 (format "\\(%s\\|%s\\|0'.\\|[0-9]+'[0-9a-zA-Z]+\\|[^\n\'\"%%]\\)*\\(,\\|%s\\|%s\\)\[ \t]*\\(%%.*\\|\\)$"
2097 prolog-quoted-atom-regexp prolog-string-regexp
2098 prolog-left-paren prolog-left-indent-regexp))
2099 (progn
2100 (goto-char oldpoint)
2101 (setq ind (prolog-find-unmatched-paren
2102 (if prolog-paren-indent-p
2103 'termdependent
2104 'skipwhite)))
2105 ;;(setq ind (prolog-find-unmatched-paren 'termdependent))
2106 )
2107 (goto-char oldpoint)
2108 (setq ind (prolog-find-unmatched-paren nil))
2109 ))
2110
2111
2112 ;; Return the indentation level
2113 ind
2114 ))))))
2115
2116 (defun prolog-find-indent-of-matching-paren ()
2117 "Find the indentation level based on the matching parenthesis.
2118 Indentation level is set to the one the point is after when the function is
2119 called."
2120 (save-excursion
2121 ;; Go to the matching paren
2122 (if prolog-use-prolog-tokenizer-flag
2123 (prolog-backward-list)
2124 (backward-list))
2125
2126 ;; If this was the first paren on the line then return this line's
2127 ;; indentation level
2128 (if (prolog-paren-is-the-first-on-line-p)
2129 (prolog-indentation-level-of-line)
2130 ;; It was not the first one
2131 (progn
2132 ;; Find the next paren
2133 (prolog-goto-next-paren 0)
2134
2135 ;; If this paren is a left one then use its column as indent level,
2136 ;; if not then recurse this function
2137 (if (looking-at prolog-left-paren)
2138 (+ (current-column) 1)
2139 (progn
2140 (forward-char 1)
2141 (prolog-find-indent-of-matching-paren)))
2142 ))
2143 ))
2144
2145 (defun prolog-indentation-level-of-line ()
2146 "Return the indentation level of the current line."
2147 (save-excursion
2148 (beginning-of-line)
2149 (skip-chars-forward " \t")
2150 (current-column)))
2151
2152 (defun prolog-paren-is-the-first-on-line-p ()
2153 "Return t if the parenthesis under the point is the first one on the line.
2154 Return nil otherwise.
2155 Note: does not check if the point is actually at a parenthesis!"
2156 (save-excursion
2157 (let ((begofline (line-beginning-position)))
2158 (if (= begofline (point))
2159 t
2160 (if (prolog-goto-next-paren begofline)
2161 nil
2162 t)))))
2163
2164 (defun prolog-find-unmatched-paren (&optional mode)
2165 "Return the column of the last unmatched left parenthesis.
2166 If MODE is `skipwhite' then any white space after the parenthesis is added to
2167 the answer.
2168 If MODE is `plusone' then the parenthesis' column +1 is returned.
2169 If MODE is `termdependent' then if the unmatched parenthesis is part of
2170 a compound term the function will work as `skipwhite', otherwise
2171 it will return the column paren plus the value of `prolog-paren-indent'.
2172 If MODE is nil or not set then the parenthesis' exact column is returned."
2173 (save-excursion
2174 ;; If the next paren we find is a left one we're finished, if it's
2175 ;; a right one then we go back one step and recurse
2176 (prolog-goto-next-paren 0)
2177
2178 (let ((roundparen (looking-at "(")))
2179 (if (looking-at prolog-left-paren)
2180 (let ((not-part-of-term
2181 (save-excursion
2182 (backward-char 1)
2183 (looking-at "[ \t]"))))
2184 (if (eq mode nil)
2185 (current-column)
2186 (if (and roundparen
2187 (eq mode 'termdependent)
2188 not-part-of-term)
2189 (+ (current-column)
2190 (if prolog-electric-tab-flag
2191 ;; Electric TAB
2192 prolog-paren-indent
2193 ;; Not electric TAB
2194 (if (looking-at ".[ \t]*$")
2195 2
2196 prolog-paren-indent))
2197 )
2198
2199 (forward-char 1)
2200 (if (or (eq mode 'skipwhite) (eq mode 'termdependent) )
2201 (skip-chars-forward " \t"))
2202 (current-column))))
2203 ;; Not looking at left paren
2204 (progn
2205 (forward-char 1)
2206 ;; Go to the matching paren. When we get there we have a total
2207 ;; balance of 0.
2208 (if prolog-use-prolog-tokenizer-flag
2209 (prolog-backward-list)
2210 (backward-list))
2211 (prolog-find-unmatched-paren mode)))
2212 )))
2213
2214
2215 (defun prolog-paren-balance ()
2216 "Return the parenthesis balance of the current line.
2217 A return value of n means n more left parentheses than right ones."
2218 (save-excursion
2219 (end-of-line)
2220 (prolog-region-paren-balance (line-beginning-position) (point))))
2221
2222 (defun prolog-region-paren-balance (beg end)
2223 "Return the summed parenthesis balance in the region.
2224 The region is limited by BEG and END positions."
2225 (save-excursion
2226 (let ((state (if prolog-use-prolog-tokenizer-flag
2227 (prolog-tokenize beg end)
2228 (parse-partial-sexp beg end))))
2229 (nth 0 state))))
2230
2231 (defun prolog-goto-next-paren (limit-pos)
2232 "Move the point to the next parenthesis earlier in the buffer.
2233 Return t if a match was found before LIMIT-POS. Return nil otherwise."
2234 (let ((retval (re-search-backward
2235 (concat prolog-left-paren "\\|" prolog-right-paren)
2236 limit-pos t)))
2237
2238 ;; If a match was found but it was in a string or comment, then recurse
2239 (if (and retval (prolog-in-string-or-comment))
2240 (prolog-goto-next-paren limit-pos)
2241 retval)
2242 ))
2243
2244 (defun prolog-in-string-or-comment ()
2245 "Check whether string, atom, or comment is under current point.
2246 Return:
2247 `txt' if the point is in a string, atom, or character code expression
2248 `cmt' if the point is in a comment
2249 nil otherwise."
2250 (save-excursion
2251 (let* ((start
2252 (if (eq prolog-parse-mode 'beg-of-line)
2253 ;; 'beg-of-line
2254 (save-excursion
2255 (let (safepoint)
2256 (beginning-of-line)
2257 (setq safepoint (point))
2258 (while (and (> (point) (point-min))
2259 (progn
2260 (forward-line -1)
2261 (end-of-line)
2262 (if (not (bobp))
2263 (backward-char 1))
2264 (looking-at "\\\\"))
2265 )
2266 (beginning-of-line)
2267 (setq safepoint (point)))
2268 safepoint))
2269 ;; 'beg-of-clause
2270 (prolog-clause-start)))
2271 (end (point))
2272 (state (if prolog-use-prolog-tokenizer-flag
2273 (prolog-tokenize start end)
2274 (if (fboundp 'syntax-ppss)
2275 (syntax-ppss)
2276 (parse-partial-sexp start end)))))
2277 (cond
2278 ((nth 3 state) 'txt) ; String
2279 ((nth 4 state) 'cmt) ; Comment
2280 (t
2281 (cond
2282 ((looking-at "%") 'cmt) ; Start of a comment
2283 ((looking-at "/\\*") 'cmt) ; Start of a comment
2284 ((looking-at "\'") 'txt) ; Start of an atom
2285 ((looking-at "\"") 'txt) ; Start of a string
2286 (t nil)
2287 ))))
2288 ))
2289
2290 (defun prolog-find-start-of-mline-comment ()
2291 "Return the start column of a /* */ comment.
2292 This assumes that the point is inside a comment."
2293 (re-search-backward "/\\*" (point-min) t)
2294 (forward-char 2)
2295 (skip-chars-forward " \t")
2296 (current-column))
2297
2298 (defun prolog-insert-spaces-after-paren ()
2299 "Insert spaces after the opening parenthesis, \"then\" (->) and \"else\" (;) branches.
2300 Spaces are inserted if all preceding objects on the line are
2301 whitespace characters, parentheses, or then/else branches."
2302 (save-excursion
2303 (let ((regexp (concat "(\\|" prolog-left-indent-regexp))
2304 level)
2305 (beginning-of-line)
2306 (skip-chars-forward " \t")
2307 (when (looking-at regexp)
2308 ;; Treat "( If -> " lines specially.
2309 ;;(setq incr (if (looking-at "(.*->")
2310 ;; 2
2311 ;; prolog-paren-indent))
2312
2313 ;; work on all subsequent "->", "(", ";"
2314 (while (looking-at regexp)
2315 (goto-char (match-end 0))
2316 (setq level (+ (prolog-find-unmatched-paren) prolog-paren-indent))
2317
2318 ;; Remove old white space
2319 (let ((start (point)))
2320 (skip-chars-forward " \t")
2321 (delete-region start (point)))
2322 (indent-to level)
2323 (skip-chars-forward " \t"))
2324 )))
2325 (when (save-excursion
2326 (backward-char 2)
2327 (looking-at "\\s ;\\|\\s (\\|->")) ; (looking-at "\\s \\((\\|;\\)"))
2328 (skip-chars-forward " \t"))
2329 )
2330
2331 ;;;; Comment filling
2332
2333 (defun prolog-comment-limits ()
2334 "Return the current comment limits plus the comment type (block or line).
2335 The comment limits are the range of a block comment or the range that
2336 contains all adjacent line comments (i.e. all comments that starts in
2337 the same column with no empty lines or non-whitespace characters
2338 between them)."
2339 (let ((here (point))
2340 lit-limits-b lit-limits-e lit-type beg end
2341 )
2342 (save-restriction
2343 ;; Widen to catch comment limits correctly.
2344 (widen)
2345 (setq end (line-end-position)
2346 beg (line-beginning-position))
2347 (save-excursion
2348 (beginning-of-line)
2349 (setq lit-type (if (search-forward-regexp "%" end t) 'line 'block))
2350 ; (setq lit-type 'line)
2351 ;(if (search-forward-regexp "^[ \t]*%" end t)
2352 ; (setq lit-type 'line)
2353 ; (if (not (search-forward-regexp "%" end t))
2354 ; (setq lit-type 'block)
2355 ; (if (not (= (forward-line 1) 0))
2356 ; (setq lit-type 'block)
2357 ; (setq done t
2358 ; ret (prolog-comment-limits)))
2359 ; ))
2360 (if (eq lit-type 'block)
2361 (progn
2362 (goto-char here)
2363 (when (looking-at "/\\*") (forward-char 2))
2364 (when (and (looking-at "\\*") (> (point) (point-min))
2365 (forward-char -1) (looking-at "/"))
2366 (forward-char 1))
2367 (when (save-excursion (search-backward "/*" nil t))
2368 (list (save-excursion (search-backward "/*") (point))
2369 (or (search-forward "*/" nil t) (point-max)) lit-type)))
2370 ;; line comment
2371 (setq lit-limits-b (- (point) 1)
2372 lit-limits-e end)
2373 (condition-case nil
2374 (if (progn (goto-char lit-limits-b)
2375 (looking-at "%"))
2376 (let ((col (current-column)) done)
2377 (setq beg (point)
2378 end lit-limits-e)
2379 ;; Always at the beginning of the comment
2380 ;; Go backward now
2381 (beginning-of-line)
2382 (while (and (zerop (setq done (forward-line -1)))
2383 (search-forward-regexp "^[ \t]*%"
2384 (line-end-position) t)
2385 (= (+ 1 col) (current-column)))
2386 (setq beg (- (point) 1)))
2387 (when (= done 0)
2388 (forward-line 1))
2389 ;; We may have a line with code above...
2390 (when (and (zerop (setq done (forward-line -1)))
2391 (search-forward "%" (line-end-position) t)
2392 (= (+ 1 col) (current-column)))
2393 (setq beg (- (point) 1)))
2394 (when (= done 0)
2395 (forward-line 1))
2396 ;; Go forward
2397 (goto-char lit-limits-b)
2398 (beginning-of-line)
2399 (while (and (zerop (forward-line 1))
2400 (search-forward-regexp "^[ \t]*%"
2401 (line-end-position) t)
2402 (= (+ 1 col) (current-column)))
2403 (setq end (line-end-position)))
2404 (list beg end lit-type))
2405 (list lit-limits-b lit-limits-e lit-type)
2406 )
2407 (error (list lit-limits-b lit-limits-e lit-type))))
2408 ))))
2409
2410 (defun prolog-guess-fill-prefix ()
2411 ;; fill 'txt entities?
2412 (when (save-excursion
2413 (end-of-line)
2414 (equal (prolog-in-string-or-comment) 'cmt))
2415 (let* ((bounds (prolog-comment-limits))
2416 (cbeg (car bounds))
2417 (type (nth 2 bounds))
2418 beg end)
2419 (save-excursion
2420 (end-of-line)
2421 (setq end (point))
2422 (beginning-of-line)
2423 (setq beg (point))
2424 (if (and (eq type 'line)
2425 (> cbeg beg)
2426 (save-excursion (not (search-forward-regexp "^[ \t]*%"
2427 cbeg t))))
2428 (progn
2429 (goto-char cbeg)
2430 (search-forward-regexp "%+[ \t]*" end t)
2431 (prolog-replace-in-string (buffer-substring beg (point))
2432 "[^ \t%]" " "))
2433 ;(goto-char beg)
2434 (if (search-forward-regexp "^[ \t]*\\(%+\\|\\*+\\|/\\*+\\)[ \t]*"
2435 end t)
2436 (prolog-replace-in-string (buffer-substring beg (point)) "/" " ")
2437 (beginning-of-line)
2438 (when (search-forward-regexp "^[ \t]+" end t)
2439 (buffer-substring beg (point)))))))))
2440
2441 (defun prolog-fill-paragraph ()
2442 "Fill paragraph comment at or after point."
2443 (interactive)
2444 (let* ((bounds (prolog-comment-limits))
2445 (type (nth 2 bounds)))
2446 (if (eq type 'line)
2447 (let ((fill-prefix (prolog-guess-fill-prefix)))
2448 (fill-paragraph nil))
2449 (save-excursion
2450 (save-restriction
2451 ;; exclude surrounding lines that delimit a multiline comment
2452 ;; and don't contain alphabetic characters, like "/*******",
2453 ;; "- - - */" etc.
2454 (save-excursion
2455 (backward-paragraph)
2456 (unless (bobp) (forward-line))
2457 (if (string-match "^/\\*[^a-zA-Z]*$" (thing-at-point 'line))
2458 (narrow-to-region (point-at-eol) (point-max))))
2459 (save-excursion
2460 (forward-paragraph)
2461 (forward-line -1)
2462 (if (string-match "^[^a-zA-Z]*\\*/$" (thing-at-point 'line))
2463 (narrow-to-region (point-min) (point-at-bol))))
2464 (let ((fill-prefix (prolog-guess-fill-prefix)))
2465 (fill-paragraph nil))))
2466 )))
2467
2468 (defun prolog-do-auto-fill ()
2469 "Carry out Auto Fill for Prolog mode.
2470 In effect it sets the `fill-prefix' when inside comments and then calls
2471 `do-auto-fill'."
2472 (let ((fill-prefix (prolog-guess-fill-prefix)))
2473 (do-auto-fill)
2474 ))
2475
2476 (defalias 'prolog-replace-in-string
2477 (if (fboundp 'replace-in-string)
2478 #'replace-in-string
2479 (lambda (str regexp newtext &optional literal)
2480 (replace-regexp-in-string regexp newtext str nil literal))))
2481 \f
2482 ;;-------------------------------------------------------------------
2483 ;; The tokenizer
2484 ;;-------------------------------------------------------------------
2485
2486 (defconst prolog-tokenize-searchkey
2487 (concat "[0-9]+'"
2488 "\\|"
2489 "['\"]"
2490 "\\|"
2491 prolog-left-paren
2492 "\\|"
2493 prolog-right-paren
2494 "\\|"
2495 "%"
2496 "\\|"
2497 "/\\*"
2498 ))
2499
2500 (defun prolog-tokenize (beg end &optional stopcond)
2501 "Tokenize a region of prolog code between BEG and END.
2502 STOPCOND decides the stop condition of the parsing. Valid values
2503 are 'zerodepth which stops the parsing at the first right parenthesis
2504 where the parenthesis depth is zero, 'skipover which skips over
2505 the current entity (e.g. a list, a string, etc.) and nil.
2506
2507 The function returns a list with the following information:
2508 0. parenthesis depth
2509 3. 'atm if END is inside an atom
2510 'str if END is inside a string
2511 'chr if END is in a character code expression (0'x)
2512 nil otherwise
2513 4. non-nil if END is inside a comment
2514 5. end position (always equal to END if STOPCOND is nil)
2515 The rest of the elements are undefined."
2516 (save-excursion
2517 (let* ((end2 (1+ end))
2518 oldp
2519 (depth 0)
2520 (quoted nil)
2521 inside_cmt
2522 (endpos end2)
2523 skiptype ; The type of entity we'll skip over
2524 )
2525 (goto-char beg)
2526
2527 (if (and (eq stopcond 'skipover)
2528 (looking-at "[^[({'\"]"))
2529 (setq endpos (point)) ; Stay where we are
2530 (while (and
2531 (re-search-forward prolog-tokenize-searchkey end2 t)
2532 (< (point) end2))
2533 (progn
2534 (setq oldp (point))
2535 (goto-char (match-beginning 0))
2536 (cond
2537 ;; Atoms and strings
2538 ((looking-at "'")
2539 ;; Find end of atom
2540 (if (re-search-forward "[^\\]'" end2 'limit)
2541 ;; Found end of atom
2542 (progn
2543 (setq oldp end2)
2544 (if (and (eq stopcond 'skipover)
2545 (not skiptype))
2546 (setq endpos (point))
2547 (setq oldp (point)))) ; Continue tokenizing
2548 (setq quoted 'atm)))
2549
2550 ((looking-at "\"")
2551 ;; Find end of string
2552 (if (re-search-forward "[^\\]\"" end2 'limit)
2553 ;; Found end of string
2554 (progn
2555 (setq oldp end2)
2556 (if (and (eq stopcond 'skipover)
2557 (not skiptype))
2558 (setq endpos (point))
2559 (setq oldp (point)))) ; Continue tokenizing
2560 (setq quoted 'str)))
2561
2562 ;; Paren stuff
2563 ((looking-at prolog-left-paren)
2564 (setq depth (1+ depth))
2565 (setq skiptype 'paren))
2566
2567 ((looking-at prolog-right-paren)
2568 (setq depth (1- depth))
2569 (if (and
2570 (or (eq stopcond 'zerodepth)
2571 (and (eq stopcond 'skipover)
2572 (eq skiptype 'paren)))
2573 (= depth 0))
2574 (progn
2575 (setq endpos (1+ (point)))
2576 (setq oldp end2))))
2577
2578 ;; Comment stuff
2579 ((looking-at comment-start)
2580 (end-of-line)
2581 ;; (if (>= (point) end2)
2582 (if (>= (point) end)
2583 (progn
2584 (setq inside_cmt t)
2585 (setq oldp end2))
2586 (setq oldp (point))))
2587
2588 ((looking-at "/\\*")
2589 (if (re-search-forward "\\*/" end2 'limit)
2590 (setq oldp (point))
2591 (setq inside_cmt t)
2592 (setq oldp end2)))
2593
2594 ;; 0'char
2595 ((looking-at "0'")
2596 (setq oldp (1+ (match-end 0)))
2597 (if (> oldp end)
2598 (setq quoted 'chr)))
2599
2600 ;; base'number
2601 ((looking-at "[0-9]+'")
2602 (goto-char (match-end 0))
2603 (skip-chars-forward "0-9a-zA-Z")
2604 (setq oldp (point)))
2605
2606
2607 )
2608 (goto-char oldp)
2609 )) ; End of while
2610 )
2611
2612 ;; Deal with multi-line comments
2613 (and (prolog-inside-mline-comment end)
2614 (setq inside_cmt t))
2615
2616 ;; Create return list
2617 (list depth nil nil quoted inside_cmt endpos)
2618 )))
2619
2620 (defun prolog-inside-mline-comment (here)
2621 (save-excursion
2622 (goto-char here)
2623 (let* ((next-close (save-excursion (search-forward "*/" nil t)))
2624 (next-open (save-excursion (search-forward "/*" nil t)))
2625 (prev-open (save-excursion (search-backward "/*" nil t)))
2626 (prev-close (save-excursion (search-backward "*/" nil t)))
2627 (unmatched-next-close (and next-close
2628 (or (not next-open)
2629 (> next-open next-close))))
2630 (unmatched-prev-open (and prev-open
2631 (or (not prev-close)
2632 (> prev-open prev-close))))
2633 )
2634 (or unmatched-next-close unmatched-prev-open)
2635 )))
2636
2637 \f
2638 ;;-------------------------------------------------------------------
2639 ;; Online help
2640 ;;-------------------------------------------------------------------
2641
2642 (defvar prolog-help-function
2643 '((mercury nil)
2644 (eclipse prolog-help-online)
2645 ;; (sicstus prolog-help-info)
2646 (sicstus prolog-find-documentation)
2647 (swi prolog-help-online)
2648 (t prolog-help-online))
2649 "Alist for the name of the function for finding help on a predicate.")
2650
2651 (defun prolog-help-on-predicate ()
2652 "Invoke online help on the atom under cursor."
2653 (interactive)
2654
2655 (cond
2656 ;; Redirect help for SICStus to `prolog-find-documentation'.
2657 ((eq prolog-help-function-i 'prolog-find-documentation)
2658 (prolog-find-documentation))
2659
2660 ;; Otherwise, ask for the predicate name and then call the function
2661 ;; in prolog-help-function-i
2662 (t
2663 (let* ((word (prolog-atom-under-point))
2664 (predicate (read-string
2665 (format "Help on predicate%s: "
2666 (if word
2667 (concat " (default " word ")")
2668 ""))
2669 nil nil word))
2670 ;;point
2671 )
2672 (if prolog-help-function-i
2673 (funcall prolog-help-function-i predicate)
2674 (error "Sorry, no help method defined for this Prolog system."))))
2675 ))
2676
2677 (defun prolog-help-info (predicate)
2678 (let ((buffer (current-buffer))
2679 oldp
2680 (str (concat "^\\* " (regexp-quote predicate) " */")))
2681 (require 'info)
2682 (pop-to-buffer nil)
2683 (Info-goto-node prolog-info-predicate-index)
2684 (if (not (re-search-forward str nil t))
2685 (error (format "Help on predicate `%s' not found." predicate)))
2686
2687 (setq oldp (point))
2688 (if (re-search-forward str nil t)
2689 ;; Multiple matches, ask user
2690 (let ((max 2)
2691 n)
2692 ;; Count matches
2693 (while (re-search-forward str nil t)
2694 (setq max (1+ max)))
2695
2696 (goto-char oldp)
2697 (re-search-backward "[^ /]" nil t)
2698 (recenter 0)
2699 (setq n (read-string ;; was read-input, which is obsolete
2700 (format "Several matches, choose (1-%d): " max) "1"))
2701 (forward-line (- (string-to-number n) 1)))
2702 ;; Single match
2703 (re-search-backward "[^ /]" nil t))
2704
2705 ;; (Info-follow-nearest-node (point))
2706 (prolog-Info-follow-nearest-node)
2707 (re-search-forward (concat "^`" (regexp-quote predicate)) nil t)
2708 (beginning-of-line)
2709 (recenter 0)
2710 (pop-to-buffer buffer)))
2711
2712 (defun prolog-Info-follow-nearest-node ()
2713 (if (featurep 'xemacs)
2714 (Info-follow-nearest-node (point))
2715 (Info-follow-nearest-node)))
2716
2717 (defun prolog-help-online (predicate)
2718 (prolog-ensure-process)
2719 (process-send-string "prolog" (concat "help(" predicate ").\n"))
2720 (display-buffer "*prolog*"))
2721
2722 (defun prolog-help-apropos (string)
2723 "Find Prolog apropos on given STRING.
2724 This function is only available when `prolog-system' is set to `swi'."
2725 (interactive "sApropos: ")
2726 (cond
2727 ((eq prolog-system 'swi)
2728 (prolog-ensure-process)
2729 (process-send-string "prolog" (concat "apropos(" string ").\n"))
2730 (display-buffer "*prolog*"))
2731 (t
2732 (error "Sorry, no Prolog apropos available for this Prolog system."))))
2733
2734 (defun prolog-atom-under-point ()
2735 "Return the atom under or left to the point."
2736 (save-excursion
2737 (let ((nonatom_chars "[](){},\. \t\n")
2738 start)
2739 (skip-chars-forward (concat "^" nonatom_chars))
2740 (skip-chars-backward nonatom_chars)
2741 (skip-chars-backward (concat "^" nonatom_chars))
2742 (setq start (point))
2743 (skip-chars-forward (concat "^" nonatom_chars))
2744 (buffer-substring-no-properties start (point))
2745 )))
2746
2747 \f
2748 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2749 ;; Help function with completion
2750 ;; Stolen from Per Mildner's SICStus debugger mode and modified
2751
2752 (defun prolog-find-documentation ()
2753 "Go to the Info node for a predicate in the SICStus Info manual."
2754 (interactive)
2755 (let ((pred (prolog-read-predicate)))
2756 (prolog-goto-predicate-info pred)))
2757
2758 (defvar prolog-info-alist nil
2759 "Alist with all builtin predicates.
2760 Only for internal use by `prolog-find-documentation'")
2761
2762 ;; Very similar to prolog-help-info except that that function cannot
2763 ;; cope with arity and that it asks the user if there are several
2764 ;; functors with different arity. This function also uses
2765 ;; prolog-info-alist for finding the info node, rather than parsing
2766 ;; the predicate index.
2767 (defun prolog-goto-predicate-info (predicate)
2768 "Go to the info page for PREDICATE, which is a PredSpec."
2769 (interactive)
2770 (require 'info)
2771 (string-match "\\(.*\\)/\\([0-9]+\\).*$" predicate)
2772 (let ((buffer (current-buffer))
2773 (name (match-string 1 predicate))
2774 (arity (string-to-number (match-string 2 predicate)))
2775 ;oldp
2776 ;(str (regexp-quote predicate))
2777 )
2778 (pop-to-buffer nil)
2779
2780 (Info-goto-node
2781 prolog-info-predicate-index) ;; We must be in the SICStus pages
2782 (Info-goto-node (car (cdr (assoc predicate prolog-info-alist))))
2783
2784 (prolog-find-term (regexp-quote name) arity "^`")
2785
2786 (recenter 0)
2787 (pop-to-buffer buffer))
2788 )
2789
2790 (defun prolog-read-predicate ()
2791 "Read a PredSpec from the user.
2792 Returned value is a string \"FUNCTOR/ARITY\".
2793 Interaction supports completion."
2794 (let ((default (prolog-atom-under-point)))
2795 ;; If the predicate index is not yet built, do it now
2796 (if (not prolog-info-alist)
2797 (prolog-build-info-alist))
2798 ;; Test if the default string could be the base for completion.
2799 ;; Discard it if not.
2800 (if (eq (try-completion default prolog-info-alist) nil)
2801 (setq default nil))
2802 ;; Read the PredSpec from the user
2803 (completing-read
2804 (if (zerop (length default))
2805 "Help on predicate: "
2806 (concat "Help on predicate (default " default "): "))
2807 prolog-info-alist nil t nil nil default)))
2808
2809 (defun prolog-build-info-alist (&optional verbose)
2810 "Build an alist of all builtins and library predicates.
2811 Each element is of the form (\"NAME/ARITY\" . (INFO-NODE1 INFO-NODE2 ...)).
2812 Typically there is just one Info node associated with each name
2813 If an optional argument VERBOSE is non-nil, print messages at the beginning
2814 and end of list building."
2815 (if verbose
2816 (message "Building info alist..."))
2817 (setq prolog-info-alist
2818 (let ((l ())
2819 (last-entry (cons "" ())))
2820 (save-excursion
2821 (save-window-excursion
2822 ;; select any window but the minibuffer (as we cannot switch
2823 ;; buffers in minibuffer window.
2824 ;; I am not sure this is the right/best way
2825 (if (active-minibuffer-window) ; nil if none active
2826 (select-window (next-window)))
2827 ;; Do this after going away from minibuffer window
2828 (save-window-excursion
2829 (info))
2830 (Info-goto-node prolog-info-predicate-index)
2831 (goto-char (point-min))
2832 (while (re-search-forward
2833 "^\\* \\(.+\\)/\\([0-9]+\\)\\([^\n:*]*\\):" nil t)
2834 (let* ((name (match-string 1))
2835 (arity (string-to-number (match-string 2)))
2836 (comment (match-string 3))
2837 (fa (format "%s/%d%s" name arity comment))
2838 info-node)
2839 (beginning-of-line)
2840 ;; Extract the info node name
2841 (setq info-node (progn
2842 (re-search-forward ":[ \t]*\\([^:]+\\).$")
2843 (match-string 1)
2844 ))
2845 ;; ###### Easier? (from Milan version 0.1.28)
2846 ;; (setq info-node (Info-extract-menu-node-name))
2847 (if (equal fa (car last-entry))
2848 (setcdr last-entry (cons info-node (cdr last-entry)))
2849 (setq last-entry (cons fa (list info-node))
2850 l (cons last-entry l)))))
2851 (nreverse l)
2852 ))))
2853 (if verbose
2854 (message "Building info alist... done.")))
2855
2856 \f
2857 ;;-------------------------------------------------------------------
2858 ;; Miscellaneous functions
2859 ;;-------------------------------------------------------------------
2860
2861 ;; For Windows. Change backslash to slash. SICStus handles either
2862 ;; path separator but backslash must be doubled, therefore use slash.
2863 (defun prolog-bsts (string)
2864 "Change backslashes to slashes in STRING."
2865 (let ((str1 (copy-sequence string))
2866 (len (length string))
2867 (i 0))
2868 (while (< i len)
2869 (if (char-equal (aref str1 i) ?\\)
2870 (aset str1 i ?/))
2871 (setq i (1+ i)))
2872 str1))
2873
2874 ;;(defun prolog-temporary-file ()
2875 ;; "Make temporary file name for compilation."
2876 ;; (make-temp-name
2877 ;; (concat
2878 ;; (or
2879 ;; (getenv "TMPDIR")
2880 ;; (getenv "TEMP")
2881 ;; (getenv "TMP")
2882 ;; (getenv "SYSTEMP")
2883 ;; "/tmp")
2884 ;; "/prolcomp")))
2885 ;;(setq prolog-temp-filename (prolog-bsts (prolog-temporary-file)))
2886
2887 (defun prolog-temporary-file ()
2888 "Make temporary file name for compilation."
2889 (if prolog-temporary-file-name
2890 ;; We already have a file, erase content and continue
2891 (progn
2892 (write-region "" nil prolog-temporary-file-name nil 'silent)
2893 prolog-temporary-file-name)
2894 ;; Actually create the file and set `prolog-temporary-file-name'
2895 ;; accordingly.
2896 (setq prolog-temporary-file-name
2897 (make-temp-file "prolcomp" nil ".pl"))))
2898
2899 (defun prolog-goto-prolog-process-buffer ()
2900 "Switch to the prolog process buffer and go to its end."
2901 (switch-to-buffer-other-window "*prolog*")
2902 (goto-char (point-max))
2903 )
2904
2905 (defun prolog-enable-sicstus-sd ()
2906 "Enable the source level debugging facilities of SICStus 3.7 and later."
2907 (interactive)
2908 (require 'pltrace) ; Load the SICStus debugger code
2909 ;; Turn on the source level debugging by default
2910 (add-hook 'prolog-inferior-mode-hook 'pltrace-on)
2911 (if (not prolog-use-sicstus-sd)
2912 (progn
2913 ;; If there is a *prolog* buffer, then call pltrace-on
2914 (if (get-buffer "*prolog*")
2915 ;; Avoid compilation warnings by using eval
2916 (eval '(pltrace-on)))
2917 (setq prolog-use-sicstus-sd t)
2918 )))
2919
2920 (defun prolog-disable-sicstus-sd ()
2921 "Disable the source level debugging facilities of SICStus 3.7 and later."
2922 (interactive)
2923 (setq prolog-use-sicstus-sd nil)
2924 ;; Remove the hook
2925 (remove-hook 'prolog-inferior-mode-hook 'pltrace-on)
2926 ;; If there is a *prolog* buffer, then call pltrace-off
2927 (if (get-buffer "*prolog*")
2928 ;; Avoid compile warnings by using eval
2929 (eval '(pltrace-off))))
2930
2931 (defun prolog-toggle-sicstus-sd ()
2932 ;; FIXME: Use define-minor-mode.
2933 "Toggle the source level debugging facilities of SICStus 3.7 and later."
2934 (interactive)
2935 (if prolog-use-sicstus-sd
2936 (prolog-disable-sicstus-sd)
2937 (prolog-enable-sicstus-sd)))
2938
2939 (defun prolog-debug-on (&optional arg)
2940 "Enable debugging.
2941 When called with prefix argument ARG, disable debugging instead."
2942 (interactive "P")
2943 (if arg
2944 (prolog-debug-off)
2945 (prolog-process-insert-string (get-process "prolog")
2946 prolog-debug-on-string)
2947 (process-send-string "prolog" prolog-debug-on-string)))
2948
2949 (defun prolog-debug-off ()
2950 "Disable debugging."
2951 (interactive)
2952 (prolog-process-insert-string (get-process "prolog")
2953 prolog-debug-off-string)
2954 (process-send-string "prolog" prolog-debug-off-string))
2955
2956 (defun prolog-trace-on (&optional arg)
2957 "Enable tracing.
2958 When called with prefix argument ARG, disable tracing instead."
2959 (interactive "P")
2960 (if arg
2961 (prolog-trace-off)
2962 (prolog-process-insert-string (get-process "prolog")
2963 prolog-trace-on-string)
2964 (process-send-string "prolog" prolog-trace-on-string)))
2965
2966 (defun prolog-trace-off ()
2967 "Disable tracing."
2968 (interactive)
2969 (prolog-process-insert-string (get-process "prolog")
2970 prolog-trace-off-string)
2971 (process-send-string "prolog" prolog-trace-off-string))
2972
2973 (defun prolog-zip-on (&optional arg)
2974 "Enable zipping (for SICStus 3.7 and later).
2975 When called with prefix argument ARG, disable zipping instead."
2976 (interactive "P")
2977 (if arg
2978 (prolog-zip-off)
2979 (prolog-process-insert-string (get-process "prolog")
2980 prolog-zip-on-string)
2981 (process-send-string "prolog" prolog-zip-on-string)))
2982
2983 (defun prolog-zip-off ()
2984 "Disable zipping (for SICStus 3.7 and later)."
2985 (interactive)
2986 (prolog-process-insert-string (get-process "prolog")
2987 prolog-zip-off-string)
2988 (process-send-string "prolog" prolog-zip-off-string))
2989
2990 ;; (defun prolog-create-predicate-index ()
2991 ;; "Create an index for all predicates in the buffer."
2992 ;; (let ((predlist '())
2993 ;; clauseinfo
2994 ;; object
2995 ;; pos
2996 ;; )
2997 ;; (goto-char (point-min))
2998 ;; ;; Replace with prolog-clause-start!
2999 ;; (while (re-search-forward "^.+:-" nil t)
3000 ;; (setq pos (match-beginning 0))
3001 ;; (setq clauseinfo (prolog-clause-info))
3002 ;; (setq object (prolog-in-object))
3003 ;; (setq predlist (append
3004 ;; predlist
3005 ;; (list (cons
3006 ;; (if (and (eq prolog-system 'sicstus)
3007 ;; (prolog-in-object))
3008 ;; (format "%s::%s/%d"
3009 ;; object
3010 ;; (nth 0 clauseinfo)
3011 ;; (nth 1 clauseinfo))
3012 ;; (format "%s/%d"
3013 ;; (nth 0 clauseinfo)
3014 ;; (nth 1 clauseinfo)))
3015 ;; pos
3016 ;; ))))
3017 ;; (prolog-end-of-predicate))
3018 ;; predlist))
3019
3020 (defun prolog-get-predspec ()
3021 (save-excursion
3022 (let ((state (prolog-clause-info))
3023 (object (prolog-in-object)))
3024 (if (or (equal (nth 0 state) "") (equal (prolog-in-string-or-comment) 'cmt))
3025 nil
3026 (if (and (eq prolog-system 'sicstus)
3027 object)
3028 (format "%s::%s/%d"
3029 object
3030 (nth 0 state)
3031 (nth 1 state))
3032 (format "%s/%d"
3033 (nth 0 state)
3034 (nth 1 state)))
3035 ))))
3036
3037 ;; For backward compatibility. Stolen from custom.el.
3038 (or (fboundp 'match-string)
3039 ;; Introduced in Emacs 19.29.
3040 (defun match-string (num &optional string)
3041 "Return string of text matched by last search.
3042 NUM specifies which parenthesized expression in the last regexp.
3043 Value is nil if NUMth pair didn't match, or there were less than NUM pairs.
3044 Zero means the entire text matched by the whole regexp or whole string.
3045 STRING should be given if the last search was by `string-match' on STRING."
3046 (if (match-beginning num)
3047 (if string
3048 (substring string (match-beginning num) (match-end num))
3049 (buffer-substring (match-beginning num) (match-end num))))))
3050
3051 (defun prolog-pred-start ()
3052 "Return the starting point of the first clause of the current predicate."
3053 (save-excursion
3054 (goto-char (prolog-clause-start))
3055 ;; Find first clause, unless it was a directive
3056 (if (and (not (looking-at "[:?]-"))
3057 (not (looking-at "[ \t]*[%/]")) ; Comment
3058
3059 )
3060 (let* ((pinfo (prolog-clause-info))
3061 (predname (nth 0 pinfo))
3062 (arity (nth 1 pinfo))
3063 (op (point)))
3064 (while (and (re-search-backward
3065 (format "^%s\\([(\\.]\\| *%s\\)"
3066 predname prolog-head-delimiter) nil t)
3067 (= arity (nth 1 (prolog-clause-info)))
3068 )
3069 (setq op (point)))
3070 (if (eq prolog-system 'mercury)
3071 ;; Skip to the beginning of declarations of the predicate
3072 (progn
3073 (goto-char (prolog-beginning-of-clause))
3074 (while (and (not (eq (point) op))
3075 (looking-at
3076 (format ":-[ \t]*\\(pred\\|mode\\)[ \t]+%s"
3077 predname)))
3078 (setq op (point))
3079 (goto-char (prolog-beginning-of-clause)))))
3080 op)
3081 (point))))
3082
3083 (defun prolog-pred-end ()
3084 "Return the position at the end of the last clause of the current predicate."
3085 (save-excursion
3086 (goto-char (prolog-clause-end)) ; if we are before the first predicate
3087 (goto-char (prolog-clause-start))
3088 (let* ((pinfo (prolog-clause-info))
3089 (predname (nth 0 pinfo))
3090 (arity (nth 1 pinfo))
3091 oldp
3092 (notdone t)
3093 (op (point)))
3094 (if (looking-at "[:?]-")
3095 ;; This was a directive
3096 (progn
3097 (if (and (eq prolog-system 'mercury)
3098 (looking-at
3099 (format ":-[ \t]*\\(pred\\|mode\\)[ \t]+\\(%s+\\)"
3100 prolog-atom-regexp)))
3101 ;; Skip predicate declarations
3102 (progn
3103 (setq predname (buffer-substring-no-properties
3104 (match-beginning 2) (match-end 2)))
3105 (while (re-search-forward
3106 (format
3107 "\n*\\(:-[ \t]*\\(pred\\|mode\\)[ \t]+\\)?%s[( \t]"
3108 predname)
3109 nil t))))
3110 (goto-char (prolog-clause-end))
3111 (setq op (point)))
3112 ;; It was not a directive, find the last clause
3113 (while (and notdone
3114 (re-search-forward
3115 (format "^%s\\([(\\.]\\| *%s\\)"
3116 predname prolog-head-delimiter) nil t)
3117 (= arity (nth 1 (prolog-clause-info))))
3118 (setq oldp (point))
3119 (setq op (prolog-clause-end))
3120 (if (>= oldp op)
3121 ;; End of clause not found.
3122 (setq notdone nil)
3123 ;; Continue while loop
3124 (goto-char op))))
3125 op)))
3126
3127 (defun prolog-clause-start (&optional not-allow-methods)
3128 "Return the position at the start of the head of the current clause.
3129 If NOTALLOWMETHODS is non-nil then do not match on methods in
3130 objects (relevent only if 'prolog-system' is set to 'sicstus)."
3131 (save-excursion
3132 (let ((notdone t)
3133 (retval (point-min)))
3134 (end-of-line)
3135
3136 ;; SICStus object?
3137 (if (and (not not-allow-methods)
3138 (eq prolog-system 'sicstus)
3139 (prolog-in-object))
3140 (while (and
3141 notdone
3142 ;; Search for a head or a fact
3143 (re-search-backward
3144 ;; If in object, then find method start.
3145 ;; "^[ \t]+[a-z$].*\\(:-\\|&\\|:: {\\|,\\)"
3146 "^[ \t]+[a-z$].*\\(:-\\|&\\|:: {\\)" ; The comma causes
3147 ; problems since we cannot assume
3148 ; that the line starts at column 0,
3149 ; thus we don't know if the line
3150 ; is a head or a subgoal
3151 (point-min) t))
3152 (if (>= (prolog-paren-balance) 0) ; To no match on " a) :-"
3153 ;; Start of method found
3154 (progn
3155 (setq retval (point))
3156 (setq notdone nil)))
3157 ) ; End of while
3158
3159 ;; Not in object
3160 (while (and
3161 notdone
3162 ;; Search for a text at beginning of a line
3163 ;; ######
3164 ;; (re-search-backward "^[a-z$']" nil t))
3165 (let ((case-fold-search nil))
3166 (re-search-backward
3167 ;; (format "^[%s$']" prolog-lower-case-string)
3168 ;; FIXME: Use [:lower:]
3169 (format "^\\([%s$']\\|[:?]-\\)" prolog-lower-case-string)
3170 nil t)))
3171 (let ((bal (prolog-paren-balance)))
3172 (cond
3173 ((> bal 0)
3174 ;; Start of clause found
3175 (progn
3176 (setq retval (point))
3177 (setq notdone nil)))
3178 ((and (= bal 0)
3179 (looking-at
3180 (format ".*\\(\\.\\|%s\\|!,\\)[ \t]*\\(%%.*\\|\\)$"
3181 prolog-head-delimiter)))
3182 ;; Start of clause found if the line ends with a '.' or
3183 ;; a prolog-head-delimiter
3184 (progn
3185 (setq retval (point))
3186 (setq notdone nil))
3187 )
3188 (t nil) ; Do nothing
3189 ))))
3190
3191 retval)))
3192
3193 (defun prolog-clause-end (&optional not-allow-methods)
3194 "Return the position at the end of the current clause.
3195 If NOTALLOWMETHODS is non-nil then do not match on methods in
3196 objects (relevent only if 'prolog-system' is set to 'sicstus)."
3197 (save-excursion
3198 (beginning-of-line) ; Necessary since we use "^...." for the search.
3199 (if (re-search-forward
3200 (if (and (not not-allow-methods)
3201 (eq prolog-system 'sicstus)
3202 (prolog-in-object))
3203 (format
3204 "^\\(%s\\|%s\\|[^\n\'\"%%]\\)*&[ \t]*\\(\\|%%.*\\)$\\|[ \t]*}"
3205 prolog-quoted-atom-regexp prolog-string-regexp)
3206 (format
3207 "^\\(%s\\|%s\\|[^\n\'\"%%]\\)*\\.[ \t]*\\(\\|%%.*\\)$"
3208 prolog-quoted-atom-regexp prolog-string-regexp))
3209 nil t)
3210 (if (and (prolog-in-string-or-comment)
3211 (not (eobp)))
3212 (progn
3213 (forward-char)
3214 (prolog-clause-end))
3215 (point))
3216 (point))))
3217
3218 (defun prolog-clause-info ()
3219 "Return a (name arity) list for the current clause."
3220 (save-excursion
3221 (goto-char (prolog-clause-start))
3222 (let* ((op (point))
3223 (predname
3224 (if (looking-at prolog-atom-char-regexp)
3225 (progn
3226 (skip-chars-forward "^ (\\.")
3227 (buffer-substring op (point)))
3228 ""))
3229 (arity 0))
3230 ;; Retrieve the arity.
3231 (if (looking-at prolog-left-paren)
3232 (let ((endp (save-excursion
3233 (prolog-forward-list) (point))))
3234 (setq arity 1)
3235 (forward-char 1) ; Skip the opening paren.
3236 (while (progn
3237 (skip-chars-forward "^[({,'\"")
3238 (< (point) endp))
3239 (if (looking-at ",")
3240 (progn
3241 (setq arity (1+ arity))
3242 (forward-char 1) ; Skip the comma.
3243 )
3244 ;; We found a string, list or something else we want
3245 ;; to skip over. Always use prolog-tokenize,
3246 ;; parse-partial-sexp does not have a 'skipover mode.
3247 (goto-char (nth 5 (prolog-tokenize (point) endp 'skipover))))
3248 )))
3249 (list predname arity))))
3250
3251 (defun prolog-in-object ()
3252 "Return object name if the point is inside a SICStus object definition."
3253 ;; Return object name if the last line that starts with a character
3254 ;; that is neither white space nor a comment start
3255 (save-excursion
3256 (if (save-excursion
3257 (beginning-of-line)
3258 (looking-at "\\([^\n ]+\\)[ \t]*::[ \t]*{"))
3259 ;; We were in the head of the object
3260 (match-string 1)
3261 ;; We were not in the head
3262 (if (and (re-search-backward "^[a-z$'}]" nil t)
3263 (looking-at "\\([^\n ]+\\)[ \t]*::[ \t]*{"))
3264 (match-string 1)
3265 nil))))
3266
3267 (defun prolog-forward-list ()
3268 "Move the point to the matching right parenthesis."
3269 (interactive)
3270 (if prolog-use-prolog-tokenizer-flag
3271 (let ((state (prolog-tokenize (point) (point-max) 'zerodepth)))
3272 (goto-char (nth 5 state)))
3273 (forward-list)))
3274
3275 ;; NB: This could be done more efficiently!
3276 (defun prolog-backward-list ()
3277 "Move the point to the matching left parenthesis."
3278 (interactive)
3279 (if prolog-use-prolog-tokenizer-flag
3280 (let ((bal 0)
3281 (paren-regexp (concat prolog-left-paren "\\|" prolog-right-paren))
3282 (notdone t))
3283 ;; FIXME: Doesn't this incorrectly count 0'( and 0') ?
3284 (while (and notdone (re-search-backward paren-regexp nil t))
3285 (cond
3286 ((looking-at prolog-left-paren)
3287 (if (not (prolog-in-string-or-comment))
3288 (setq bal (1+ bal)))
3289 (if (= bal 0)
3290 (setq notdone nil)))
3291 ((looking-at prolog-right-paren)
3292 (if (not (prolog-in-string-or-comment))
3293 (setq bal (1- bal))))
3294 )))
3295 (backward-list)))
3296
3297 (defun prolog-beginning-of-clause ()
3298 "Move to the beginning of current clause.
3299 If already at the beginning of clause, move to previous clause."
3300 (interactive)
3301 (let ((point (point))
3302 (new-point (prolog-clause-start)))
3303 (if (and (>= new-point point)
3304 (> point 1))
3305 (progn
3306 (goto-char (1- point))
3307 (goto-char (prolog-clause-start)))
3308 (goto-char new-point)
3309 (skip-chars-forward " \t"))))
3310
3311 ;; (defun prolog-previous-clause ()
3312 ;; "Move to the beginning of the previous clause."
3313 ;; (interactive)
3314 ;; (forward-char -1)
3315 ;; (prolog-beginning-of-clause))
3316
3317 (defun prolog-end-of-clause ()
3318 "Move to the end of clause.
3319 If already at the end of clause, move to next clause."
3320 (interactive)
3321 (let ((point (point))
3322 (new-point (prolog-clause-end)))
3323 (if (and (<= new-point point)
3324 (not (eq new-point (point-max))))
3325 (progn
3326 (goto-char (1+ point))
3327 (goto-char (prolog-clause-end)))
3328 (goto-char new-point))))
3329
3330 ;; (defun prolog-next-clause ()
3331 ;; "Move to the beginning of the next clause."
3332 ;; (interactive)
3333 ;; (prolog-end-of-clause)
3334 ;; (forward-char)
3335 ;; (prolog-end-of-clause)
3336 ;; (prolog-beginning-of-clause))
3337
3338 (defun prolog-beginning-of-predicate ()
3339 "Go to the nearest beginning of predicate before current point.
3340 Return the final point or nil if no such a beginning was found."
3341 (interactive)
3342 (let ((op (point))
3343 (pos (prolog-pred-start)))
3344 (if pos
3345 (if (= op pos)
3346 (if (not (bobp))
3347 (progn
3348 (goto-char pos)
3349 (backward-char 1)
3350 (setq pos (prolog-pred-start))
3351 (if pos
3352 (progn
3353 (goto-char pos)
3354 (point)))))
3355 (goto-char pos)
3356 (point)))))
3357
3358 (defun prolog-end-of-predicate ()
3359 "Go to the end of the current predicate."
3360 (interactive)
3361 (let ((op (point)))
3362 (goto-char (prolog-pred-end))
3363 (if (= op (point))
3364 (progn
3365 (forward-line 1)
3366 (prolog-end-of-predicate)))))
3367
3368 (defun prolog-insert-predspec ()
3369 "Insert the predspec for the current predicate."
3370 (interactive)
3371 (let* ((pinfo (prolog-clause-info))
3372 (predname (nth 0 pinfo))
3373 (arity (nth 1 pinfo)))
3374 (insert (format "%s/%d" predname arity))))
3375
3376 (defun prolog-view-predspec ()
3377 "Insert the predspec for the current predicate."
3378 (interactive)
3379 (let* ((pinfo (prolog-clause-info))
3380 (predname (nth 0 pinfo))
3381 (arity (nth 1 pinfo)))
3382 (message (format "%s/%d" predname arity))))
3383
3384 (defun prolog-insert-predicate-template ()
3385 "Insert the template for the current clause."
3386 (interactive)
3387 (let* ((n 1)
3388 oldp
3389 (pinfo (prolog-clause-info))
3390 (predname (nth 0 pinfo))
3391 (arity (nth 1 pinfo)))
3392 (insert predname)
3393 (if (> arity 0)
3394 (progn
3395 (insert "(")
3396 (when prolog-electric-dot-full-predicate-template
3397 (setq oldp (point))
3398 (while (< n arity)
3399 (insert ",")
3400 (setq n (1+ n)))
3401 (insert ")")
3402 (goto-char oldp))
3403 ))
3404 ))
3405
3406 (defun prolog-insert-next-clause ()
3407 "Insert newline and the name of the current clause."
3408 (interactive)
3409 (insert "\n")
3410 (prolog-insert-predicate-template))
3411
3412 (defun prolog-insert-module-modeline ()
3413 "Insert a modeline for module specification.
3414 This line should be first in the buffer.
3415 The module name should be written manually just before the semi-colon."
3416 (interactive)
3417 (insert "%%% -*- Module: ; -*-\n")
3418 (backward-char 6))
3419
3420 (defalias 'prolog-uncomment-region
3421 (if (fboundp 'uncomment-region) #'uncomment-region
3422 (lambda (beg end)
3423 "Uncomment the region between BEG and END."
3424 (interactive "r")
3425 (comment-region beg end -1))))
3426
3427 (defun prolog-goto-comment-column (&optional nocreate)
3428 "Move comments on the current line to the correct position.
3429 If NOCREATE is nil (or omitted) and there is no comment on the line, then
3430 a new comment is created."
3431 (interactive)
3432 (beginning-of-line)
3433 (if (or (not nocreate)
3434 (and
3435 (re-search-forward
3436 (format "^\\(\\(%s\\|%s\\|[^\n\'\"%%]\\)*\\)%% *"
3437 prolog-quoted-atom-regexp prolog-string-regexp)
3438 (line-end-position) 'limit)
3439 (progn
3440 (goto-char (match-beginning 0))
3441 (not (eq (prolog-in-string-or-comment) 'txt)))))
3442 (indent-for-comment)))
3443
3444 (defun prolog-indent-predicate ()
3445 "*Indent the current predicate."
3446 (interactive)
3447 (indent-region (prolog-pred-start) (prolog-pred-end) nil))
3448
3449 (defun prolog-indent-buffer ()
3450 "*Indent the entire buffer."
3451 (interactive)
3452 (indent-region (point-min) (point-max) nil))
3453
3454 (defun prolog-mark-clause ()
3455 "Put mark at the end of this clause and move point to the beginning."
3456 (interactive)
3457 (let ((pos (point)))
3458 (goto-char (prolog-clause-end))
3459 (forward-line 1)
3460 (beginning-of-line)
3461 (set-mark (point))
3462 (goto-char pos)
3463 (goto-char (prolog-clause-start))))
3464
3465 (defun prolog-mark-predicate ()
3466 "Put mark at the end of this predicate and move point to the beginning."
3467 (interactive)
3468 (goto-char (prolog-pred-end))
3469 (let ((pos (point)))
3470 (forward-line 1)
3471 (beginning-of-line)
3472 (set-mark (point))
3473 (goto-char pos)
3474 (goto-char (prolog-pred-start))))
3475
3476 ;; Stolen from `cc-mode.el':
3477 (defun prolog-electric-delete (arg)
3478 "Delete preceding character or whitespace.
3479 If `prolog-hungry-delete-key-flag' is non-nil, then all preceding whitespace is
3480 consumed. If however an ARG is supplied, or `prolog-hungry-delete-key-flag' is
3481 nil, or point is inside a literal then the function in the variable
3482 `backward-delete-char' is called."
3483 (interactive "P")
3484 (if (or (not prolog-hungry-delete-key-flag)
3485 arg
3486 (prolog-in-string-or-comment))
3487 (funcall 'backward-delete-char (prefix-numeric-value arg))
3488 (let ((here (point)))
3489 (skip-chars-backward " \t\n")
3490 (if (/= (point) here)
3491 (delete-region (point) here)
3492 (funcall 'backward-delete-char 1)
3493 ))))
3494
3495 ;; For XEmacs compatibility (suggested by Per Mildner)
3496 (put 'prolog-electric-delete 'pending-delete 'supersede)
3497
3498 (defun prolog-electric-if-then-else (arg)
3499 "If `prolog-electric-if-then-else-flag' is non-nil, indent if-then-else constructs.
3500 Bound to the >, ; and ( keys."
3501 (interactive "P")
3502 (self-insert-command (prefix-numeric-value arg))
3503 (if prolog-electric-if-then-else-flag (prolog-insert-spaces-after-paren)))
3504
3505 (defun prolog-electric-colon (arg)
3506 "If `prolog-electric-colon-flag' is non-nil, insert the electric `:' construct.
3507 That is, insert space (if appropriate), `:-' and newline if colon is pressed
3508 at the end of a line that starts in the first column (i.e., clause
3509 heads)."
3510 (interactive "P")
3511 (if (and prolog-electric-colon-flag
3512 (null arg)
3513 (eolp)
3514 ;(not (string-match "^\\s " (thing-at-point 'line))))
3515 (not (string-match "^\\(\\s \\|%\\)" (thing-at-point 'line))))
3516 (progn
3517 (unless (save-excursion (backward-char 1) (looking-at "\\s "))
3518 (insert " "))
3519 (insert ":-\n")
3520 (prolog-indent-line))
3521 (self-insert-command (prefix-numeric-value arg))))
3522
3523 (defun prolog-electric-dash (arg)
3524 "If `prolog-electric-dash-flag' is non-nil, insert the electric `-' construct.
3525 that is, insert space (if appropriate), `-->' and newline if dash is pressed
3526 at the end of a line that starts in the first column (i.e., DCG
3527 heads)."
3528 (interactive "P")
3529 (if (and prolog-electric-dash-flag
3530 (null arg)
3531 (eolp)
3532 ;(not (string-match "^\\s " (thing-at-point 'line))))
3533 (not (string-match "^\\(\\s \\|%\\)" (thing-at-point 'line))))
3534 (progn
3535 (unless (save-excursion (backward-char 1) (looking-at "\\s "))
3536 (insert " "))
3537 (insert "-->\n")
3538 (prolog-indent-line))
3539 (self-insert-command (prefix-numeric-value arg))))
3540
3541 (defun prolog-electric-dot (arg)
3542 "Insert dot and newline or a head of a new clause.
3543
3544 If `prolog-electric-dot-flag' is nil, then simply insert dot.
3545 Otherwise::
3546 When invoked at the end of nonempty line, insert dot and newline.
3547 When invoked at the end of an empty line, insert a recursive call to
3548 the current predicate.
3549 When invoked at the beginning of line, insert a head of a new clause
3550 of the current predicate.
3551
3552 When called with prefix argument ARG, insert just dot."
3553 (interactive "P")
3554 ;; Check for situations when the electricity should not be active
3555 (if (or (not prolog-electric-dot-flag)
3556 arg
3557 (prolog-in-string-or-comment)
3558 ;; Do not be electric in a floating point number or an operator
3559 (not
3560 (or
3561 ;; (re-search-backward
3562 ;; ######
3563 ;; "\\(^\\|[])}a-zA-Z_!'0-9]+\\)[ \t]*\\=" nil t)))
3564 (save-excursion
3565 (re-search-backward
3566 ;; "\\(^\\|[])}_!'0-9]+\\)[ \t]*\\=" nil t)))
3567 "\\(^\\|[])}_!'0-9]+\\)[ \t]*\\="
3568 nil t))
3569 (save-excursion
3570 (re-search-backward
3571 ;; "\\(^\\|[])}a-zA-Z]+\\)[ \t]*\\=" nil t)))
3572 (format "\\(^\\|[])}%s]+\\)[ \t]*\\="
3573 prolog-lower-case-string) ;FIXME: [:lower:]
3574 nil t))
3575 (save-excursion
3576 (re-search-backward
3577 ;; "\\(^\\|[])}a-zA-Z]+\\)[ \t]*\\=" nil t)))
3578 (format "\\(^\\|[])}%s]+\\)[ \t]*\\="
3579 prolog-upper-case-string) ;FIXME: [:upper:]
3580 nil t))
3581 )
3582 )
3583 ;; Do not be electric if inside a parenthesis pair.
3584 (not (= (prolog-region-paren-balance (prolog-clause-start) (point))
3585 0))
3586 )
3587 (funcall 'self-insert-command (prefix-numeric-value arg))
3588 (cond
3589 ;; Beginning of line
3590 ((bolp)
3591 (prolog-insert-predicate-template))
3592 ;; At an empty line with at least one whitespace
3593 ((save-excursion
3594 (beginning-of-line)
3595 (looking-at "[ \t]+$"))
3596 (prolog-insert-predicate-template)
3597 (when prolog-electric-dot-full-predicate-template
3598 (save-excursion
3599 (end-of-line)
3600 (insert ".\n"))))
3601 ;; Default
3602 (t
3603 (insert ".\n"))
3604 )))
3605
3606 (defun prolog-electric-underscore ()
3607 "Replace variable with an underscore.
3608 If `prolog-electric-underscore-flag' is non-nil and the point is
3609 on a variable then replace the variable with underscore and skip
3610 the following comma and whitespace, if any.
3611 If the point is not on a variable then insert underscore."
3612 (interactive)
3613 (if prolog-electric-underscore-flag
3614 (let (;start
3615 (case-fold-search nil)
3616 (oldp (point)))
3617 ;; ######
3618 ;;(skip-chars-backward "a-zA-Z_")
3619 (skip-chars-backward
3620 (format "%s%s_"
3621 ;; FIXME: Why not "a-zA-Z"?
3622 prolog-lower-case-string
3623 prolog-upper-case-string))
3624
3625 ;(setq start (point))
3626 (if (and (not (prolog-in-string-or-comment))
3627 ;; ######
3628 ;; (looking-at "\\<[_A-Z][a-zA-Z_0-9]*\\>"))
3629 (looking-at (format "\\<[_%s][%s%s_0-9]*\\>"
3630 ;; FIXME: Use [:upper:] and friends.
3631 prolog-upper-case-string
3632 prolog-lower-case-string
3633 prolog-upper-case-string)))
3634 (progn
3635 (replace-match "_")
3636 (skip-chars-forward ", \t\n"))
3637 (goto-char oldp)
3638 (self-insert-command 1))
3639 )
3640 (self-insert-command 1))
3641 )
3642
3643
3644 (defun prolog-find-term (functor arity &optional prefix)
3645 "Go to the position at the start of the next occurance of a term.
3646 The term is specified with FUNCTOR and ARITY. The optional argument
3647 PREFIX is the prefix of the search regexp."
3648 (let* (;; If prefix is not set then use the default "\\<"
3649 (prefix (if (not prefix)
3650 "\\<"
3651 prefix))
3652 (regexp (concat prefix functor))
3653 (i 1))
3654
3655 ;; Build regexp for the search if the arity is > 0
3656 (if (= arity 0)
3657 ;; Add that the functor must be at the end of a word. This
3658 ;; does not work if the arity is > 0 since the closing )
3659 ;; is not a word constituent.
3660 (setq regexp (concat regexp "\\>"))
3661 ;; Arity is > 0, add parens and commas
3662 (setq regexp (concat regexp "("))
3663 (while (< i arity)
3664 (setq regexp (concat regexp ".+,"))
3665 (setq i (1+ i)))
3666 (setq regexp (concat regexp ".+)")))
3667
3668 ;; Search, and return position
3669 (if (re-search-forward regexp nil t)
3670 (goto-char (match-beginning 0))
3671 (error "Term not found"))
3672 ))
3673
3674 (defun prolog-variables-to-anonymous (beg end)
3675 "Replace all variables within a region BEG to END by anonymous variables."
3676 (interactive "r")
3677 (save-excursion
3678 (let ((case-fold-search nil))
3679 (goto-char end)
3680 (while (re-search-backward "\\<[A-Z_][a-zA-Z_0-9]*\\>" beg t)
3681 (progn
3682 (replace-match "_")
3683 (backward-char)))
3684 )))
3685
3686
3687 (defun prolog-set-atom-regexps ()
3688 "Set the `prolog-atom-char-regexp' and `prolog-atom-regexp' variables.
3689 Must be called after `prolog-build-case-strings'."
3690 (setq prolog-atom-char-regexp
3691 (format "[%s%s0-9_$]"
3692 ;; FIXME: why not a-zA-Z?
3693 prolog-lower-case-string
3694 prolog-upper-case-string))
3695 (setq prolog-atom-regexp
3696 (format "[%s$]%s*"
3697 prolog-lower-case-string
3698 prolog-atom-char-regexp))
3699 )
3700
3701 (defun prolog-build-case-strings ()
3702 "Set `prolog-upper-case-string' and `prolog-lower-case-string'.
3703 Uses the current case-table for extracting the relevant information."
3704 (let ((up_string "")
3705 (low_string ""))
3706 ;; Use `map-char-table' if it is defined. Otherwise enumerate all
3707 ;; numbers between 0 and 255. `map-char-table' is probably safer.
3708 ;;
3709 ;; `map-char-table' causes problems under Emacs 23.0.0.1, the
3710 ;; while loop seems to do its job well (Ryszard Szopa)
3711 ;;
3712 ;;(if (and (not (featurep 'xemacs))
3713 ;; (fboundp 'map-char-table))
3714 ;; (map-char-table
3715 ;; (lambda (key value)
3716 ;; (cond
3717 ;; ((and
3718 ;; (eq (prolog-int-to-char key) (downcase key))
3719 ;; (eq (prolog-int-to-char key) (upcase key)))
3720 ;; ;; Do nothing if upper and lower case are the same
3721 ;; )
3722 ;; ((eq (prolog-int-to-char key) (downcase key))
3723 ;; ;; The char is lower case
3724 ;; (setq low_string (format "%s%c" low_string key)))
3725 ;; ((eq (prolog-int-to-char key) (upcase key))
3726 ;; ;; The char is upper case
3727 ;; (setq up_string (format "%s%c" up_string key)))
3728 ;; ))
3729 ;; (current-case-table))
3730 ;; `map-char-table' was undefined.
3731 (let ((key 0))
3732 (while (< key 256)
3733 (cond
3734 ((and
3735 (eq (prolog-int-to-char key) (downcase key))
3736 (eq (prolog-int-to-char key) (upcase key)))
3737 ;; Do nothing if upper and lower case are the same
3738 )
3739 ((eq (prolog-int-to-char key) (downcase key))
3740 ;; The char is lower case
3741 (setq low_string (format "%s%c" low_string key)))
3742 ((eq (prolog-int-to-char key) (upcase key))
3743 ;; The char is upper case
3744 (setq up_string (format "%s%c" up_string key)))
3745 )
3746 (setq key (1+ key))))
3747 ;; )
3748 ;; The strings are single-byte strings
3749 (setq prolog-upper-case-string (prolog-dash-letters up_string))
3750 (setq prolog-lower-case-string (prolog-dash-letters low_string))
3751 ))
3752
3753 ;(defun prolog-regexp-dash-continuous-chars (chars)
3754 ; (let ((ints (mapcar #'prolog-char-to-int (string-to-list chars)))
3755 ; (beg 0)
3756 ; (end 0))
3757 ; (if (null ints)
3758 ; chars
3759 ; (while (and (< (+ beg 1) (length chars))
3760 ; (not (or (= (+ (nth beg ints) 1) (nth (+ beg 1) ints))
3761 ; (= (nth beg ints) (nth (+ beg 1) ints)))))
3762 ; (setq beg (+ beg 1)))
3763 ; (setq beg (+ beg 1)
3764 ; end beg)
3765 ; (while (and (< (+ end 1) (length chars))
3766 ; (or (= (+ (nth end ints) 1) (nth (+ end 1) ints))
3767 ; (= (nth end ints) (nth (+ end 1) ints))))
3768 ; (setq end (+ end 1)))
3769 ; (if (equal (substring chars end) "")
3770 ; (substring chars 0 beg)
3771 ; (concat (substring chars 0 beg) "-"
3772 ; (prolog-regexp-dash-continuous-chars (substring chars end))))
3773 ; )))
3774
3775 (defun prolog-ints-intervals (ints)
3776 "Return a list of intervals (from . to) covering INTS."
3777 (when ints
3778 (setq ints (sort ints '<))
3779 (let ((prev (car ints))
3780 (interval-start (car ints))
3781 intervals)
3782 (while ints
3783 (let ((next (car ints)))
3784 (when (> next (1+ prev)) ; start of new interval
3785 (setq intervals (cons (cons interval-start prev) intervals))
3786 (setq interval-start next))
3787 (setq prev next)
3788 (setq ints (cdr ints))))
3789 (setq intervals (cons (cons interval-start prev) intervals))
3790 (reverse intervals))))
3791
3792 (defun prolog-dash-letters (string)
3793 "Return a condensed regexp covering all letters in STRING."
3794 (let ((intervals (prolog-ints-intervals (mapcar #'prolog-char-to-int
3795 (string-to-list string))))
3796 codes)
3797 (while intervals
3798 (let* ((i (car intervals))
3799 (from (car i))
3800 (to (cdr i))
3801 (c (cond ((= from to) `(,from))
3802 ((= (1+ from) to) `(,from ,to))
3803 (t `(,from ?- ,to)))))
3804 (setq codes (cons c codes)))
3805 (setq intervals (cdr intervals)))
3806 (apply 'concat (reverse codes))))
3807
3808 ;(defun prolog-condense-character-sets (regexp)
3809 ; "Condense adjacent characters in character sets of REGEXP."
3810 ; (let ((next -1))
3811 ; (while (setq next (string-match "\\[\\(.*?\\)\\]" regexp (1+ next)))
3812 ; (setq regexp (replace-match (prolog-dash-letters (match-string 1 regexp))
3813 ; t t regexp 1))))
3814 ; regexp)
3815
3816 ;; GNU Emacs compatibility: GNU Emacs does not differentiate between
3817 ;; ints and chars, or at least these two are interchangeable.
3818 (defalias 'prolog-int-to-char
3819 (if (fboundp 'int-to-char) #'int-to-char #'identity))
3820
3821 (defalias 'prolog-char-to-int
3822 (if (fboundp 'char-to-int) #'char-to-int #'identity))
3823 \f
3824 ;;-------------------------------------------------------------------
3825 ;; Menu stuff (both for the editing buffer and for the inferior
3826 ;; prolog buffer)
3827 ;;-------------------------------------------------------------------
3828
3829 (unless (fboundp 'region-exists-p)
3830 (defun region-exists-p ()
3831 "Non-nil iff the mark is set. Lobotomized version for Emacsen that do not provide their own."
3832 (mark)))
3833
3834
3835 ;; GNU Emacs ignores `easy-menu-add' so the order in which the menus
3836 ;; are defined _is_ important!
3837
3838 (easy-menu-define
3839 prolog-menu-help (list prolog-mode-map prolog-inferior-mode-map)
3840 "Help menu for the Prolog mode."
3841 ;; FIXME: Does it really deserve a whole menu to itself?
3842 `(,(if (featurep 'xemacs) "Help"
3843 ;; Not sure it's worth the trouble. --Stef
3844 ;; (add-to-list 'menu-bar-final-items
3845 ;; (easy-menu-intern "Prolog-Help"))
3846 "Prolog-help")
3847 ["On predicate" prolog-help-on-predicate prolog-help-function-i]
3848 ["Apropos" prolog-help-apropos (eq prolog-system 'swi)]
3849 "---"
3850 ["Describe mode" describe-mode t]))
3851
3852 (easy-menu-define
3853 prolog-edit-menu-runtime prolog-mode-map
3854 "Runtime Prolog commands available from the editing buffer"
3855 ;; FIXME: Don't use a whole menu for just "Run Mercury". --Stef
3856 `("System"
3857 ;; Runtime menu name.
3858 ,@(unless (featurep 'xemacs)
3859 '(:label (cond ((eq prolog-system 'eclipse) "ECLiPSe")
3860 ((eq prolog-system 'mercury) "Mercury")
3861 (t "System"))))
3862
3863 ;; Consult items, NIL for mercury.
3864 ["Consult file" prolog-consult-file
3865 :included (not (eq prolog-system 'mercury))]
3866 ["Consult buffer" prolog-consult-buffer
3867 :included (not (eq prolog-system 'mercury))]
3868 ["Consult region" prolog-consult-region :active (region-exists-p)
3869 :included (not (eq prolog-system 'mercury))]
3870 ["Consult predicate" prolog-consult-predicate
3871 :included (not (eq prolog-system 'mercury))]
3872
3873 ;; Compile items, NIL for everything but SICSTUS.
3874 ,(if (featurep 'xemacs) "---"
3875 ["---" nil :included (eq prolog-system 'sicstus)])
3876 ["Compile file" prolog-compile-file
3877 :included (eq prolog-system 'sicstus)]
3878 ["Compile buffer" prolog-compile-buffer
3879 :included (eq prolog-system 'sicstus)]
3880 ["Compile region" prolog-compile-region :active (region-exists-p)
3881 :included (eq prolog-system 'sicstus)]
3882 ["Compile predicate" prolog-compile-predicate
3883 :included (eq prolog-system 'sicstus)]
3884
3885 ;; Debug items, NIL for Mercury.
3886 ,(if (featurep 'xemacs) "---"
3887 ["---" nil :included (not (eq prolog-system 'mercury))])
3888 ;; FIXME: Could we use toggle or radio buttons? --Stef
3889 ["Debug" prolog-debug-on :included (not (eq prolog-system 'mercury))]
3890 ["Debug off" prolog-debug-off
3891 ;; In SICStus, these are pairwise disjunctive,
3892 ;; so it's enough with a single "off"-command
3893 :included (not (memq prolog-system '(mercury sicstus)))]
3894 ["Trace" prolog-trace-on :included (not (eq prolog-system 'mercury))]
3895 ["Trace off" prolog-trace-off
3896 :included (not (memq prolog-system '(mercury sicstus)))]
3897 ["Zip" prolog-zip-on :included (and (eq prolog-system 'sicstus)
3898 (prolog-atleast-version '(3 . 7)))]
3899 ["All debug off" prolog-debug-off
3900 :included (eq prolog-system 'sicstus)]
3901 ["Source level debugging"
3902 prolog-toggle-sicstus-sd
3903 :included (and (eq prolog-system 'sicstus)
3904 (prolog-atleast-version '(3 . 7)))
3905 :style toggle
3906 :selected prolog-use-sicstus-sd]
3907
3908 "---"
3909 ["Run" run-prolog
3910 :suffix (cond ((eq prolog-system 'eclipse) "ECLiPSe")
3911 ((eq prolog-system 'mercury) "Mercury")
3912 (t "Prolog"))]))
3913
3914 (easy-menu-define
3915 prolog-edit-menu-insert-move prolog-mode-map
3916 "Commands for Prolog code manipulation."
3917 '("Prolog"
3918 ["Comment region" comment-region (region-exists-p)]
3919 ["Uncomment region" prolog-uncomment-region (region-exists-p)]
3920 ["Add comment/move to comment" indent-for-comment t]
3921 ["Convert variables in region to '_'" prolog-variables-to-anonymous
3922 :active (region-exists-p) :included (not (eq prolog-system 'mercury))]
3923 "---"
3924 ["Insert predicate template" prolog-insert-predicate-template t]
3925 ["Insert next clause head" prolog-insert-next-clause t]
3926 ["Insert predicate spec" prolog-insert-predspec t]
3927 ["Insert module modeline" prolog-insert-module-modeline t]
3928 "---"
3929 ["Beginning of clause" prolog-beginning-of-clause t]
3930 ["End of clause" prolog-end-of-clause t]
3931 ["Beginning of predicate" prolog-beginning-of-predicate t]
3932 ["End of predicate" prolog-end-of-predicate t]
3933 "---"
3934 ["Indent line" prolog-indent-line t]
3935 ["Indent region" indent-region (region-exists-p)]
3936 ["Indent predicate" prolog-indent-predicate t]
3937 ["Indent buffer" prolog-indent-buffer t]
3938 ["Align region" align (region-exists-p)]
3939 "---"
3940 ["Mark clause" prolog-mark-clause t]
3941 ["Mark predicate" prolog-mark-predicate t]
3942 ["Mark paragraph" mark-paragraph t]
3943 ;;"---"
3944 ;;["Fontify buffer" font-lock-fontify-buffer t]
3945 ))
3946
3947 (defun prolog-menu ()
3948 "Add the menus for the Prolog editing buffers."
3949
3950 (easy-menu-add prolog-edit-menu-insert-move)
3951 (easy-menu-add prolog-edit-menu-runtime)
3952
3953 ;; Add predicate index menu
3954 (set (make-local-variable 'imenu-create-index-function)
3955 'imenu-default-create-index-function)
3956 ;;Milan (this has problems with object methods...) ###### Does it? (Stefan)
3957 (setq imenu-prev-index-position-function 'prolog-beginning-of-predicate)
3958 (setq imenu-extract-index-name-function 'prolog-get-predspec)
3959
3960 (if (and prolog-imenu-flag
3961 (< (count-lines (point-min) (point-max)) prolog-imenu-max-lines))
3962 (imenu-add-to-menubar "Predicates"))
3963
3964 (easy-menu-add prolog-menu-help))
3965
3966 (easy-menu-define
3967 prolog-inferior-menu-all prolog-inferior-mode-map
3968 "Menu for the inferior Prolog buffer."
3969 `("Prolog"
3970 ;; Runtime menu name.
3971 ,@(unless (featurep 'xemacs)
3972 '(:label (cond ((eq prolog-system 'eclipse) "ECLiPSe")
3973 ((eq prolog-system 'mercury) "Mercury")
3974 (t "Prolog"))))
3975
3976 ;; Debug items, NIL for Mercury.
3977 ,(if (featurep 'xemacs) "---"
3978 ["---" nil :included (not (eq prolog-system 'mercury))])
3979 ;; FIXME: Could we use toggle or radio buttons? --Stef
3980 ["Debug" prolog-debug-on :included (not (eq prolog-system 'mercury))]
3981 ["Debug off" prolog-debug-off
3982 ;; In SICStus, these are pairwise disjunctive,
3983 ;; so it's enough with a single "off"-command
3984 :included (not (memq prolog-system '(mercury sicstus)))]
3985 ["Trace" prolog-trace-on :included (not (eq prolog-system 'mercury))]
3986 ["Trace off" prolog-trace-off
3987 :included (not (memq prolog-system '(mercury sicstus)))]
3988 ["Zip" prolog-zip-on :included (and (eq prolog-system 'sicstus)
3989 (prolog-atleast-version '(3 . 7)))]
3990 ["All debug off" prolog-debug-off
3991 :included (eq prolog-system 'sicstus)]
3992 ["Source level debugging"
3993 prolog-toggle-sicstus-sd
3994 :included (and (eq prolog-system 'sicstus)
3995 (prolog-atleast-version '(3 . 7)))
3996 :style toggle
3997 :selected prolog-use-sicstus-sd]
3998
3999 ;; Runtime.
4000 "---"
4001 ["Interrupt Prolog" comint-interrupt-subjob t]
4002 ["Quit Prolog" comint-quit-subjob t]
4003 ["Kill Prolog" comint-kill-subjob t]))
4004
4005
4006 (defun prolog-inferior-menu ()
4007 "Create the menus for the Prolog inferior buffer.
4008 This menu is dynamically created because one may change systems during
4009 the life of an Emacs session."
4010 (easy-menu-add prolog-inferior-menu-all)
4011 (easy-menu-add prolog-menu-help))
4012
4013 (defun prolog-mode-version ()
4014 "Echo the current version of Prolog mode in the minibuffer."
4015 (interactive)
4016 (message "Using Prolog mode version %s" prolog-mode-version))
4017
4018 (provide 'prolog)
4019
4020 ;;; prolog.el ends here