From 0bb2a103bb5e279024fe385702484eb92bd35cb6 Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Tue, 30 May 2023 22:29:34 +0300 Subject: [PATCH] ADDED: command for inserting example usage comments * sweeprolog.el (sweeprolog-top-level-example-mode): New minor mode. (sweeprolog-make-example-usage-comment): New command. (sweeprolog-mode-map): Bind it. * README.org (Example Usage Comments): New section. --- README.org | 36 +++++++++++++++++++++++ sweeprolog.el | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/README.org b/README.org index 2864632..6d43662 100644 --- a/README.org +++ b/README.org @@ -1511,6 +1511,42 @@ documented in [[info:emacs#Text][Commands for Human Languages]], which see. For more information about =PlDoc= and source documentation in SWI-Prolog, see [[https://www.swi-prolog.org/pldoc/doc_for?object=section(%27packages/pldoc.html%27)][the PlDoc manual]]. +** Example Usage Comments +:PROPERTIES: +:CUSTOM_ID: usage-comments +:DESCRIPTION: Commands for inserting comments that show example usage of your code +:ALT_TITLE: Usage Comments +:END: + +Beyond documenting your code with =PlDoc= comments as described in +[[#sweeprolog-pldoc][Documenting Predicates]], you might want to have comments in your source +code that shows example usage of some predicate. Creating such +comments usually involves posting queries in a Prolog top-level, +copying the queries and their results into the relevant source code +buffer, and formatting them as comments. Sweep provides the following +command to streamline this process: + +#+FINDEX: sweeprolog-make-example-usage-comment +- Key: C-c C-% (sweeprolog-make-example-usage-comment) :: Start a + new top-level for recording example usage. When you finish + interacting with the top-level its contents are formatted as a + comment in the buffer and position where you invoked this command. + +The command ~sweeprolog-make-example-usage-comment~, bound to ~C-c +C-%~ in ~sweeprolog-mode~ buffers, creates and switches to a new +top-level buffer for recording example usage that you want to +demonstrate. The /example usage top-level/ is a regular top-level +buffer (see [[*The Prolog Top-Level][The Prolog Top-Level]]), except that it's tied to the +specific position in the source buffer where you invoke this command. +You can post queries in the example usage top-level and edit it +freely, then type ~C-c C-q~ in to quit the top-level buffer and format +its contents as a comment in the source buffer. + +You can have multiple example usage top-levels for different parts of +your code at the same time. To display the source position where you +created a certain usage example top-level buffer by, type ~C-c C-b~ in +that buffer. + ** Displaying Predicate Documentation :PROPERTIES: :CUSTOM_ID: eldoc-integration diff --git a/sweeprolog.el b/sweeprolog.el index c90c30d..240d264 100644 --- a/sweeprolog.el +++ b/sweeprolog.el @@ -448,6 +448,7 @@ use `autoload/2' for all added directives." #'sweeprolog-show-diagnostics #'flymake-show-diagnostics-buffer)) (define-key map (kbd "C-c C-&") #'sweeprolog-async-goal) + (define-key map (kbd "C-c C-%") #'sweeprolog-make-example-usage-comment) (define-key map (kbd "C-c C--") #'sweeprolog-decrement-numbered-variables) (define-key map (kbd "C-c C-+") #'sweeprolog-increment-numbered-variables) (define-key map (kbd "C-M-^") #'kill-backward-up-list) @@ -518,6 +519,13 @@ use `autoload/2' for all added directives." map) "Keymap for moving to next hole with TAB.") +(defvar sweeprolog-top-level-example-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-c C-b") #'sweeprolog-top-level-example-display-source) + (define-key map (kbd "C-c C-q") #'sweeprolog-top-level-example-done) + map) + "Keymap for example top-level buffer.") + ;;;; Menu bar (easy-menu-define sweeprolog-menu (list sweeprolog-mode-map @@ -605,6 +613,8 @@ use `autoload/2' for all added directives." (defvar-local sweeprolog-top-level-thread-id nil "Prolog top-level thread ID corresponding to this buffer.") +(defvar-local sweeprolog-top-level-example-marker nil) + (defvar-local sweeprolog--buffer-last-modified-time nil) (defvar-local sweeprolog--buffer-modified nil) @@ -6433,6 +6443,75 @@ Return nil if POS is not the beginning of a macro invocation." (unless (sweeprolog-expand-macro-at-pos point) (user-error "No macro invocation at point"))) +(define-minor-mode sweeprolog-top-level-example-mode + "Minor mode for example top-level sessions. +This mode is enabled in top-level buffers created by +\\[sweeprolog-make-example-usage-comment]." + :lighter " Example" + :group 'sweeprolog) + +(defun sweeprolog-top-level-example-display-source () + "Pop to the source position where this example session was started. +This is the position where +`sweeprolog-make-example-usage-comment' was invoked to create +the current top-level buffer, and where +\\[sweeprolog-top-level-example-done] inserts its contents of as +a comment." + (interactive "" sweeprolog-top-level-mode) + (unless sweeprolog-top-level-example-mode + (user-error "Not in an example top-level session")) + (let ((source-buffer (marker-buffer sweeprolog-top-level-example-marker))) + (unless (buffer-live-p source-buffer) + (user-error "Source buffer for this example session no longer alive")) + (let ((marker sweeprolog-top-level-example-marker)) + (display-buffer source-buffer) + (goto-char marker)))) + +(defun sweeprolog-top-level-example-done () + "Finalize the current example top-level session. +This kills the current top-level buffer and inserts its contents +as a comment in the source location where you invoked +`sweeprolog-make-example-usage-comment' to create it." + (interactive "" sweeprolog-top-level-mode) + (unless sweeprolog-top-level-example-mode + (user-error "Not in an example top-level session")) + (let ((source-buffer (marker-buffer sweeprolog-top-level-example-marker))) + (unless (buffer-live-p source-buffer) + (user-error "Source buffer for this example session no longer alive")) + (let ((top-level-buffer (current-buffer)) + (example (replace-regexp-in-string + (rx "?- " eos) "" + (string-replace + "\n\n" "\n" + (buffer-substring-no-properties (point-min) + (point-max))))) + (marker sweeprolog-top-level-example-marker)) + (pop-to-buffer source-buffer) + (unless (string-empty-p example) + (save-excursion + (goto-char marker) + (insert example) + (comment-region marker (point)))) + (delete-process (get-buffer-process top-level-buffer)) + (kill-buffer top-level-buffer)))) + +(defun sweeprolog-make-example-usage-comment (point) + "Start a top-level and record it as a comment at POINT. +This creates a new example top-level buffer where you can perform +queries in this top-level as usual. Use +\\\\[sweeprolog-top-level-example-done] +in the example top-level buffer to finish and format the session +as a comment in the source buffer at starting at POINT." + (interactive "d" sweeprolog-mode) + (let ((marker (copy-marker point)) + (buffer (sweeprolog-top-level-buffer (generate-new-buffer-name + "*Example Session*")))) + (pop-to-buffer buffer) + (setq sweeprolog-top-level-example-marker marker + header-line-format (substitute-command-keys + (format "`\\\\[sweeprolog-top-level-example-done]' to quit and write contents as a comment in buffer %s" (buffer-name (marker-buffer marker))))) + (sweeprolog-top-level-example-mode))) + ;;;; Footer (provide 'sweeprolog) -- 2.39.5