(add-to-list 'auto-mode-alist '("\\.plt\\'" . sweep-mode))
#+end_src
+** Indentation
+:PROPERTIES:
+:CUSTOM_ID: indentation
+:END:
+
+#+CINDEX: indentation
+
+In =sweep-mode= buffers, the appropriate indentation for each line is
+determined by a bespoke /indentation engine/. The indentation engine
+analyses the syntactic context of a given line and determines the
+appropriate indentation to apply based on a set of rules.
+
+#+FINDEX: sweep-indent-line
+The entry point of the indentation engine is the function
+=sweep-indent-line= which takes no arguments and indents that line at
+point. =sweep-mode= supports the standard Emacs interface for
+indentation by arranging for =sweep-indent-line= to be called whenever a
+line should be indented, notably after pressing =TAB=. For more a full
+description of the available commands and options that pertain to
+indentation, see [[info:emacs#Indentation][Indentation in the Emacs manual]].
+
+*** Indentation rules
+:PROPERTIES:
+:CUSTOM_ID: indentation-rules
+:END:
+
+Lines in =sweep-mode= buffers are indented according to the following
+rules:
+
+1. If the current line starts inside a string or a multi-line comment,
+ do not indent.
+2. If the current line starts with a top term, do not indent.
+3. If the current line starts with a closing parenthesis and the
+ matching opening parenthesis is part of a functor, indent to the
+ column of the opening parenthesis if any arguments appear on the
+ same line as the functor, otherwise indent to the start of the
+ functor.
+
+ This rule yields the following layouts:
+
+ #+begin_src prolog
+ some_functor(
+ some_arg
+ ).
+
+ some_functor( some_arg
+ ).
+ #+end_src
+
+#+VINDEX: sweep-indent-offset
+4. If the current line is the first non-comment line of a clause body,
+ indent to the starting column of the head term plus the value of
+ the user option =sweep-indent-offset= (by default, four extra
+ columns).
+
+ As an example, this rule yields the following layouts when
+ =sweep-indent-offset= is set to the default value of four columns:
+
+ #+begin_src prolog
+ some_functor(arg1, arg2) :-
+ body_term.
+
+ asserta( some_functor(arg1, arg2) :-
+ body_term
+ ).
+ #+end_src
+
+5. If the current line starts with the right hand side operand of an
+ infix operator, indent to the starting column of the first operand
+ in the chain of infix operators of the same precedence.
+
+ This rule yields the following layouts:
+
+ #+begin_src prolog
+ head :- body1, body2, body3,
+ body4, body5.
+
+ A is 1 * 2 ^ 3 * 4 *
+ 5.
+
+ A is 1 * 2 + 3 * 4 *
+ 5.
+ #+end_src
+
+6. If the last non-comment line ends with a functor and its opening
+ parenthesis, indent to the starting column of the functor plus
+ =sweep-indent-offset=.
+
+ This rule yields the following layout:
+
+ #+begin_src prolog
+ some_functor(
+ arg1, ...
+ #+end_src
+
+7. If the last non-comment line ends with a prefix operator, indent to
+ starting column of the operator plus =sweep-indent-offset=.
+
+ This rule yields the following layout:
+
+ #+begin_src prolog
+ :- multifile
+ predicate/3.
+ #+end_src
+
** Semantic highlighting
:PROPERTIES:
:CUSTOM_ID: semantic-highlighting
"Tests indentation rules."
(sweep-test-indentation
"
+some_functor(
+arg1,
+arg2,
+)."
+ "
+some_functor(
+ arg1,
+ arg2,
+)."
+ )
+ (sweep-test-indentation
+ "
+asserta( some_functor(arg1, arg2) :-
+body_term
+).
+"
+ "
+asserta( some_functor(arg1, arg2) :-
+ body_term
+ ).
+"
+ )
+ (sweep-test-indentation
+ "
+:- module(spam, [ foo,
+bar,
+baz
+]
+).
+"
+ "
+:- module(spam, [ foo,
+ bar,
+ baz
+ ]
+ ).
+"
+ )
+ (sweep-test-indentation
+ "
+:- module(spam, [
+foo,
+bar,
+baz
+]
+).
+"
+ "
+:- module(spam, [
+ foo,
+ bar,
+ baz
+ ]
+ ).
+"
+ )
+ (sweep-test-indentation
+ "
+[
+ ].
+"
+ "
+[
+].
+"
+ )
+ (sweep-test-indentation
+ "
+:-
+use_module(foo),
+use_module(bar).
+"
+ "
+:-
+ use_module(foo),
+ use_module(bar).
+"
+ )
+ (sweep-test-indentation
+ "
colourise_declaration(Module:PI, _, TB,
term_position(_,_,QF,QT,[PM,PG])) :-
atom(Module), nonvar(PI), PI = Name/Arity,
;; Maintainer: Eshel Yaron <me(at)eshelyaron(dot)com>
;; Keywords: prolog languages extensions
;; URL: https://git.sr.ht/~eshel/sweep
-;; Package-Version: 0.3.0
+;; Package-Version: 0.3.1
;; Package-Requires: ((emacs "28"))
;; This file is NOT part of GNU Emacs.
"SWI-Prolog Embedded in Emacs."
:group 'prolog)
+(defcustom sweep-indent-offset 4
+ "Number of columns to indent lines with in `sweep-mode' buffers."
+ :package-version '((sweep . "0.3.1"))
+ :type 'integer
+ :group 'sweep)
+
(defcustom sweep-colourise-buffer-on-idle t
"If non-nil, update highlighting of `sweep-mode' buffers on idle."
:package-version '((sweep . "0.2.0"))
(defun sweep-indent-line-after-functor (fbeg _fend)
(save-excursion
(goto-char fbeg)
- (+ (current-column) 4)))
+ (+ (current-column) sweep-indent-offset)))
(defun sweep-indent-line-after-open (fbeg _fend)
(save-excursion
(goto-char fbeg)
- (+ (current-column) 4)))
+ (+ (current-column) sweep-indent-offset)))
(defun sweep-indent-line-after-prefix (fbeg _fend _pre)
(save-excursion
(save-excursion
(goto-char fbeg)
(sweep-backward-term 1200)
- (+ (current-column) 4)))
+ (+ (current-column) sweep-indent-offset)))
(defun sweep-indent-line-after-infix (fbeg _fend pre)
(save-excursion
(current-column)))
(defun sweep-indent-line ()
+ "Indent the current line in a `sweep-mode' buffer."
(interactive)
(let ((pos (- (point-max) (point))))
(back-to-indentation)
(let ((indent (if (nth 8 (syntax-ppss))
'noindent
- (pcase (sweep-last-token-boundaries)
- ('nil 'noindent)
- (`(functor ,lbeg ,lend)
- (sweep-indent-line-after-functor lbeg lend))
- (`(open ,lbeg ,lend)
- (sweep-indent-line-after-open lbeg lend))
- (`(symbol ,lbeg ,lend)
- (let ((sym (buffer-substring-no-properties lbeg lend)))
- (cond
- ((pcase (sweep-op-prefix-precedence sym)
- ('nil (sweep-indent-line-after-term))
- (pre (sweep-indent-line-after-prefix lbeg lend pre)))))))
- (`(operator ,lbeg ,lend)
- (let ((op (buffer-substring-no-properties lbeg lend)))
- (cond
- ((string= op ".") 'noindent)
- ((pcase (sweep-op-infix-precedence op)
- ('nil nil)
- (1200 (sweep-indent-line-after-neck lbeg lend))
- (pre (sweep-indent-line-after-infix lbeg lend pre))))
- ((pcase (sweep-op-prefix-precedence op)
- ('nil nil)
- (pre (sweep-indent-line-after-prefix lbeg lend pre)))))))
- (`(,_ltyp ,_lbeg ,_lend)
- (sweep-indent-line-after-term))))))
+ (if-let ((open (and (= (char-syntax (char-after)) ?\))
+ (nth 1 (syntax-ppss)))))
+ (save-excursion
+ (goto-char open)
+ (when (or (= (char-syntax (char-before)) ?w)
+ (= (char-syntax (char-before)) ?_))
+ (when (save-excursion
+ (forward-char)
+ (skip-syntax-forward " " (line-end-position))
+ (eolp))
+ (skip-syntax-backward "w_")))
+ (current-column))
+ (pcase (sweep-last-token-boundaries)
+ ('nil 'noindent)
+ (`(functor ,lbeg ,lend)
+ (sweep-indent-line-after-functor lbeg lend))
+ (`(open ,lbeg ,lend)
+ (sweep-indent-line-after-open lbeg lend))
+ (`(symbol ,lbeg ,lend)
+ (let ((sym (buffer-substring-no-properties lbeg lend)))
+ (cond
+ ((pcase (sweep-op-prefix-precedence sym)
+ ('nil (sweep-indent-line-after-term))
+ (pre (sweep-indent-line-after-prefix lbeg lend pre)))))))
+ (`(operator ,lbeg ,lend)
+ (let ((op (buffer-substring-no-properties lbeg lend)))
+ (cond
+ ((string= op ".") 'noindent)
+ ((pcase (sweep-op-infix-precedence op)
+ ('nil nil)
+ (1200 (sweep-indent-line-after-neck lbeg lend))
+ (pre (sweep-indent-line-after-infix lbeg lend pre))))
+ ((pcase (sweep-op-prefix-precedence op)
+ ('nil nil)
+ (pre (sweep-indent-line-after-prefix lbeg lend pre)))))))
+ (`(,_ltyp ,_lbeg ,_lend)
+ (sweep-indent-line-after-term)))))))
(when (numberp indent)
(unless (= indent (current-column))
(combine-after-change-calls