From: Eshel Yaron Date: Mon, 13 Feb 2023 18:35:10 +0000 (+0200) Subject: MODIFIED: sweeprolog-update-dependencies choice of directives X-Git-Tag: V9.1.6-sweep-0.17.0~5 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=89f05c253f285064a9bb05853cb2f5cced908811;p=sweep.git MODIFIED: sweeprolog-update-dependencies choice of directives * 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. --- diff --git a/README.org b/README.org index ccd5355..3644f12 100644 --- a/README.org +++ b/README.org @@ -1674,13 +1674,33 @@ sweeprolog-update-dependencies~ bound to ~C-c C-u~. - 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 diff --git a/sweep.pl b/sweep.pl index 48a8c0d..7f0fb34 100644 --- a/sweep.pl +++ b/sweep.pl @@ -192,7 +192,7 @@ sweep_analyze_region_(OneTerm, Offset, Stream, Path, _) :- 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). diff --git a/sweeprolog-tests.el b/sweeprolog-tests.el index 6f5f6bf..42ebaba 100644 --- a/sweeprolog-tests.el +++ b/sweeprolog-tests.el @@ -909,7 +909,7 @@ foo :- Body. ")))) (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" @@ -929,14 +929,12 @@ bar(X) :- arithmetic_function(X). (should (string= (buffer-string) " :- module(foo, [bar/1]). +:- use_module(library(arithmetic), [arithmetic_function/1]). /** Foo */ -:- use_module(library(arithmetic), [ arithmetic_function/1 - ]). - bar(X) :- arithmetic_function(X). ")))) @@ -979,6 +977,120 @@ bar(X) :- permutation(X, [1,2,3]). " )))) +(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]). + +/** 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]). + +/** 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]). + +/** 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]). + +/** 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]). + +/** 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]). + +/** 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" @@ -1000,14 +1112,12 @@ bar(X) :- http_open(X, X, X). (should (string= (buffer-string) " :- module(foo, [bar/1]). +:- autoload(library(http/http_open), [http_open/3]). /** Foo */ -:- autoload(library(http/http_open), [ http_open/3 - ]). - bar(X) :- http_open(X, X, X). ")))) @@ -1032,14 +1142,12 @@ bar(X) :- member(X, [1,2,3]). (should (string= (buffer-string) " :- module(foo, [bar/1]). +:- autoload(library(lists), [member/2]). /** Foo */ -:- autoload(library(lists), [ member/2 - ]). - bar(X) :- member(X, [1,2,3]). " @@ -1050,15 +1158,12 @@ bar(X) :- member(X, [1,2,3]). (should (string= (buffer-string) " :- module(foo, [bar/1]). +:- autoload(library(lists), [member/2, permutation/2]). /** Foo */ -:- autoload(library(lists), [ member/2, - permutation/2 - ]). - bar(X) :- member(X, [1,2,3]). bar(X) :- permutation(X, [1,2,3]).")))) diff --git a/sweeprolog.el b/sweeprolog.el index 990b3e0..4bdbfa1 100644 --- a/sweeprolog.el +++ b/sweeprolog.el @@ -398,6 +398,26 @@ first." (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 @@ -4902,19 +4922,60 @@ accordingly." (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 @@ -4930,6 +4991,14 @@ accordingly." (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)))) @@ -4937,59 +5006,72 @@ accordingly." (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."))))