diff --git a/.gitignore b/.gitignore
index 16ac6a4..ca37bee 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
*.elc
numpydoc-autoloads.el
.DS_Store
+README.html
# Added automatically by ‘eldev init’.
/.eldev
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..40bb156
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,39 @@
+numpydoc.el NEWS -- history of user visable changes
+
+* Unreleased
+
+** Changes
+TBD
+
+* 0.2.0 (Mar 5, 2021)
+
+** Changes
+
+*** Added support for yasnippet
+If yasnippet is installed we we `yas-expand-snippet' to on-the-fly add
+the docstring components in buffer.
+
+*** Added customization `numpydoc-insertion-style'.
+Use this single customization to direct the insertion style instead of
+multiple boolean customization. Can take on:
+- 'prompt (prompt in minibuffer)
+- 'yas (use yasnippet)
+- nil (use insertion helper, just use templates)
+
+*** Added interactive convenience functions for toggling insertion style.
+`numpydoc-use-yasnippet', `numpydoc-use-prompt', and
+`numpydoc-use-templates' are new interactive convenience functions to
+adjust `numpydoc-insertion-style' without having to use
+`eval-expression' and `setq'.
+
+** Removed
+
+*** Removed variable `numpydoc-prompt-for-input'.
+Not needed anymore (use `numpydoc-insertion-style').
+
+*** Removed function `numpydoc-toggle-prompt'.
+Not needed anymore (use numpydoc-use-{yasnippet,prompt,templates}).
+
+* 0.1.0
+
+Initial release
diff --git a/README.md b/README.md
index e9e5f49..260f846 100644
--- a/README.md
+++ b/README.md
@@ -8,19 +8,21 @@ An Emacs Lisp package to automatically insert [NumPy style
docstrings](https://numpydoc.readthedocs.io/en/latest/format.html) for
Python functions.
-Calling `numpydoc-generate` parses a function signature and body
-(corresponding to the current cursor location; just have the cursor
-somewhere in the function you want to document) detecting argument
+Calling `numpydoc-generate` parses the function at point (the cursor
+can be anywhere in the function body). The parsing detects argument
names, type hints, exceptions, and the return type hint. This
information is used to generate a docstring.
-The default behavior is to prompt the user, in the minibuffer, for a
-(short and long) description of the function, a description for each
-function argument, a description for each possible exception, and the
-returned value. If the prompt is off (`numpydoc-prompt-for-input` is
-`nil`), then some customizable template text will be inserted into the
-docstring. If an existing docstring is detected, you'll be asked if
-you'd like to delete it and start fresh.
+The default behavior is to prompt the user (in the minibuffer) for a
+short & long description of the function, a description for each
+function argument, a description for each possible exception, and a
+description for the return. It's also possible to either disable the
+minibuffer prompt or use
+[yasnippet](https://github.com/joaotavora/yasnippet) insertion. See
+[customization](#customization) for more information. You'll also find
+a few [examples](#examples) below. See the
+[NEWS](https://github.com/douglasdavis/numpydoc.el/blob/main/NEWS)
+file to see recent changes.
## Setup
@@ -67,14 +69,20 @@ writing this), so you may want to give yourself a convenient shortcut:
See inside Emacs with M-x customize-group RET numpydoc
- - numpydoc-prompt-for-input
+ - numpydoc-insertion-style
-
- If
t
you will be prompted to enter a short description
- and long description, a description for each function argument, and
- a description for the return (if a return type hint is provided). An
- interactive convenience function
- (numpydoc-toggle-prompt
) is provided to toggle the
- value of this variable.
+ The method used to insert components of the docstring (default is
+ 'prompt
).
+
+ -
'prompt
will trigger a request for each description
+ in the minibuffer.
+ -
'yas
(requires yasnippet
to be
+ installed) will generate a template and call
+ yas-expand-snippet
, providing an insertion method
+ familiar to yasnippet
users.
+ -
nil
will disable any interactive insertion (template
+ text will be inserted).
+
- numpydoc-quote-char
-
@@ -117,18 +125,21 @@ See inside Emacs with M-x customize-group RET numpydoc
## Examples
-M-x numpydoc-generate with the default configuration that
-will prompt for input in the minibuffer (notice how long text is
+M-x numpydoc-generate with the default configuration,
+`numpydoc-insertion-style` set to `'prompt` (notice how long text is
automatically paragraph-filled):
-
+
-Or, M-x numpydoc-generate with
-`numpydoc-prompt-for-input` set to `nil`:
+Using `yasnippet` (`numpydoc-insertion-style` set to `'yas`):
-Before:
+
+
+
+
+With `numpydoc-insertion-style` set to `nil`; before:
```python
def plot_histogram(
@@ -145,7 +156,7 @@ def plot_histogram(
pass
```
-After:
+After M-x numpydoc-generate:
```python
def plot_histogram(
diff --git a/doc/ex1.gif b/doc/ex1.gif
new file mode 100644
index 0000000..b68c8ba
Binary files /dev/null and b/doc/ex1.gif differ
diff --git a/doc/ex2.gif b/doc/ex2.gif
new file mode 100644
index 0000000..ffdaafb
Binary files /dev/null and b/doc/ex2.gif differ
diff --git a/doc/example.gif b/doc/example.gif
deleted file mode 100644
index 1e077d7..0000000
Binary files a/doc/example.gif and /dev/null differ
diff --git a/numpydoc.el b/numpydoc.el
index 0de2c54..8fb0540 100644
--- a/numpydoc.el
+++ b/numpydoc.el
@@ -6,7 +6,7 @@
;; Maintainer: Doug Davis
;; URL: https://github.com/douglasdavis/numpydoc.el
;; SPDX-License-Identifier: GPL-3.0-or-later
-;; Version: 0.1.0
+;; Version: 0.2.0
;; Package-Requires: ((emacs "25.1") (s "1.12.0") (dash "2.18.0"))
;; Keywords: convenience
@@ -30,12 +30,19 @@
;; NumPy docstring style guide can be found at
;; https://numpydoc.readthedocs.io/en/latest/format.html
;;
-;; Customizations include opting in or out of a minibuffer prompt for
-;; entering various components of the docstring (which can be toggled
-;; with `numpydoc-toggle-prompt'), templates for when opting out of
-;; the prompt, the quoting style used, and whether or not to include
-;; an Examples block. See the `numpydoc' customization group.
-
+;; There are three ways that one can be guided to insert descriptions
+;; for the components:
+;;
+;; 1. Minibuffer prompt (the default).
+;; 2. yasnippet expansion (requires `yasnippet' to be installed)
+;; 3. Nothing (template text is inserted).
+;;
+;; Convenience functions are provided to interactively configure the
+;; insertion style symbol:
+;; - `numpydoc-use-prompt'
+;; - `numpydoc-use-yasnippet'
+;; - `numpydoc-use-templates'
+;;
;;; Code:
(require 'cl-lib)
@@ -45,6 +52,10 @@
(require 'dash)
(require 's)
+;; forward declare some yasnippet code.
+(defvar yas-indent-line)
+(declare-function yas-expand-snippet "yasnippet")
+
;;; customization code.
(defgroup numpydoc nil
@@ -52,12 +63,16 @@
:group 'convenience
:prefix "numpydoc-")
-(defcustom numpydoc-prompt-for-input t
- "If t, use minibuffer prompt to enter some docstring components.
-An interactive convenience function, `numpydoc-toggle-prompt', is
-provided to toggle this value via command execution."
+(defcustom numpydoc-insertion-style 'prompt
+ "Which insertion guide to use when generating the docstring.
+When set to 'prompt the minibuffer will be used to prompt for
+docstring components. Setting to 'yas requires yasnippet to be
+installed and `yas-expand-snippet' will be used to insert components.
+When nil, template text will be inserted."
:group 'numpydoc
- :type 'boolean)
+ :type '(choice (const :tag "None" nil)
+ (const :tag "Prompt" prompt)
+ (const :tag "Yasnippet" yas)))
(defcustom numpydoc-quote-char ?\"
"Character for docstring quoting style (double or single quote)."
@@ -110,6 +125,15 @@ text, and below the Examples section."
type
defval)
+(defconst numpydoc--yas-replace-pat "--NPDOCYAS--"
+ "Temporary text to be replaced for yasnippet usage.")
+
+(defun numpydoc--prompt-p ()
+ (eq numpydoc-insertion-style 'prompt))
+
+(defun numpydoc--yas-p ()
+ (eq numpydoc-insertion-style 'yas))
+
(defun numpydoc--arg-str-to-struct (argstr)
"Convert ARGSTR to an instance of `numpydoc--arg'.
The argument takes on one of four possible styles:
@@ -301,19 +325,23 @@ This function assumes the cursor to be in the function body."
(defun numpydoc--insert-short-and-long-desc (indent)
"Insert short description with INDENT level."
- (let ((ld nil))
+ (let ((ld nil)
+ (tmps (cond ((numpydoc--yas-p) numpydoc--yas-replace-pat)
+ (t numpydoc-template-short)))
+ (tmpl (cond ((numpydoc--yas-p) numpydoc--yas-replace-pat)
+ (t numpydoc-template-long))))
(insert "\n")
(numpydoc--insert indent
(concat (make-string 3 numpydoc-quote-char)
- (if numpydoc-prompt-for-input
+ (if (numpydoc--prompt-p)
(read-string
(format "Short description: "))
- numpydoc-template-short)
+ tmps)
"\n\n")
(make-string 3 numpydoc-quote-char))
(forward-line -1)
(beginning-of-line)
- (if numpydoc-prompt-for-input
+ (if (numpydoc--prompt-p)
(progn
(setq ld (read-string (concat "Long description "
"(or press return to skip): ")
@@ -324,7 +352,7 @@ This function assumes the cursor to be in the function body."
(numpydoc--fill-last-insertion)
(insert "\n")))
(insert "\n")
- (numpydoc--insert indent numpydoc-template-long)
+ (numpydoc--insert indent tmpl)
(insert "\n"))))
(defun numpydoc--insert-item (indent name &optional type)
@@ -336,21 +364,25 @@ This function assumes the cursor to be in the function body."
(defun numpydoc--insert-item-and-type (indent name type)
"Insert parameter with NAME and TYPE at level INDENT."
- (let ((tp type))
+ (let ((tp type)
+ (tmpt (cond ((numpydoc--yas-p) numpydoc--yas-replace-pat)
+ (t numpydoc-template-type-desc))))
(unless tp
- (setq tp (if numpydoc-prompt-for-input
+ (setq tp (if (numpydoc--prompt-p)
(read-string (format "Type of %s: "
name))
- numpydoc-template-type-desc)))
+ tmpt)))
(numpydoc--insert indent (format "%s : %s\n" name tp))))
(defun numpydoc--insert-item-desc (indent element)
"Insert ELEMENT parameter description at level INDENT."
- (let ((desc (concat (make-string 4 ?\s)
- (if numpydoc-prompt-for-input
+ (let* ((tmpd (cond ((numpydoc--yas-p) numpydoc--yas-replace-pat)
+ (t numpydoc-template-desc)))
+ (desc (concat (make-string 4 ?\s)
+ (if (numpydoc--prompt-p)
(read-string (format "Description for %s: "
element))
- numpydoc-template-desc))))
+ tmpd))))
(numpydoc--insert indent desc)
(numpydoc--fill-last-insertion)
(insert "\n")))
@@ -371,20 +403,22 @@ This function assumes the cursor to be in the function body."
(defun numpydoc--insert-return (indent fnret)
"Insert FNRET (return) description (if exists) at INDENT level."
- (when (and fnret (not (string= fnret "None")))
- (insert "\n")
- (numpydoc--insert indent
- "Returns\n"
- "-------\n"
- fnret)
- (insert "\n")
- (numpydoc--insert indent
- (concat (make-string 4 ?\s)
- (if numpydoc-prompt-for-input
- (read-string "Description for return: ")
- numpydoc-template-desc)))
- (numpydoc--fill-last-insertion)
- (insert "\n")))
+ (let ((tmpr (cond ((numpydoc--yas-p) numpydoc--yas-replace-pat)
+ (t numpydoc-template-desc))))
+ (when (and fnret (not (string= fnret "None")))
+ (insert "\n")
+ (numpydoc--insert indent
+ "Returns\n"
+ "-------\n"
+ fnret)
+ (insert "\n")
+ (numpydoc--insert indent
+ (concat (make-string 4 ?\s)
+ (if (numpydoc--prompt-p)
+ (read-string "Description for return: ")
+ tmpr)))
+ (numpydoc--fill-last-insertion)
+ (insert "\n"))))
(defun numpydoc--insert-exceptions (indent fnexcepts)
"Insert FNEXCEPTS (exception) elements at INDENT level."
@@ -399,12 +433,42 @@ This function assumes the cursor to be in the function body."
(defun numpydoc--insert-examples (indent)
"Insert function examples block at INDENT level."
- (when numpydoc-insert-examples-block
- (insert "\n")
- (numpydoc--insert indent
- "Examples\n"
- "--------\n"
- (concat numpydoc-template-desc "\n"))))
+ (let ((tmpd (cond ((numpydoc--yas-p) numpydoc--yas-replace-pat)
+ (t numpydoc-template-desc))))
+ (when numpydoc-insert-examples-block
+ (insert "\n")
+ (numpydoc--insert indent
+ "Examples\n"
+ "--------\n"
+ (concat tmpd "\n")))))
+
+(defun numpydoc--yasnippetfy ()
+ "Take the template and convert to yasnippet then execute."
+ ;; replace the template
+ (save-excursion
+ (python-nav-beginning-of-defun)
+ (let ((i 1)
+ (start (point)))
+ (goto-char start)
+ (while (re-search-forward numpydoc--yas-replace-pat nil t)
+ (replace-match (format "${%s}" i))
+ (setq i (+ 1 i)))))
+ ;; execute the yasnippet
+ (save-excursion
+ (let ((ds-start (progn
+ (python-nav-beginning-of-statement)
+ (forward-char 3)
+ (point)))
+ (ds-end (progn
+ (python-nav-end-of-statement)
+ (forward-char -3)
+ (point))))
+ (goto-char ds-start)
+ (set-mark-command nil)
+ (goto-char ds-end)
+ (kill-region 1 1 t)
+ (yas-expand-snippet (current-kill 0 t)
+ nil nil '((yas-indent-line 'nothing))))))
(defun numpydoc--insert-docstring (indent fndef)
"Insert FNDEF with indentation level INDENT."
@@ -412,7 +476,9 @@ This function assumes the cursor to be in the function body."
(numpydoc--insert-parameters indent (numpydoc--def-args fndef))
(numpydoc--insert-return indent (numpydoc--def-rtype fndef))
(numpydoc--insert-exceptions indent (numpydoc--def-raises fndef))
- (numpydoc--insert-examples indent))
+ (numpydoc--insert-examples indent)
+ (when (numpydoc--yas-p)
+ (numpydoc--yasnippetfy)))
(defun numpydoc--delete-existing ()
"Delete existing docstring."
@@ -433,10 +499,22 @@ This function assumes the cursor to be in the function body."
;;; public API
;;;###autoload
-(defun numpydoc-toggle-prompt ()
- "Toggle the value of `numpydoc-prompt-for-input'."
+(defun numpydoc-use-yasnippet ()
+ "Enable yasnippet insertion (see `numpydoc-insertion-style')."
+ (interactive)
+ (setq numpydoc-insertion-style 'yas))
+
+;;;###autoload
+(defun numpydoc-use-prompt ()
+ "Enable minibuffer prompt insertion (see `numpydoc-insertion-style')."
+ (interactive)
+ (setq numpydoc-insertion-style 'prompt))
+
+;;;###autoload
+(defun numpydoc-use-templates ()
+ "Enable template text insertion (see `numpydoc-insertion-style')."
(interactive)
- (setq numpydoc-prompt-for-input (not numpydoc-prompt-for-input)))
+ (setq numpydoc-insertion-style nil))
;;;###autoload
(defun numpydoc-generate ()