From 833734f461d589b50589bafc1170958d7d074bb4 Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Thu, 22 Dec 2022 16:51:47 +0200 Subject: [PATCH] ADDED: New command sweeprolog-insert-term-with-holes * sweep.pl (sweep_format_term/2, sweep_current_functors/2): new predicates. * sweeprolog.el (sweeprolog-read-functor-history): new variable, history list for... (sweeprolog-read-functor): new function. (sweeprolog-forward-hole-repeat-mode): rename to... (sweeprolog-forward-hole-repeat-map): corrected name for keymap. (sweeprolog-analyze-end-font-lock): don't rely on sweeprolog--next-hole to move point. (sweeprolog-at-hole-p, sweeprolog-beginning-of-hole) (sweeprolog-end-of-hole): new functions, used by... (sweeprolog--previous-hole, sweeprolog--next-hole): revise. (sweeprolog--precedence-at-point): new function, used by... (sweeprolog-insert-term-with-holes): new command. (sweeprolog-mode-map): bind it to C-c C-m * sweeprolog-tests: test it. * README.org ("Filling Holes"): move to top and rename to... ("Holes"): document sweeprolog-insert-term-with-holes. --- README.org | 173 +++++++++++++++++----------- sweep.pl | 29 ++++- sweeprolog-tests.el | 20 ++++ sweeprolog.el | 268 ++++++++++++++++++++++++++++++++++---------- 4 files changed, 360 insertions(+), 130 deletions(-) diff --git a/README.org b/README.org index 7515e07..634a90a 100644 --- a/README.org +++ b/README.org @@ -781,6 +781,105 @@ directly: goal2. #+end_src +** Holes +:PROPERTIES: +:CUSTOM_ID: holes +:DESCRIPTION: Commands for finding and filling holes for interactive term insertion +:ALT_TITLE: Holes +:END: + +#+CINDEX: holes +When writing Prolog code in the usual way of typing in one character +at a time, the buffer text is often found in a syntactically incorrect +state while you edit it. This happens for example right after you +insert an infix operator, before typing its expected right-hand side +argument. ~sweep~ provides an alternative method for inserting Prolog +terms in a way that maintains the syntactic correctness of the buffer +text while allowing the user to incrementally refine it by using +placeholder terms, called simply "holes". Holes indicate the location +of missing terms that the user can later fill in, essentially they +represent source-level unknown terms and their presence satisfies the +Prolog parser. Holes are written in the buffer as regular Prolog +variables, but they are annotated with a special text property[fn:2] +that allows ~sweep~ to recognize them as holes needed to be filled. + +#+FINDEX: sweeprolog-insert-term-with-holes +#+KINDEX: C-c C-m +#+KINDEX: C-c RET +The main command for inserting terms with holes is ~M-x +sweeprolog-insert-term-with-holes~. This command, bound by default to +~C-c C-m~ (or ~C-c RET~) in ~sweeprolog-mode~ buffers, prompts for a functor +and an arity and inserts a corresponding term with holes in place of +the term's arguments. It leaves point right after the first hole, +sets the mark to its start and activates region such that the hole is +marked. Call ~sweeprolog-insert-term-with-holes~ again to replace the +active region which now covers the first hole with another term, that +may again contain further holes. That way you can incrementally write +down a Prolog term, including whole clauses, by working down the +syntactic structure of the term and maintaining its all the while. +Without a prefix argument, ~sweeprolog-insert-term-with-holes~ prompts +for the functor and the arity to use. A non-negative prefix argument, +e.g. ~C-2 C-c C-m~ or ~C-u C-c C-m~, is taken as to be the inserted term's +arity and in this case ~sweeprolog-insert-term-with-holes~ only prompts +for the functor to insert. A negative prefix argument, ~C-- C-c C-m~, +inserts only a single hole without prompting for a functor. To +further help with keeping the buffer syntactically correct, this +command adds a comma (~,~) before or after the inserted term when needed +according to the surrounding tokens. If you call it at the end of a +term that doesn't have a closing fullstop, it adds the fullstop after +the inserted term. + +Several other ~sweep~ commands insert holes in place of unknown terms, +including ~C-M-i~ (see [[#code-completion][Code Completion]]), ~C-M-m~ (see [[#insert-term-at-point][Context-Based Term +Insertion]]) and ~M-x sweeprolog-plunit-testset-skeleton~ (see [[#writing-tests][Writing +Tests]]). + +#+VINDEX: sweeprolog-highlight-holes +When the user option ~sweeprolog-highlight-holes~ is set to non-nil, +holes in Prolog buffers are highlighted with a dedicated face, making +them easily distinguishable from regular Prolog variables. Hole +highlighting is enabled by default, to disable it customize +~sweeprolog-highlight-holes~ to nil. + +#+FINDEX: sweeprolog-backward-hole +#+FINDEX: sweeprolog-forward-hole +#+KINDEX: C-c C-i +#+KINDEX: C-c TAB +#+KINDEX: C-- C-c C-i +#+KINDEX: C-- C-c TAB +To jump to the next hole in a ~sweeprolog-mode~ buffer, use the command +~M-x sweeprolog-forward-hole~, bound by default to ~C-c TAB~ (or ~C-c C-i~). +This command sets up the region to cover the next hole after point +leaving the cursor at right after the hole. To jump to the previous +hole instead, use ~sweeprolog-backward-hole~ or call +~sweeprolog-forward-hole~ with a negative prefix argument (~C-- C-c TAB~). + +#+FINDEX: sweeprolog-forward-hole-on-tab-mode +#+KINDEX: TAB (sweeprolog-forward-hole-on-tab-mode) +#+KINDEX: C-i (sweeprolog-forward-hole-on-tab-mode) +When the minor mode ~sweeprolog-forward-hole-on-tab-mode~ is enabled, +the ~TAB~ key is bound to a command moves to the next hole when called +in a properly indented line (otherwise it indents the line). This +makes moving between holes in the buffer easier since ~TAB~ can be used +instead of ~C-c TAB~ in most cases. To enable this mode in a Prolog +buffer, type ~M-x sweeprolog-forward-hole-on-tab-mode-map~. This step +can be automated by adding ~sweeprolog-forward-hole-on-tab-mode~ to +~sweeprolog-mode-hook~: + +#+begin_src emacs-lisp + (add-hook 'sweeprolog-mode-hook #'sweeprolog-forward-hole-on-tab-mode) +#+end_src + +To "fill" a hole marked by one of the aforementioned commands, either +use ~C-c C-m~ as described above or type ~C-w~ (~M-x kill-region~) to kill +the region and remove the placeholder variable, and then insert Prolog +code as usual. As an alternative to manually killing the region with +~C-w~, with ~delete-selection-mode~ enabled the placeholder is +automatically deleted when you insert a character while the region is +active (see also [[info:emacs#Using Region][Using Region in the Emacs manual]]). + +[fn:2] see [[info:elisp#Text Properties][Text Properties in the Elisp manual]] + ** Definitions and references :PROPERTIES: :CUSTOM_ID: sweeprolog-xref @@ -1096,7 +1195,7 @@ In ~sweeprolog-mode~ buffers, the following enhancements are provided: ~completion-at-point~ suggests matching predicates as completion candidates. Predicate calls are inserted as complete term. If the chosen predicate takes arguments, holes are inserted in their places - (see [[#filling-holes][Filling Holes]]). + (see [[#holes][Holes]]). - Atom completion :: If point is at a non-callable, ~completion-at-point~ suggests matching atoms as completion candidates. @@ -1142,70 +1241,8 @@ functions, in order: ~sweeprolog-new-predicate-location-function~ can be customized to control where this function inserts new predicate definitions. -*** Filling Holes -:PROPERTIES: -:CUSTOM_ID: filling-holes -:DESCRIPTION: Commands for finding and filling holes for interactive term insertion -:ALT_TITLE: Filling Holes -:END: - -#+CINDEX: holes -The default term insertion functions used by -~sweeprolog-insert-term-dwim~ create a new clause in the buffer, with -placeholders for the arguments of the head term (if any) and for the -clause's body. These placeholders, called simply "holes", represent -the Prolog terms that remain to be given by the user. Holes are -written in the buffer as regular Prolog variables, but they are -annotated with a special text property[fn:2] that allows -~sweeprolog-mode~ to recognize them as holes needed to be filled. After -a term is inserted with ~sweeprolog-insert-term-dwim~, the region is set -to the first hole and the cursor left at the its end. - -#+VINDEX: sweeprolog-highlight-holes -When the user option ~sweeprolog-highlight-holes~ is set to non-nil, -holes in Prolog buffers are highlighted with a dedicated face, making -them easily distinguishable from regular Prolog variables. Hole -highlighting is enabled by default, to disable it customize -~sweeprolog-highlight-holes~ to nil. - -#+FINDEX: sweeprolog-backward-hole -#+FINDEX: sweeprolog-forward-hole -#+KINDEX: C-c C-i -#+KINDEX: C-c TAB -#+KINDEX: C-- C-c C-i -#+KINDEX: C-- C-c TAB -To jump to the next hole in a ~sweeprolog-mode~ buffer, use the command -~M-x sweeprolog-forward-hole~, bound by default to ~C-c TAB~ (or ~C-c C-i~). -This command sets up the region to cover the next hole after point -leaving the cursor at right after the hole. To jump to the previous -hole instead, use ~sweeprolog-backward-hole~ or call -~sweeprolog-forward-hole~ with a negative prefix argument (~C-- C-c TAB~). - -#+FINDEX: sweeprolog-forward-hole-on-tab-mode -#+KINDEX: TAB (sweeprolog-forward-hole-on-tab-mode) -#+KINDEX: C-i (sweeprolog-forward-hole-on-tab-mode) -When the minor mode ~sweeprolog-forward-hole-on-tab-mode~ is enabled, -the ~TAB~ key is bound to a command moves to the next hole when called -in a properly indented line (otherwise it indents the line). This -makes moving between holes in the buffer easier since ~TAB~ can be used -instead of ~C-c TAB~ in most cases. To enable this mode in a Prolog -buffer, type ~M-x sweeprolog-forward-hole-on-tab-mode-map~. This step -can be automated by adding ~sweeprolog-forward-hole-on-tab-mode~ to -~sweeprolog-mode-hook~: - -#+begin_src emacs-lisp - (add-hook 'sweeprolog-mode-hook #'sweeprolog-forward-hole-on-tab-mode) -#+end_src - -To "fill" a hole marked by one of the aforementioned commands, type -~C-w~ (~M-x kill-region~) to kill the region and remove the placeholder -variable, then insert Prolog code as usual. As an alternative to -manually killing the region with ~C-w~, with ~delete-selection-mode~ -enabled the placeholder is automatically deleted when the user inserts -a character while the region is active (see also [[info:emacs#Using Region][Using Region in the -Emacs manual]]). - -[fn:2] see [[info:elisp#Text Properties][Text Properties in the Elisp manual]] +This command inserts holes as placeholders for the body term and the +head's arguments, if any. See also [[#holes][Holes]]. ** Writing Tests :PROPERTIES: @@ -1234,9 +1271,9 @@ test() :- TestBody. #+end_src The cursor is left between the parentheses of the ~test()~ head term, -and the ~TestBody~ variable is marked as a hole (see [[#filling-holes][Filling Holes]]). To -insert another unit test, place point after a complete test case and -type ~C-M-m~ or ~M-RET~ to invoke ~sweeprolog-insert-term-dwim~ (see +and the ~TestBody~ variable is marked as a hole (see [[#holes][Holes]]). To insert +another unit test, place point after a complete test case and type +~C-M-m~ or ~M-RET~ to invoke ~sweeprolog-insert-term-dwim~ (see [[#insert-term-at-point][Context-Based Term Insertion]]). [fn:3] See [[https://www.swi-prolog.org/pldoc/doc_for?object=section(%27packages/plunit.html%27)][Prolog Unit Tests in the SWI-Prolog manual]]. diff --git a/sweep.pl b/sweep.pl index 91a4e69..2c1d338 100644 --- a/sweep.pl +++ b/sweep.pl @@ -71,7 +71,9 @@ sweep_string_to_atom/2, sweep_file_path_in_library/2, sweep_file_missing_dependencies/2, - sweep_format_head/2 + sweep_format_head/2, + sweep_format_term/2, + sweep_current_functors/2 ]). :- use_module(library(pldoc)). @@ -945,3 +947,28 @@ sweep_format_head([F0|A], R) :- pi_head(F/A, H), sweep_current_module(M), sweep_format_predicate(M, 0, H, R). + +sweep_format_term([F0,N,P], [S|SP]) :- + atom_string(F, F0), + pi_head(F/N, H), + length(NamedArgs, N), + maplist(=('$VAR'('_')), NamedArgs), + H =.. [F|NamedArgs], + term_string(H, S, [quoted(true), + character_escapes(true), + spacing(next_argument), + numbervars(true), + priority(P)]), + term_string(_, S, [subterm_positions(SP)]). + +sweep_current_functors(A0, Col) :- + ( A0 == [] + -> true + ; A = A0 + ), + findall([F|A], + ( current_functor(F0, A), + atom(F0), + atom_string(F0, F) + ), + Col). diff --git a/sweeprolog-tests.el b/sweeprolog-tests.el index 1210a5b..c27eb36 100644 --- a/sweeprolog-tests.el +++ b/sweeprolog-tests.el @@ -181,6 +181,26 @@ foo(Foo) :- bar. '(sweeprolog-undefined-default-face sweeprolog-body-default-face))))) +(ert-deftest insert-term-with-holes () + "Test `sweeprolog-insert-term-with-holes'." + (let ((temp (make-temp-file "sweeprolog-test" + nil + "pl" + ""))) + (find-file-literally temp) + (sweeprolog-mode) + (sweeprolog-insert-term-with-holes ":-" 2) + (call-interactively #'kill-region) + (sweeprolog-insert-term-with-holes "foo" 3) + (call-interactively #'kill-region) + (sweeprolog-insert-term-with-holes "bar" 0) + (call-interactively #'kill-region) + (sweeprolog-insert-term-with-holes ";" 2) + (call-interactively #'kill-region) + (sweeprolog-insert-term-with-holes "->" 2) + (should (string= (buffer-string) + "foo(bar, (_->_;_), _):-_.")))) + (ert-deftest plunit-testset-skeleton () "Tests inserting PlUnit test-set blocks." (let ((temp (make-temp-file "sweeprolog-test" diff --git a/sweeprolog.el b/sweeprolog.el index 057b164..9f6799d 100644 --- a/sweeprolog.el +++ b/sweeprolog.el @@ -49,6 +49,8 @@ (defvar sweeprolog-read-module-history nil) +(defvar sweeprolog-read-functor-history nil) + (defvar sweeprolog-top-level-signal-goal-history nil) (defvar sweeprolog--extra-init-args nil) @@ -361,13 +363,14 @@ non-terminals)." (defvar sweeprolog-mode-map (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-l") #'sweeprolog-load-buffer) (define-key map (kbd "C-c C-c") #'sweeprolog-analyze-buffer) - (define-key map (kbd "C-c C-t") #'sweeprolog-top-level) - (define-key map (kbd "C-c C-o") #'sweeprolog-find-file-at-point) (define-key map (kbd "C-c C-d") #'sweeprolog-document-predicate-at-point) (define-key map (kbd "C-c C-e") #'sweeprolog-export-predicate) (define-key map (kbd "C-c C-i") #'sweeprolog-forward-hole) + (define-key map (kbd "C-c C-l") #'sweeprolog-load-buffer) + (define-key map (kbd "C-c C-m") #'sweeprolog-insert-term-with-holes) + (define-key map (kbd "C-c C-o") #'sweeprolog-find-file-at-point) + (define-key map (kbd "C-c C-t") #'sweeprolog-top-level) (define-key map (kbd "C-c C-u") #'sweeprolog-update-dependencies) (define-key map (kbd "C-c C-`") (if (fboundp 'flymake-show-buffer-diagnostics) ;; Flymake 1.2.1+ @@ -381,11 +384,12 @@ non-terminals)." map) "Keymap for `sweeprolog-mode'.") -(defvar sweeprolog-forward-hole-repeat-mode +(defvar sweeprolog-forward-hole-repeat-map (let ((map (make-sparse-keymap))) (define-key map (kbd "C-i") #'sweeprolog-forward-hole) - (define-key map (kbd "p") #'sweeprolog-backward-hole) - (define-key map (kbd "n") #'sweeprolog-forward-hole) + (define-key map (kbd "C-n") #'sweeprolog-forward-hole) + (define-key map (kbd "C-p") #'sweeprolog-backward-hole) + (define-key map (kbd "C-m") #'sweeprolog-insert-term-with-holes) map) "Repeat map for \\[sweeprolog-forward-hole].") @@ -867,6 +871,28 @@ depends on the value of the user option "Return a list of predicates whose name resembeles PATTERN." (sweeprolog--query-once "sweep" "sweep_predicate_apropos" pattern)) +(defun sweeprolog-read-functor (&optional arity) + "Read a Prolog functor/arity pair from the minibuffer. + +If ARITY is nil, prompt for the arity after reading the functor. +Otherwise, read only the functor, with completion candidates are +restricted to functors with arity ARITY, and return ARITY as the +arity. + +Return a cons cell of the functor as a string and the arity." + (let* ((col (sweeprolog--query-once "sweep" "sweep_current_functors" + arity)) + (completion-extra-properties + (list :annotation-function + (lambda (key) + (when-let ((val (cdr (assoc-string key col)))) + (concat "/" (number-to-string val))))))) + (let ((functor (completing-read "Functor: " + col nil nil nil + 'sweeprolog-read-functor-history))) + (cons functor + (or arity (read-number (concat functor "/"))))))) + (defun sweeprolog-read-predicate (&optional prompt) "Read a Prolog predicate from the minibuffer with prompt PROMPT. If PROMPT is nil, `sweeprolog-read-predicate-prompt' is used by @@ -2091,12 +2117,15 @@ resulting list even when found in the current clause." (narrow-to-region beg end) (let ((hole (sweeprolog--next-hole))) (while hole - (font-lock--add-text-property (car hole) (cdr hole) - 'font-lock-face - (sweeprolog-hole-face) - (current-buffer) - nil) - (setq hole (sweeprolog--next-hole))))))))) + (let ((hbeg (car hole)) + (hend (cdr hole))) + (font-lock--add-text-property hbeg hend + 'font-lock-face + (sweeprolog-hole-face) + (current-buffer) + nil) + (goto-char hend) + (setq hole (sweeprolog--next-hole)))))))))) (defun sweeprolog-analyze-start-flymake (&rest _) (flymake-start)) @@ -2625,64 +2654,79 @@ Interactively, POINT is set to the current point." (or (re-search-forward (rx "." (or white "\n")) nil t) (goto-char (point-max)))))) +(defun sweeprolog-at-hole-p (&optional point) + (setq point (or point (point))) + (get-text-property point 'sweeprolog-hole)) + +(defun sweeprolog-beginning-of-hole (&optional point) + (let ((beg (or point (point)))) + (when (sweeprolog-at-hole-p beg) + (while (and (< (point-min) beg) + (sweeprolog-at-hole-p (1- beg))) + (setq beg (1- beg))) + beg))) + +(defun sweeprolog-end-of-hole (&optional point) + (let ((end (or point (point)))) + (when (sweeprolog-at-hole-p end) + (while (and (< end (point-max)) + (sweeprolog-at-hole-p (1+ end))) + (setq end (1+ end))) + (1+ end)))) + (defun sweeprolog--next-hole (&optional wrap) "Return the bounds of the next hole in the current buffer. When WRAP in non-nil, wrap around if no holes are found between point and the end of the buffer." - (let ((current-hole-beg - (save-excursion - (while (and (get-text-property (point) 'sweeprolog-hole) - (not (bobp))) - (forward-char -1)) - (point)))) - (while (and (get-text-property (point) 'sweeprolog-hole) - (not (eobp))) - (forward-char)) - (while (not (or (get-text-property (point) 'sweeprolog-hole) - (eobp))) - (forward-char)) - (if (eobp) - (when wrap - (save-restriction - (goto-char (point-min)) - (narrow-to-region (point) current-hole-beg) - (sweeprolog--next-hole))) - (let ((beg (point))) - (while (and (get-text-property (point) 'sweeprolog-hole) - (not (eobp))) - (forward-char)) - (cons beg (point)))))) + (if-let ((current-hole-beg (sweeprolog-beginning-of-hole))) + (cons current-hole-beg + (sweeprolog-end-of-hole)) + (let ((point (point))) + (while (not (or (sweeprolog-at-hole-p) (eobp))) + (forward-char)) + (if (eobp) + (when wrap + (save-restriction + (goto-char (point-min)) + (narrow-to-region (point) point) + (sweeprolog--next-hole))) + (cons (point) + (sweeprolog-end-of-hole)))))) + +(defun sweeprolog--backward-wrap (&optional wrap) + (if (bobp) + (when wrap + (goto-char (point-max)) + t) + (forward-char -1) + t)) (defun sweeprolog--previous-hole (&optional wrap) "Return the bounds of the previous hole in the current buffer. When WRAP in non-nil, wrap around if no holes are found between point and the beginning of the buffer." - (let ((current-hole-end - (save-excursion - (while (and (get-text-property (point) 'sweeprolog-hole) - (not (eobp))) - (forward-char)) - (point)))) - (forward-char -1) - (while (and (get-text-property (point) 'sweeprolog-hole) - (not (bobp))) - (forward-char -1)) - (while (not (or (get-text-property (point) 'sweeprolog-hole) - (bobp))) - (forward-char -1)) - (if (bobp) - (when wrap - (save-restriction - (goto-char (point-max)) - (narrow-to-region current-hole-end (point)) - (sweeprolog--previous-hole))) - (let ((end (point))) - (while (and (get-text-property (point) 'sweeprolog-hole) - (not (bobp))) - (forward-char -1)) - (cons (1+ (point)) (1+ end)))))) + (let ((start (point))) + (when (use-region-p) + (goto-char (region-beginning)) + (deactivate-mark)) + (when (sweeprolog--backward-wrap wrap) + (if-let ((current-hole-beg (sweeprolog-beginning-of-hole))) + (cons current-hole-beg + (sweeprolog-end-of-hole)) + (let ((point (point))) + (while (not (or (sweeprolog-at-hole-p) (bobp))) + (forward-char -1)) + (if (bobp) + (or (and wrap + (save-restriction + (goto-char (point-max)) + (narrow-to-region point (point)) + (sweeprolog--previous-hole))) + (and (goto-char start) nil)) + (cons (sweeprolog-beginning-of-hole) + (sweeprolog-end-of-hole)))))))) (defun sweeprolog--forward-hole (&optional wrap) (if-let ((hole (sweeprolog--next-hole wrap)) @@ -2725,11 +2769,15 @@ instead." (put 'sweeprolog-backward-hole 'repeat-map - 'sweeprolog-forward-hole-repeat-mode) + 'sweeprolog-forward-hole-repeat-map) (put 'sweeprolog-forward-hole 'repeat-map - 'sweeprolog-forward-hole-repeat-mode) + 'sweeprolog-forward-hole-repeat-map) + +(put 'sweeprolog-insert-term-with-holes + 'repeat-map + 'sweeprolog-forward-hole-repeat-map) (defun sweeprolog--hole (&optional string) (propertize (or string "_") @@ -2738,6 +2786,104 @@ instead." cursor-sensor-functions font-lock-face))) +(defun sweeprolog--precedence-at-point (&optional point) + (setq point (or point (point))) + (pcase (sweeprolog-last-token-boundaries point) + ((or `(operator ,obeg ,oend) + `(symbol ,obeg ,oend)) + (let ((op (buffer-substring-no-properties obeg oend))) + (or (and (string= "." op) + (or (not (char-after (1+ obeg))) + (member (char-syntax (char-after (1+ obeg))) + '(?> ? ))) + 1200) + (sweeprolog-op-infix-precedence op) + (sweeprolog-op-prefix-precedence op) + 1200))) + (_ 1200))) + +(defun sweeprolog-insert-term-with-holes (functor arity) + "Insert a term with functor FUNCTOR and arity ARITY. + +If ARITY is negative, just insert a single hole at point. +Otherwise, insert FUNCTOR along with ARITY holes, one for each of +the term's arguments. + +Interactively, prompt for FUNCTOR. Without a prefix argument, +prompt for ARITY as well. Otherwise, ARITY is the numeric value +of the prefix argument." + (interactive + (let* ((arity (and current-prefix-arg + (prefix-numeric-value current-prefix-arg)))) + (if (and (numberp arity) + (< arity 0)) + (list "_" arity) + (let ((functor-arity (sweeprolog-read-functor arity))) + (list (car functor-arity) (cdr functor-arity)))))) + (combine-after-change-calls + (when (use-region-p) + (delete-region (region-beginning) + (region-end))) + (let* ((beg (point))) + (when + (pcase (sweeprolog-last-token-boundaries beg) + (`(symbol ,obeg ,oend) + (let ((op (buffer-substring-no-properties obeg oend))) + (not (or (sweeprolog-op-infix-precedence op) + (sweeprolog-op-prefix-precedence op))))) + (`(close . ,_) t)) + (insert ", ")) + (if (<= 0 arity) + (let ((term-format + (sweeprolog--query-once + "sweep" "sweep_format_term" + (list functor arity + (sweeprolog--precedence-at-point beg))))) + (insert (car term-format)) + (pcase (cdr term-format) + ((or `(compound + "term_position" + 0 ,length + ,_ ,_ + ,holes) + `(compound + "parentheses_term_position" + 0 ,length + (compound + "term_position" + ,_ ,_ ,_ ,_ + ,holes))) + (with-silent-modifications + (dolist (hole holes) + (pcase hole + (`(compound "-" ,hbeg ,hend) + (add-text-properties + (- (point) length (- hbeg)) + (- (point) length (- hend)) + (list + 'sweeprolog-hole t + 'font-lock-face (list (sweeprolog-hole-face)) + 'rear-nonsticky '(sweeprolog-hole + cursor-sensor-functions + font-lock-face)))))))))) + (insert (sweeprolog--hole functor))) + (pcase (sweeprolog-next-token-boundaries) + ('nil (insert ".")) + (`(,kind ,obeg ,oend) + (if (save-excursion + (goto-char obeg) + (sweeprolog-at-beginning-of-top-term-p)) + (insert ".") + (when (or (and (member kind '(symbol operator)) + (let ((op (buffer-substring-no-properties obeg oend))) + (not (or (sweeprolog-op-infix-precedence op) + (sweeprolog-op-suffix-precedence op))))) + (member kind '(open functor))) + (insert ", "))))) + (unless (= 0 arity) + (goto-char beg)) + (sweeprolog-forward-hole)))) + (defun sweeprolog-insert-clause (functor arity &optional neck module) (let ((point (point)) (neck (or neck ":-")) -- 2.39.2