emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/sweeprolog 39f7e40e68 2/5: ADDED: New command 'sweeprolog-


From: ELPA Syncer
Subject: [nongnu] elpa/sweeprolog 39f7e40e68 2/5: ADDED: New command 'sweeprolog-extract-region-to-predicate'
Date: Fri, 8 Sep 2023 16:01:22 -0400 (EDT)

branch: elpa/sweeprolog
commit 39f7e40e68cf213f0eb76c7b810083e6e06e1026
Author: Eshel Yaron <me@eshelyaron.com>
Commit: Eshel Yaron <me@eshelyaron.com>

    ADDED: New command 'sweeprolog-extract-region-to-predicate'
    
    Add a command for extracting a part of a clause body into a separate
    predicate.
    
    * sweep.pl (sweep_term_variable_names/2)
    (sweep_goal_may_cut/2): New helper predicates.
    * sweeprolog.el (sweeprolog-extract-region-to-predicate): New command.
    (sweeprolog-maybe-extract-region-to-predicate): New function.
    (sweeprolog-insert-term-functions): Add it.
    * sweep.texi (Insert Term DWIM, Extract Goal): Document it.
---
 sweep.pl      | 30 ++++++++++++++++++-
 sweep.texi    | 45 ++++++++++++++++++++++++++---
 sweeprolog.el | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 159 insertions(+), 9 deletions(-)

diff --git a/sweep.pl b/sweep.pl
index 5e8ce0abb4..d9d10e9898 100644
--- a/sweep.pl
+++ b/sweep.pl
@@ -93,7 +93,9 @@
             sweep_variable_start_code/2,
             sweep_head_functors_collection/2,
             sweep_functors_collection/2,
-            sweep_compound_functors_collection/2
+            sweep_compound_functors_collection/2,
+            sweep_term_variable_names/2,
+            sweep_goal_may_cut/2
           ]).
 
 :- use_module(library(pldoc)).
@@ -1422,3 +1424,29 @@ sweep_expand_macro(String0, String) :-
     term_string(Term, String, [variable_names(Vs), module(M)]).
 
 sweep_variable_start_code(C, _) :- code_type(C, prolog_var_start).
+
+sweep_term_variable_names(String, Names) :-
+    term_string(_, String, [variable_names(VarNames)]),
+    maplist([Atom=_,Name]>>atom_string(Atom, Name), VarNames, Names).
+
+sweep_goal_may_cut(String, _) :-
+    term_string(Goal, String),
+    sweep_goal_may_cut_(Goal),
+    !.
+
+sweep_goal_may_cut_((_->A;B)) =>
+    (   sweep_goal_may_cut_(A)
+    ;   sweep_goal_may_cut_(B)
+    ).
+sweep_goal_may_cut_(A;B) =>
+    (   sweep_goal_may_cut_(A)
+    ;   sweep_goal_may_cut_(B)
+    ).
+sweep_goal_may_cut_((A,B)) =>
+    (   sweep_goal_may_cut_(A)
+    ;   sweep_goal_may_cut_(B)
+    ).
+sweep_goal_may_cut_(!) =>
+    true.
+sweep_goal_may_cut_(_) =>
+    false.
diff --git a/sweep.texi b/sweep.texi
index 13d558d0ba..17fdf1e441 100644
--- a/sweep.texi
+++ b/sweep.texi
@@ -2099,8 +2099,8 @@ As a means of automating common Prolog code editing 
tasks, such as
 adding new clauses to an existing predicate, Sweep Prolog mode
 provides the ``do what I mean'' command
 @code{sweeprolog-insert-term-dwim}, bound by default to @kbd{C-M-m}
-(or equivalently, @kbd{M-RET}).  This command inserts a new term at or
-after point according to the context in which you invoke it.
+(or equivalently, @kbd{M-@key{RET}}).  This command inserts a new term in
+the current buffer according to the context in which you invoke it.
 
 @table @kbd
 @kindex M-RET
@@ -2109,7 +2109,7 @@ after point according to the context in which you invoke 
it.
 @item M-@key{RET}
 @itemx C-M-m
 Insert an appropriate Prolog term in the current buffer, based on the
-context at point (@code{sweeprolog-insert-term-dwim}).
+current context (@code{sweeprolog-insert-term-dwim}).
 @end table
 
 @defvar sweeprolog-insert-term-functions
@@ -2126,6 +2126,11 @@ non-@code{nil}.
 By default, @code{sweeprolog-insert-term-dwim} tries the following
 insertion functions, in order:
 
+@defun sweeprolog-maybe-extract-region-to-predicate
+If the region is active, extract the selected goal into a separate
+predicate.  @xref{Extract Goal}.
+@end defun
+
 @defun sweeprolog-maybe-insert-next-clause
 If the last token before point is a fullstop ending a predicate
 clause, insert a new clause below it.
@@ -2177,7 +2182,7 @@ test() :- TestBody.
 The cursor is left between the parentheses of the @code{test()} head
 term, and the @code{TestBody} variable is marked as a hole
 (@pxref{Holes}).  To insert another unit test, place point after a
-complete test case and type @kbd{C-M-m} (or @kbd{M-RET}) to invoke
+complete test case and type @kbd{C-M-m} (or @kbd{M-@key{RET}}) to invoke
 @code{sweeprolog-insert-term-dwim} (@pxref{Insert Term DWIM, ,
 Context-Based Term Insertion}).
 
@@ -2517,6 +2522,38 @@ With Context Menu mode enabled, you can also expand 
macros by
 right-clicking on the @code{#} and selecting @samp{Expand Macro} from
 the context menu.  @xref{Context Menu}.
 
+@node Extract Goal
+@section Extracting Goals to Separate Predicates
+
+Sweep can help you extract a part of the body of a Prolog clause into
+a separate predicate, so you can reuse it in other places.
+
+@findex sweeprolog-extract-region-to-predicate
+@deffn Command sweeprolog-extract-region-to-predicate
+Extract the goal between point and mark into a new predicate.
+@end deffn
+
+This command extracts the selected goal into a separate predicate.  It
+prompts you for the name of the new predicate and inserts a definition
+for that predicate in the current buffer, while replacing the current
+region with a call to this new predicate.  The body of the new
+predicate is the goal in the current region, and this command
+determines the arguments of the new predicate based on the variables
+that the goal to extract shares with the containing clause.
+
+If the selected goal contains a cut whose scope would change as a
+result of being extracted from the current clause,
+@code{sweeprolog-extract-region-to-predicate} warns you about it and
+asks you to confirm before continuing.
+
+By default, @code{sweeprolog-extract-region-to-predicate} is not bound
+directly to any key in Sweep Prolog mode; instead, you can it by
+typing @kbd{M-@key{RET}} (@code{sweeprolog-insert-term-dwim}) when the
+region is active.  @xref{Insert Term DWIM}).
+
+With Context Menu mode enabled, you can also invoke this command by
+right-clicking on an active region.
+
 @node Prolog Help
 @chapter Prolog Help
 
diff --git a/sweeprolog.el b/sweeprolog.el
index 24938cc725..d348d28088 100644
--- a/sweeprolog.el
+++ b/sweeprolog.el
@@ -56,7 +56,8 @@
 (defvar sweeprolog--extra-init-args nil)
 
 (defvar sweeprolog-insert-term-functions
-  '(sweeprolog-maybe-insert-next-clause
+  '(sweeprolog-maybe-extract-region-to-predicate
+    sweeprolog-maybe-insert-next-clause
     sweeprolog-maybe-define-predicate)
   "Hook of functions that insert a Prolog term in a certain context.
 
@@ -1392,8 +1393,8 @@ Prolog buffers."
 (defun sweeprolog-local-variables-collection (&rest exclude)
   "Return a list of variable names that occur in the current clause.
 
-EXCLUDE is a list of variables name to be excluded from the
-resulting list even when found in the current clause."
+EXCLUDE is a list of variable names to exclude from the resulting
+list even when found in the current clause."
   (let* ((case-fold-search nil)
          (beg (save-mark-and-excursion
                 (unless (sweeprolog-at-beginning-of-top-term-p)
@@ -5987,13 +5988,23 @@ POINT is the buffer position of the mouse click."
                              :help ,(format "Expand macro to %s" expansion)
                              :keys "\\[sweeprolog-expand-macro-at-point]")))))
 
+(defun sweeprolog-context-menu-for-region (menu &rest _)
+  "Extend MENU with commands that are only relevant when the region is active."
+  (when (use-region-p)
+    (define-key menu [sweeprolog-extract-region-to-predicate]
+                `(menu-item "Extract to New Predicate"
+                            sweeprolog-extract-region-to-predicate
+                            :help "Extract the selected goal into a separate 
predicate"
+                            :keys 
"\\[sweeprolog-extract-region-to-predicate]"))))
+
 (defvar sweeprolog-context-menu-functions
   '(sweeprolog-context-menu-for-clause
     sweeprolog-context-menu-for-file
     sweeprolog-context-menu-for-module
     sweeprolog-context-menu-for-predicate
     sweeprolog-context-menu-for-variable
-    sweeprolog-context-menu-for-macro)
+    sweeprolog-context-menu-for-macro
+    sweeprolog-context-menu-for-region)
   "Functions that create context menu entries for Prolog tokens.
 Each function receives as its arguments the menu, the Prolog
 token's description, its start position, its end position, and
@@ -6809,6 +6820,80 @@ This function is used as a 
`add-log-current-defun-function' in
               fun ind (number-to-string ari)))))
 
 
+;;;; Extract goals to separate predicates
+
+(defun sweeprolog-extract-region-to-predicate (beg end new)
+  "Extract the Prolog goal from BEG to END into a new predicate, NEW.
+
+BEG and END are buffer positions; interactively, these are the
+beginning and end of the current region.  NEW is a string used as
+the functor of the new predicate; interactively, this command
+prompts for NEW in the minibuffer.
+
+This command defines the new predicate with arguments based on
+the variables that the goal to extract shares with the containing
+clause.
+
+The user option `sweeprolog-new-predicate-location-function' says
+where in the buffer to insert the newly created predicate."
+  (interactive "r\nsNew predicate functor: " sweeprolog-mode)
+  ;; TODO - check that NEW isn't already used
+  (let* ((name (sweeprolog-format-string-as-atom new))
+         (body (buffer-substring-no-properties beg end))
+         (vars (condition-case error
+                   (sweeprolog--query-once "sweep" "sweep_term_variable_names"
+                                           body)
+                 (prolog-exception
+                  (user-error "Region does not contain a valid Prolog 
term")))))
+    (if (and (sweeprolog--query-once "sweep" "sweep_goal_may_cut" body)
+             (not (y-or-n-p (concat
+                             "The selected goal contains a cut whose "
+                             "scope would change as a result of this "
+                             "operation.  Continue?"))))
+        (message "Canceled.")
+      (combine-after-change-calls
+        (goto-char beg)
+        (delete-region beg end)
+        (insert name)
+        (let* ((clause-beg (save-excursion
+                             (sweeprolog-beginning-of-top-term)
+                             (point)))
+               (clause-end (save-excursion
+                             (sweeprolog-end-of-top-term)
+                             (point)))
+               (clause-vars
+                (condition-case error
+                    (sweeprolog--query-once "sweep" "sweep_term_variable_names"
+                                            (buffer-substring-no-properties
+                                             clause-beg clause-end))
+                  (prolog-exception (sweeprolog-local-variables-collection))))
+               (neck (or (nth 4 (sweeprolog-definition-at-point)) ":-"))
+               (args (seq-intersection vars clause-vars #'string=)))
+          (when args
+            (insert "(" (mapconcat #'identity args ", ") ")"))
+          (funcall sweeprolog-new-predicate-location-function
+                   name (length args) neck)
+          (let ((def-beg (1+ (point))))
+            (insert (concat "\n"
+                            name
+                            (when args
+                              (concat "(" (mapconcat #'identity args ", ") 
")"))
+                            " "
+                            neck
+                            "\n"
+                            body
+                            ".\n"))
+            (indent-region-line-by-line def-beg (point))
+            (goto-char def-beg)))))))
+
+(defun sweeprolog-maybe-extract-region-to-predicate (&rest _)
+  (when (use-region-p)
+    (sweeprolog-extract-region-to-predicate
+     (use-region-beginning)
+     (use-region-end)
+     (read-string "Extract region to new predicate: "))
+    t))
+
 ;;;; Bug Reports
 
 (defvar reporter-prompt-for-summary-p)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]