From 81338214994998e125b09abf8dbbae05dad53761 Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Sat, 19 Nov 2022 18:38:32 +0200 Subject: [PATCH] FIXED: exporting predicates in presence of exported operators * sweep.pl (sweep_exportable_predicates/2): new predicate. * sweeprolog.el (sweeprolog--module-term) (sweeprolog--exportable-predicates) (sweeprolog-analyze-start-exportable) (sweeprolog-analyze-fragment-exportable): no longer used, deleted. (sweeprolog-exportable-predicates): new function. (sweeprolog-read-exportable-predicate): use it. (sweeprolog-export-predicate): handle exported operators. --- sweep.pl | 13 ++++- sweeprolog-tests.el | 70 +++++++++++++++++++++- sweeprolog.el | 137 ++++++++++++++++++++++++-------------------- 3 files changed, 155 insertions(+), 65 deletions(-) diff --git a/sweep.pl b/sweep.pl index 646cfbc..ec1be7a 100644 --- a/sweep.pl +++ b/sweep.pl @@ -65,7 +65,8 @@ sweep_beginning_of_last_predicate/2, sweep_atom_collection/2, sweep_context_callable/2, - sweep_predicate_completion_candidates/2 + sweep_predicate_completion_candidates/2, + sweep_exportable_predicates/2 ]). :- use_module(library(pldoc)). @@ -813,3 +814,13 @@ callable_arg(N) :- integer(N), !. callable_arg(^) :- !. callable_arg(//) :- !. callable_arg(:) :- !. + +sweep_exportable_predicates(Path0, Preds) :- + atom_string(Path, Path0), + findall(D, + ( xref_defined(Path, D0, _), + \+ xref_exported(Path, D0), + pi_head(D1, D0), + term_string(D1, D) + ), + Preds). diff --git a/sweeprolog-tests.el b/sweeprolog-tests.el index 5decdae..9bc0ad8 100644 --- a/sweeprolog-tests.el +++ b/sweeprolog-tests.el @@ -217,7 +217,7 @@ bar(Bar) :- baz(Bar). " :- module(sweeprolog_test_export_predicate, []). -%! foo(+Bar) is det +%! foo(+Bar) is det. foo(Bar) :- bar(Bar). "))) @@ -231,11 +231,77 @@ foo(Bar) :- bar(Bar). :- module(sweeprolog_test_export_predicate, [foo/1 % +Bar ]). -%! foo(+Bar) is det +%! foo(+Bar) is det. foo(Bar) :- bar(Bar). ")))) +(ert-deftest export-predicate-with-op () + "Test exporting a predicate in presence of an exported operator." + (let ((temp (make-temp-file "sweeprolog-test" + nil + ".pl" + " +:- module(tester, + [ instantiate_test_template/4, % +In,+Replacement,-Dict,-Map + op(200, fy, @) % @name + ]). + +%! foo(+Bar) is det. + +foo(Bar). +"))) + (find-file-literally temp) + (sweeprolog-mode) + (goto-char (point-max)) + (backward-word) + (call-interactively #'sweeprolog-export-predicate) + (should (equal (buffer-string) + " +:- module(tester, + [ instantiate_test_template/4, % +In,+Replacement,-Dict,-Map + foo/1, % +Bar + op(200, fy, @) % @name + ]). + +%! foo(+Bar) is det. + +foo(Bar). +" + )))) + +(ert-deftest export-predicate-with-only-op () + "Test exporting a predicate in presence of only exported operators." + (let ((temp (make-temp-file "sweeprolog-test" + nil + ".pl" + " +:- module(tester, + [ op(200, fy, @) % @name + ]). + +%! foo(+Bar) is det. + +foo(Bar). +"))) + (find-file-literally temp) + (sweeprolog-mode) + (goto-char (point-max)) + (backward-word) + (call-interactively #'sweeprolog-export-predicate) + (should (equal (buffer-string) + " +:- module(tester, + [ foo/1, % +Bar + op(200, fy, @) % @name + ]). + +%! foo(+Bar) is det. + +foo(Bar). +" + )))) + (ert-deftest identifier-at-point () "Test recognizing predicate invocations." (let ((temp (make-temp-file "sweeprolog-test" diff --git a/sweeprolog.el b/sweeprolog.el index b514c5b..74af087 100644 --- a/sweeprolog.el +++ b/sweeprolog.el @@ -438,8 +438,6 @@ buffer where the new predicate defintion should be inserted." ;;;; Local variables -(defvar-local sweeprolog--module-term nil) - (defvar-local sweeprolog--diagnostics nil) (defvar-local sweeprolog--diagnostics-report-fn nil) @@ -448,8 +446,6 @@ buffer where the new predicate defintion should be inserted." (defvar-local sweeprolog--diagnostics-changes-end nil) -(defvar-local sweeprolog--exportable-predicates nil) - (defvar-local sweeprolog--timer nil) (defvar-local sweeprolog--analyze-buffer-duration 0.2) @@ -2009,21 +2005,6 @@ resulting list even when found in the current clause." :region (cons beg end)) (setq sweeprolog--diagnostics-report-fn nil))) -(defun sweeprolog-analyze-start-exportable (&rest _) - (setq sweeprolog--exportable-predicates nil - sweeprolog--module-term nil)) - -(defun sweeprolog-analyze-fragment-exportable (beg end arg) - (pcase arg - (`("head" ,(rx (or "dynamic " - "unreferenced" - "local(")) - ,f ,a) - (add-to-list 'sweeprolog--exportable-predicates - (concat f "/" (number-to-string a)))) - (`("goal_term" "built_in" "module" 2) - (setq sweeprolog--module-term (cons beg end))))) - (defun sweeprolog-analyze-fragment-variable (beg end arg) (pcase arg ((or "var" @@ -3144,10 +3125,16 @@ predicate definition at or directly above POINT." (sweeprolog--query-once "sweep" "sweep_local_predicate_export_comment" (list (buffer-file-name) fun ari))) +(defun sweeprolog-exportable-predicates () + "Return a list of exportable predicates from the current buffer." + (sweeprolog-xref-buffer) + (sweeprolog--query-once "sweep" "sweep_exportable_predicates" + (buffer-file-name))) + (defun sweeprolog-read-exportable-predicate () "Read a predicate name that can be exported in the current buffer." (completing-read sweeprolog-read-exportable-predicate-prompt - sweeprolog--exportable-predicates)) + (sweeprolog-exportable-predicates))) (defun sweeprolog-export-predicate (pred &optional comm) "Add PRED to the export list of the current module. @@ -3169,48 +3156,74 @@ non-exported predicates defined in the current buffer." (sweeprolog-read-exportable-predicate) (read-string "Export comment: "))) sweeprolog-mode) - (add-hook 'sweeprolog-analyze-region-start-hook - #'sweeprolog-analyze-start-exportable nil t) - (add-hook 'sweeprolog-analyze-region-fragment-hook - #'sweeprolog-analyze-fragment-exportable nil t) - (sweeprolog-analyze-buffer t) - (remove-hook 'sweeprolog-analyze-region-fragment-hook - #'sweeprolog-analyze-fragment-exportable t) - (remove-hook 'sweeprolog-analyze-region-start-hook - #'sweeprolog-analyze-start-exportable t) - (unless (member pred sweeprolog--exportable-predicates) - (user-error "Cannot add %s to export list" pred)) - (if-let ((mbeg (car sweeprolog--module-term)) - (mend (cdr sweeprolog--module-term))) - (save-excursion - (goto-char mend) - (let ((pos (- (point-max) (point)))) - (unless (search-backward "]" (car sweeprolog--module-term) t) - (user-error "Cannot find export list")) - (combine-after-change-calls - (pcase (sweeprolog-last-token-boundaries) - (`(open ,_ ,_) - (insert pred) - (when (and comm (not (string-empty-p comm))) - (insert " % " comm)) - (insert "\n") - (indent-region mbeg (1+ (point)))) - (`(symbol ,_ ,oend) - (let ((point (point))) - (goto-char oend) - (insert ",") - (goto-char (1+ point)) - (insert "\n") - (backward-char) - (insert pred) - (when (and comm (not (string-empty-p comm))) - (insert " % " comm)) - (indent-region mbeg (- (point-max) pos)) - (align-regexp mbeg (- (point-max) pos) (rx (group (zero-or-more blank)) "%")))) - (_ (user-error "Unexpected token while looking for export list"))))) - (sweeprolog-analyze-buffer t) - (message "Exported %s" pred)) - (user-error "Buffer is not a module"))) + (save-restriction + (widen) + (save-excursion + (goto-char (point-min)) + (unless (or (sweeprolog-at-beginning-of-top-term-p) + (sweeprolog-beginning-of-next-top-term)) + (user-error "No module declaration found")) + (let* ((indent-tabs-mode nil) + (target-position nil) + (module-term-beg nil) + (module-term-end nil) + (exported-operator nil) + (func (lambda (beg end arg) + (pcase arg + (`("goal_term" "built_in" "module" 2) + (setq module-term-beg beg + module-term-end end)) + ((or "list" + "empty_list") + (when (and (not target-position) + (< module-term-beg beg) + (< end module-term-end)) + (setq target-position (1- end)))) + ("exported_operator" + (unless exported-operator + (setq exported-operator t + target-position beg))))))) + (sweeprolog-analyze-term-at-point func) + (unless (member pred (sweeprolog-exportable-predicates)) + (user-error "Cannot add %s to export list" pred)) + (if target-position + (save-excursion + (goto-char target-position) + (let ((pos (- (point-max) (line-end-position)))) + (combine-after-change-calls + (if exported-operator + (progn + (insert pred ",\n") + (backward-char) + (when (and comm (not (string-empty-p comm))) + (insert " % " comm)) + (indent-region module-term-beg (- (point-max) pos)) + (align-regexp module-term-beg (- (point-max) pos) + (rx (group (zero-or-more blank)) "%"))) + (pcase (sweeprolog-last-token-boundaries) + (`(open ,_ ,_) + (insert pred) + (when (and comm (not (string-empty-p comm))) + (insert " % " comm)) + (insert "\n") + (indent-region module-term-beg (1+ (point)))) + (`(symbol ,_ ,oend) + (let ((point (point))) + (goto-char oend) + (insert ",") + (goto-char (1+ point)) + (insert "\n") + (backward-char) + (insert pred) + (when (and comm (not (string-empty-p comm))) + (insert " % " comm)) + (indent-region module-term-beg (- (point-max) pos)) + (align-regexp module-term-beg (- (point-max) pos) + (rx (group (zero-or-more blank)) "%")))) + (tok (user-error "Unexpected token %s while looking for export list" tok)))))) + (sweeprolog-analyze-buffer t) + (message "Exported %s" pred)) + (user-error "No export list found")))))) (defun sweeprolog-align-spaces (&optional _) "Adjust in-line whitespace between the previous next Prolog tokens. -- 2.39.2