Use line-end-position rather than end-of-line, etc.
[bpt/emacs.git] / lisp / progmodes / prolog.el
index 5bca350..dd17c4b 100644 (file)
@@ -31,6 +31,7 @@
 
 (defvar comint-prompt-regexp)
 (defvar comint-process-echoes)
+(require 'smie)
 
 (defgroup prolog nil
   "Major mode for editing and running Prolog under Emacs."
@@ -98,6 +99,86 @@ When nil, send actual operating system end of file."
 (defvar prolog-mode-abbrev-table nil)
 (define-abbrev-table 'prolog-mode-abbrev-table ())
 
+(defun prolog-smie-forward-token ()
+  (forward-comment (point-max))
+  (buffer-substring-no-properties
+   (point)
+   (progn (cond
+           ((looking-at "[!;]") (forward-char 1))
+           ((not (zerop (skip-chars-forward "#&*+-./:<=>?@\\^`~"))))
+           ((not (zerop (skip-syntax-forward "w_'"))))
+           ;; In case of non-ASCII punctuation.
+           ((not (zerop (skip-syntax-forward ".")))))
+          (point))))
+
+(defun prolog-smie-backward-token ()
+  (forward-comment (- (point-max)))
+  (buffer-substring-no-properties
+   (point)
+   (progn (cond
+           ((memq (char-before) '(?! ?\;)) (forward-char -1))
+           ((not (zerop (skip-chars-backward "#&*+-./:<=>?@\\^`~"))))
+           ((not (zerop (skip-syntax-backward "w_'"))))
+           ;; In case of non-ASCII punctuation.
+           ((not (zerop (skip-syntax-backward ".")))))
+          (point))))
+
+(defconst prolog-smie-op-levels
+  ;; Rather than construct the operator levels table from the BNF,
+  ;; we directly provide the operator precedences from GNU Prolog's
+  ;; manual (7.14.10 op/3).  The only problem is that GNU Prolog's
+  ;; manual uses precedence levels in the opposite sense (higher
+  ;; numbers bind less tightly) than SMIE, so we use negative numbers.
+  '(("." -10000 -10000)
+    (":-" -1200 -1200)
+    ("-->" -1200 -1200)
+    (";" -1100 -1100)
+    ("->" -1050 -1050)
+    ("," -1000 -1000)
+    ("\\+" -900 -900)
+    ("=" -700 -700)
+    ("\\=" -700 -700)
+    ("=.." -700 -700)
+    ("==" -700 -700)
+    ("\\==" -700 -700)
+    ("@<" -700 -700)
+    ("@=<" -700 -700)
+    ("@>" -700 -700)
+    ("@>=" -700 -700)
+    ("is" -700 -700)
+    ("=:=" -700 -700)
+    ("=\\=" -700 -700)
+    ("<" -700 -700)
+    ("=<" -700 -700)
+    (">" -700 -700)
+    (">=" -700 -700)
+    (":" -600 -600)
+    ("+" -500 -500)
+    ("-" -500 -500)
+    ("/\\" -500 -500)
+    ("\\/" -500 -500)
+    ("*" -400 -400)
+    ("/" -400 -400)
+    ("//" -400 -400)
+    ("rem" -400 -400)
+    ("mod" -400 -400)
+    ("<<" -400 -400)
+    (">>" -400 -400)
+    ("**" -200 -200)
+    ("^" -200 -200)
+    ;; Prefix
+    ;; ("+" 200 200)
+    ;; ("-" 200 200)
+    ;; ("\\" 200 200)
+    )
+  "Precedence levels of infix operators.")
+
+(defun prolog-smie-rules (kind token)
+  (pcase (cons kind token)
+    (`(:elem . basic) prolog-indent-width)
+    (`(:after . ".") 0) ;; To work around smie-closer-alist.
+    (`(:after . ,(or `":-" `"->")) prolog-indent-width)))
+
 (defun prolog-mode-variables ()
   (make-local-variable 'paragraph-separate)
   (setq paragraph-separate (concat "%%\\|$\\|" page-delimiter)) ;'%%..'
@@ -105,8 +186,17 @@ When nil, send actual operating system end of file."
   (setq paragraph-ignore-fill-prefix t)
   (make-local-variable 'imenu-generic-expression)
   (setq imenu-generic-expression '((nil "^\\sw+" 0)))
-  (make-local-variable 'indent-line-function)
-  (setq indent-line-function 'prolog-indent-line)
+
+  ;; Setup SMIE.
+  (smie-setup prolog-smie-op-levels #'prolog-smie-rules
+              :forward-token #'prolog-smie-forward-token
+              :backward-token #'prolog-smie-backward-token)
+  (set (make-local-variable 'smie-blink-matching-triggers) '(?.))
+  (set (make-local-variable 'smie-closer-alist) '((t . ".")))
+  (add-hook 'post-self-insert-hook #'smie-blink-matching-open 'append 'local)
+  ;; There's no real closer in Prolog anyway.
+  (set (make-local-variable 'smie-blink-matching-inners) t)
+
   (make-local-variable 'comment-start)
   (setq comment-start "%")
   (make-local-variable 'comment-start-skip)
@@ -122,7 +212,7 @@ When nil, send actual operating system end of file."
     (define-key map "\C-c\C-l" 'inferior-prolog-load-file)
     (define-key map "\C-c\C-z" 'switch-to-prolog)
     map))
+
 (easy-menu-define prolog-mode-menu prolog-mode-map "Menu for Prolog mode."
   ;; Mostly copied from scheme-mode's menu.
   ;; Not tremendously useful, but it's a start.
@@ -149,69 +239,10 @@ if that value is non-nil."
                              nil nil nil
                              beginning-of-line)))
 
-(defun prolog-indent-line ()
-  "Indent current line as Prolog code.
-With argument, indent any additional lines of the same clause
-rigidly along with this one (not yet)."
-  (interactive "p")
-  (let ((indent (prolog-indent-level))
-       (pos (- (point-max) (point))))
-    (beginning-of-line)
-    (indent-line-to indent)
-    (if (> (- (point-max) pos) (point))
-       (goto-char (- (point-max) pos)))))
-
-(defun prolog-indent-level ()
-  "Compute Prolog indentation level."
-  (save-excursion
-    (beginning-of-line)
-    (skip-chars-forward " \t")
-    (cond
-     ((looking-at "%%%") 0)            ;Large comment starts
-     ((looking-at "%[^%]") comment-column) ;Small comment starts
-     ((bobp) 0)                                ;Beginning of buffer
-     (t
-      (let ((empty t) ind more less)
-       (if (looking-at ")")
-           (setq less t)               ;Find close
-         (setq less nil))
-       ;; See previous indentation
-       (while empty
-         (forward-line -1)
-         (beginning-of-line)
-         (if (bobp)
-             (setq empty nil)
-           (skip-chars-forward " \t")
-           (if (not (or (looking-at "%[^%]") (looking-at "\n")))
-               (setq empty nil))))
-       (if (bobp)
-           (setq ind 0)                ;Beginning of buffer
-         (setq ind (current-column)))  ;Beginning of clause
-       ;; See its beginning
-       (if (looking-at "%%[^%]")
-           ind
-         ;; Real prolog code
-         (if (looking-at "(")
-             (setq more t)             ;Find open
-           (setq more nil))
-         ;; See its tail
-         (end-of-prolog-clause)
-         (or (bobp) (forward-char -1))
-         (cond ((looking-at "[,(;>]")
-                (if (and more (looking-at "[^,]"))
-                    (+ ind prolog-indent-width) ;More indentation
-                  (max tab-width ind))) ;Same indentation
-               ((looking-at "-") tab-width) ;TAB
-               ((or less (looking-at "[^.]"))
-                (max (- ind prolog-indent-width) 0)) ;Less indentation
-               (t 0))                  ;No indentation
-         )))
-     )))
-
 (defun end-of-prolog-clause ()
   "Go to end of clause in this line."
   (beginning-of-line 1)
-  (let* ((eolpos (save-excursion (end-of-line) (point))))
+  (let* ((eolpos (line-end-position)))
     (if (re-search-forward comment-start-skip eolpos 'move)
        (goto-char (match-beginning 0)))
     (skip-chars-backward " \t")))
@@ -403,5 +434,4 @@ If COMPILE (prefix arg) is not nil, use compile mode rather than consult mode."
 
 (provide 'prolog)
 
-;; arch-tag: f3ec6748-1272-4ab6-8826-c50cb1607636
 ;;; prolog.el ends here