* sweeprolog.el (sweeprolog-dependency-directive): New user option.
Determines which directive to use in...
(sweeprolog-update-dependencies): Fix edge cases in finding where to
insert new directives. Optionally infer directive to add based on
current buffer contents.
* README.org (Managing Dependencies): Update section.
- Key: C-c C-u (sweeprolog-update-dependencies) :: Add explicit
dependencies for implicitly autoloaded predicates in the current
buffer.
+#+VINDEX: sweeprolog-dependency-directive
+- User Option: sweeprolog-dependency-directive :: Determines which
+ Prolog directive to use in ~sweeprolog-update-dependencies~ when
+ adding new directives. The value of this user option is one of the
+ symbols ~use-module~, ~autoload~ or ~infer~. If it is ~use-module~,
+ ~sweeprolog-update-dependencies~ adds ~use_module/2~ directives,
+ ~autoload~ means to add ~autoload/2~ directives, and ~infer~ says to infer
+ which directive to use based on the existing dependency directives
+ in the buffer, if any. Defaults to ~infer~.
#+VINDEX: sweeprolog-note-implicit-autoloads
- User Option: sweeprolog-note-implicit-autoloads :: If non-nil, have
Flymake complain about implicitly autoloaded predicates in
~sweeprolog-mode~ buffers.
-This command analyzes the current buffer and adds or updates
-~autoload/2~ and ~use_module/2~ as needed.
+The command ~sweeprolog-update-dependencies~, bound to ~C-c C-u~, analyzes
+the current buffer and adds or updates ~autoload/2~ and ~use_module/2~ as
+needed.
+
+When this command adds a new directive, rather than updating an
+existing one, it can use either ~autoload/2~ or ~use_module/2~ to declare
+the new dependency based on the value of the user option
+~sweeprolog-dependency-directive~. If you set this option is to
+~use-module~, new dependencies use the ~use_module/2~ directive. If it's
+~autoload~, new dependencies use ~autoload/2~. If it's ~infer~, as it is by
+default, new dependencies use ~autoload/2~ unless the buffer already
+contains dependency directives and they are all ~use_module/2~
+directives, in which case they also use ~use_module/2~.
By default, when Flymake integration is enabled (see [[#diagnostics][Examining
diagnostics]]), calls to implicitly autoloaded predicates are marked
sweep_handle_fragment(Offset, comment(Kind), Beg, Len) :-
!,
Start is Beg + Offset,
- asserta(sweep_current_comment(Kind, Start, Len)).
+ assertz(sweep_current_comment(Kind, Start, Len)).
sweep_handle_fragment(Offset, Col, Beg, Len) :-
sweep_handle_fragment_(Offset, Col, Beg, Len).
"))))
(ert-deftest update-dependencies-no-autoload ()
- "Tests making adding a use_module/1 directive."
+ "Tests adding a use_module/2 directive."
(let ((temp (make-temp-file "sweeprolog-test"
nil
"pl"
(should (string= (buffer-string)
"
:- module(foo, [bar/1]).
+:- use_module(library(arithmetic), [arithmetic_function/1]).
/** <module> Foo
*/
-:- use_module(library(arithmetic), [ arithmetic_function/1
- ]).
-
bar(X) :- arithmetic_function(X).
"))))
"
))))
+(ert-deftest update-dependencies-without-inference ()
+ "Tests setting `sweeprolog-dependency-directive' to `autoload'."
+ (let ((temp (make-temp-file "sweeprolog-test"
+ nil
+ "pl"
+ "
+:- module(foo, [bar/1]).
+
+/** <module> Foo
+
+*/
+
+:- use_module(library(lists), [ member/2
+ ]).
+
+bar(X) :- member(X, [1,2,3]).
+bar(X) :- maplist(X, [1,2,3]).
+"
+ )))
+ (find-file-literally temp)
+ (sweeprolog-mode)
+ (let ((sweeprolog-dependency-directive 'autoload))
+ (call-interactively #'sweeprolog-update-dependencies))
+ (should (string= (buffer-string)
+ "
+:- module(foo, [bar/1]).
+
+/** <module> Foo
+
+*/
+
+:- use_module(library(lists), [ member/2
+ ]).
+:- autoload(library(apply), [maplist/2]).
+
+bar(X) :- member(X, [1,2,3]).
+bar(X) :- maplist(X, [1,2,3]).
+"
+ ))))
+
+(ert-deftest update-dependencies-without-inference-2 ()
+ "Tests setting `sweeprolog-dependency-directive' to `use-module'."
+ (let ((temp (make-temp-file "sweeprolog-test"
+ nil
+ "pl"
+ "
+:- module(foo, [bar/1]).
+
+/** <module> Foo
+
+*/
+
+bar(X) :- member(X, [1,2,3]).
+bar(X) :- maplist(X, [1,2,3]).
+"
+ )))
+ (find-file-literally temp)
+ (sweeprolog-mode)
+ (let ((sweeprolog-dependency-directive 'use-module))
+ (call-interactively #'sweeprolog-update-dependencies))
+ (should (string= (buffer-string)
+ "
+:- module(foo, [bar/1]).
+:- use_module(library(apply), [maplist/2]).
+:- use_module(library(lists), [member/2]).
+
+/** <module> Foo
+
+*/
+
+bar(X) :- member(X, [1,2,3]).
+bar(X) :- maplist(X, [1,2,3]).
+"
+ ))))
+
+(ert-deftest update-dependencies-with-use-module ()
+ "Tests updating dependencies in presence of use_module directives."
+ (let ((temp (make-temp-file "sweeprolog-test"
+ nil
+ "pl"
+ "
+:- module(foo, [bar/1]).
+
+/** <module> Foo
+
+*/
+
+:- use_module(library(lists), [ member/2
+ ]).
+
+bar(X) :- member(X, [1,2,3]).
+bar(X) :- maplist(X, [1,2,3]).
+"
+ )))
+ (find-file-literally temp)
+ (sweeprolog-mode)
+ (call-interactively #'sweeprolog-update-dependencies)
+ (should (string= (buffer-string)
+ "
+:- module(foo, [bar/1]).
+
+/** <module> Foo
+
+*/
+
+:- use_module(library(lists), [ member/2
+ ]).
+:- use_module(library(apply), [maplist/2]).
+
+bar(X) :- member(X, [1,2,3]).
+bar(X) :- maplist(X, [1,2,3]).
+"
+ ))))
+
(ert-deftest update-dependencies-autoload-from-package ()
"Tests making implicit autoloads from a package explicit."
(let ((temp (make-temp-file "sweeprolog-test"
(should (string= (buffer-string)
"
:- module(foo, [bar/1]).
+:- autoload(library(http/http_open), [http_open/3]).
/** <module> Foo
*/
-:- autoload(library(http/http_open), [ http_open/3
- ]).
-
bar(X) :- http_open(X, X, X).
"))))
(should (string= (buffer-string)
"
:- module(foo, [bar/1]).
+:- autoload(library(lists), [member/2]).
/** <module> Foo
*/
-:- autoload(library(lists), [ member/2
- ]).
-
bar(X) :- member(X, [1,2,3]).
"
(should (string= (buffer-string)
"
:- module(foo, [bar/1]).
+:- autoload(library(lists), [member/2, permutation/2]).
/** <module> Foo
*/
-:- autoload(library(lists), [ member/2,
- permutation/2
- ]).
-
bar(X) :- member(X, [1,2,3]).
bar(X) :- permutation(X, [1,2,3])."))))
(const :tag "Refuse" nil))
:group 'sweeprolog)
+(defcustom sweeprolog-dependency-directive 'infer
+ "Prolog directive to use for adding dependencies.
+This determines whether `sweeprolog-update-dependencies' uses
+`autoload/2' or `use_module/2' directives to make implicit
+dependencies into explicit dependencies.
+
+If set to the symbol `use-module', then
+`sweeprolog-update-dependencies' only uses `use_module/2'
+directives. If set to the symbol `infer', then
+`sweeprolog-update-dependencies' uses `autoload/2' directives
+unless the buffer already contains dependency directives and all
+of them are `use_module/2' directives. Any other values means to
+use `autoload/2' for all added directives."
+ :package-version '((sweeprolog "0.16.1"))
+ :type '(choice (const :tag "Prefer use_module/2" use-module)
+ (const :tag "Prefer autoload/2" autoload)
+ (const :tag "Infer" infer))
+ :group 'sweeprolog)
+
+
;;;; Keymaps
(defvar sweeprolog-mode-map
(defun sweeprolog-update-dependencies ()
"Add explicit dependencies for implicitly autoaloaded predicates."
(interactive "" sweeprolog-mode)
- (let ((existing nil)
+ (let ((styles nil)
+ (existing nil)
(current-directive-beg nil)
(current-directive-end nil)
- (current-directive-file nil))
+ (current-directive-file nil)
+ (last-directive-end nil)
+ (in-directive nil)
+ (seen-non-directive nil)
+ (first-structured-comment nil))
(sweeprolog-analyze-buffer-with
(lambda (beg end arg)
(pcase arg
+ ("directive"
+ (setq in-directive t))
+ (`("goal_term" "built_in" "autoload" 1)
+ (when in-directive
+ (add-to-list 'styles 'autoload)
+ (unless seen-non-directive
+ (setq last-directive-end end))))
(`("goal_term" "built_in" "autoload" 2)
- (setq current-directive-beg beg
- current-directive-end end))
+ (when in-directive
+ (setq current-directive-beg beg
+ current-directive-end end)
+ (unless seen-non-directive
+ (setq last-directive-end end))
+ (add-to-list 'styles 'autoload)))
+ (`("goal_term" "built_in" "use_module" 1)
+ (when in-directive
+ (add-to-list 'styles 'use-module)
+ (unless seen-non-directive
+ (setq last-directive-end end))))
(`("goal_term" "built_in" "use_module" 2)
- (setq current-directive-beg beg
- current-directive-end end))
+ (when in-directive
+ (setq current-directive-beg beg
+ current-directive-end end)
+ (unless seen-non-directive
+ (setq last-directive-end end))
+ (add-to-list 'styles 'use-module)))
+ (`("goal_term" "built_in" "module" 2)
+ (when in-directive
+ (unless seen-non-directive
+ (setq last-directive-end end))))
+ (`("goal_term" . ,_)
+ (setq in-directive nil))
+ ((or "term"
+ "clause"
+ "grammar_rule"
+ "method")
+ (setq in-directive nil
+ seen-non-directive beg))
+ (`("comment" . "structured")
+ (unless (or first-structured-comment
+ (< seen-non-directive beg))
+ (setq first-structured-comment beg)))
((or `("file" . ,file)
`("file_no_depend" . ,file))
(when (and current-directive-beg
(push
(cons current-directive-file (copy-marker (1- end) t))
existing))))))
+ (setq last-directive-end
+ (copy-marker (or (and last-directive-end
+ (save-excursion
+ (goto-char last-directive-end)
+ (sweeprolog-end-of-top-term)
+ (point)))
+ first-structured-comment
+ (point-min))))
(if-let ((missing
(sweeprolog--query-once "sweep" "sweep_file_missing_dependencies"
(buffer-file-name))))
(dolist (autoloaded missing)
(let* ((file (nth 0 autoloaded))
(pred (nth 1 autoloaded))
- (kind (nth 2 autoloaded)))
+ (kind (pcase sweeprolog-dependency-directive
+ ('use-module "use_module")
+ ('infer (if (equal styles '(use-module))
+ "use_module"
+ (nth 2 autoloaded)))
+ (_ (nth 2 autoloaded)))))
(if (not pred)
(save-mark-and-excursion
- (goto-char (point-min))
- (sweeprolog-end-of-top-term)
- (while (forward-comment 1))
- (insert ":- " kind "("
- (sweeprolog--query-once "sweep"
- "sweep_file_path_in_library"
- file)
- ").\n\n")
+ (goto-char last-directive-end)
+ (insert-before-markers
+ ":- " kind "("
+ (sweeprolog--query-once "sweep"
+ "sweep_file_path_in_library"
+ file)
+ ").\n")
(indent-region-line-by-line (save-excursion
(sweeprolog-beginning-of-top-term)
(point))
(point)))
- (message "Adding explicit dependency on %s from %s."
- pred file)
(if-let ((marker (cdr (assoc-string file existing))))
(save-mark-and-excursion
(goto-char marker)
- (pcase (sweeprolog-last-token-boundaries)
- (`(open ,_ ,oend)
- (goto-char oend)
- (insert " " pred "\n"))
- (`(symbol ,_ ,oend)
- (let ((point (point)))
+ (let ((close-line (line-number-at-pos (point)))
+ (previous-line nil))
+ (pcase (sweeprolog-last-token-boundaries)
+ (`(open ,_ ,oend)
(goto-char oend)
- (insert ",")
- (goto-char (1+ point))
- (insert pred "\n")))
- (tok
- (user-error "Unexpected token %s while looking for import list"
- tok)))
- (indent-region-line-by-line (save-excursion
- (sweeprolog-beginning-of-top-term)
- (point))
- (save-excursion
- (sweeprolog-end-of-top-term)
- (point))))
+ (insert pred))
+ (`(symbol ,_ ,oend)
+ (let ((import-list-end-marker
+ (copy-marker (point))))
+ (goto-char oend)
+ (setq previous-line (line-number-at-pos (point)))
+ (delete-horizontal-space)
+ (insert-before-markers (if (eolp)
+ ","
+ ", "))
+ (goto-char import-list-end-marker)
+ (insert pred)
+ (unless (= previous-line close-line)
+ (insert "\n"))
+ (when (< fill-column (current-column))
+ (goto-char import-list-end-marker)
+ (insert "\n"))))
+ (tok
+ (user-error "Unexpected token %s while looking for import list"
+ tok))))
+ (indent-region-line-by-line (save-excursion
+ (sweeprolog-beginning-of-top-term)
+ (point))
+ (save-excursion
+ (sweeprolog-end-of-top-term)
+ (point))))
(save-mark-and-excursion
- (goto-char (point-min))
- (sweeprolog-end-of-top-term)
- (while (forward-comment 1))
- (insert ":- " kind "("
- (sweeprolog--query-once "sweep"
- "sweep_file_path_in_library"
- file)
- ", [ " pred "\n]).\n\n")
+ (goto-char last-directive-end)
+ (insert-before-markers
+ ":- " kind "("
+ (sweeprolog--query-once "sweep"
+ "sweep_file_path_in_library"
+ file)
+ ", [" pred "]).\n")
(indent-region-line-by-line (save-excursion
(sweeprolog-beginning-of-top-term)
(point))
(point))
- (push (cons file (copy-marker (- (point) 5) t)) existing))))))
+ (push (cons file (copy-marker (- (point) 4) t)) existing))))))
(sweeprolog-analyze-buffer t))
(message "No implicit autoloads found."))))