Skip to content

Commit 1549f36

Browse files
authoredMar 5, 2021
Adjusting some customization options; add yasnippet support. (#3)
* yasnippet works; needs more documenation. * Improving prompt insertion configuration. * helper -> style * add NEWS and improve docs.
1 parent fcbdeca commit 1549f36

File tree

7 files changed

+200
-71
lines changed

7 files changed

+200
-71
lines changed
 

‎.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
*.elc
22
numpydoc-autoloads.el
33
.DS_Store
4+
README.html
45

56
# Added automatically by ‘eldev init’.
67
/.eldev

‎NEWS

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
numpydoc.el NEWS -- history of user visable changes
2+
3+
* Unreleased
4+
5+
** Changes
6+
TBD
7+
8+
* 0.2.0 (Mar 5, 2021)
9+
10+
** Changes
11+
12+
*** Added support for yasnippet
13+
If yasnippet is installed we we `yas-expand-snippet' to on-the-fly add
14+
the docstring components in buffer.
15+
16+
*** Added customization `numpydoc-insertion-style'.
17+
Use this single customization to direct the insertion style instead of
18+
multiple boolean customization. Can take on:
19+
- 'prompt (prompt in minibuffer)
20+
- 'yas (use yasnippet)
21+
- nil (use insertion helper, just use templates)
22+
23+
*** Added interactive convenience functions for toggling insertion style.
24+
`numpydoc-use-yasnippet', `numpydoc-use-prompt', and
25+
`numpydoc-use-templates' are new interactive convenience functions to
26+
adjust `numpydoc-insertion-style' without having to use
27+
`eval-expression' and `setq'.
28+
29+
** Removed
30+
31+
*** Removed variable `numpydoc-prompt-for-input'.
32+
Not needed anymore (use `numpydoc-insertion-style').
33+
34+
*** Removed function `numpydoc-toggle-prompt'.
35+
Not needed anymore (use numpydoc-use-{yasnippet,prompt,templates}).
36+
37+
* 0.1.0
38+
39+
Initial release

‎README.md

+35-24
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,21 @@ An Emacs Lisp package to automatically insert [NumPy style
88
docstrings](https://numpydoc.readthedocs.io/en/latest/format.html) for
99
Python functions.
1010

11-
Calling `numpydoc-generate` parses a function signature and body
12-
(corresponding to the current cursor location; just have the cursor
13-
somewhere in the function you want to document) detecting argument
11+
Calling `numpydoc-generate` parses the function at point (the cursor
12+
can be anywhere in the function body). The parsing detects argument
1413
names, type hints, exceptions, and the return type hint. This
1514
information is used to generate a docstring.
1615

17-
The default behavior is to prompt the user, in the minibuffer, for a
18-
(short and long) description of the function, a description for each
19-
function argument, a description for each possible exception, and the
20-
returned value. If the prompt is off (`numpydoc-prompt-for-input` is
21-
`nil`), then some customizable template text will be inserted into the
22-
docstring. If an existing docstring is detected, you'll be asked if
23-
you'd like to delete it and start fresh.
16+
The default behavior is to prompt the user (in the minibuffer) for a
17+
short & long description of the function, a description for each
18+
function argument, a description for each possible exception, and a
19+
description for the return. It's also possible to either disable the
20+
minibuffer prompt or use
21+
[yasnippet](https://github.com/joaotavora/yasnippet) insertion. See
22+
[customization](#customization) for more information. You'll also find
23+
a few [examples](#examples) below. See the
24+
[NEWS](https://github.com/douglasdavis/numpydoc.el/blob/main/NEWS)
25+
file to see recent changes.
2426

2527
## Setup
2628

@@ -67,14 +69,20 @@ writing this), so you may want to give yourself a convenient shortcut:
6769
See inside Emacs with <kbd>M-x customize-group RET numpydoc</kbd>
6870

6971
<dl>
70-
<dt>numpydoc-prompt-for-input</dt>
72+
<dt>numpydoc-insertion-style</dt>
7173
<dd>
72-
If <code>t</code> you will be prompted to enter a short description
73-
and long description, a description for each function argument, and
74-
a description for the return (if a return type hint is provided). An
75-
interactive convenience function
76-
(<code>numpydoc-toggle-prompt</code>) is provided to toggle the
77-
value of this variable.
74+
The method used to insert components of the docstring (default is
75+
<code>'prompt</code>).
76+
<ul>
77+
<li> <code>'prompt</code> will trigger a request for each description
78+
in the minibuffer.</li>
79+
<li> <code>'yas</code> (requires <code>yasnippet</code> to be
80+
installed) will generate a template and call
81+
<code>yas-expand-snippet</code>, providing an insertion method
82+
familiar to <code>yasnippet</code> users.</li>
83+
<li> <code>nil</code> will disable any interactive insertion (template
84+
text will be inserted).</li>
85+
</ul>
7886
</dd>
7987
<dt>numpydoc-quote-char</dt>
8088
<dd>
@@ -117,18 +125,21 @@ See inside Emacs with <kbd>M-x customize-group RET numpydoc</kbd>
117125

118126
## Examples
119127

120-
<kbd>M-x numpydoc-generate</kbd> with the default configuration that
121-
will prompt for input in the minibuffer (notice how long text is
128+
<kbd>M-x numpydoc-generate</kbd> with the default configuration,
129+
`numpydoc-insertion-style` set to `'prompt` (notice how long text is
122130
automatically paragraph-filled):
123131

124132
<p align="center">
125-
<img src="doc/example.gif" style="border-radius:10px"/>
133+
<img src="doc/ex1.gif" width="65%"/>
126134
</p>
127135

128-
Or, <kbd>M-x numpydoc-generate</kbd> with
129-
`numpydoc-prompt-for-input` set to `nil`:
136+
Using `yasnippet` (`numpydoc-insertion-style` set to `'yas`):
130137

131-
Before:
138+
<p align="center">
139+
<img src="doc/ex2.gif" width="65%"/>
140+
</p>
141+
142+
With `numpydoc-insertion-style` set to `nil`; before:
132143

133144
```python
134145
def plot_histogram(
@@ -145,7 +156,7 @@ def plot_histogram(
145156
pass
146157
```
147158

148-
After:
159+
After <kbd>M-x numpydoc-generate</kbd>:
149160

150161
```python
151162
def plot_histogram(

‎doc/ex1.gif

2.33 MB
Loading

‎doc/ex2.gif

1.68 MB
Loading

‎doc/example.gif

-395 KB
Binary file not shown.

‎numpydoc.el

+125-47
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
;; Maintainer: Doug Davis <ddavis@ddavis.io>
77
;; URL: https://github.com/douglasdavis/numpydoc.el
88
;; SPDX-License-Identifier: GPL-3.0-or-later
9-
;; Version: 0.1.0
9+
;; Version: 0.2.0
1010
;; Package-Requires: ((emacs "25.1") (s "1.12.0") (dash "2.18.0"))
1111
;; Keywords: convenience
1212

@@ -30,12 +30,19 @@
3030
;; NumPy docstring style guide can be found at
3131
;; https://numpydoc.readthedocs.io/en/latest/format.html
3232
;;
33-
;; Customizations include opting in or out of a minibuffer prompt for
34-
;; entering various components of the docstring (which can be toggled
35-
;; with `numpydoc-toggle-prompt'), templates for when opting out of
36-
;; the prompt, the quoting style used, and whether or not to include
37-
;; an Examples block. See the `numpydoc' customization group.
38-
33+
;; There are three ways that one can be guided to insert descriptions
34+
;; for the components:
35+
;;
36+
;; 1. Minibuffer prompt (the default).
37+
;; 2. yasnippet expansion (requires `yasnippet' to be installed)
38+
;; 3. Nothing (template text is inserted).
39+
;;
40+
;; Convenience functions are provided to interactively configure the
41+
;; insertion style symbol:
42+
;; - `numpydoc-use-prompt'
43+
;; - `numpydoc-use-yasnippet'
44+
;; - `numpydoc-use-templates'
45+
;;
3946
;;; Code:
4047

4148
(require 'cl-lib)
@@ -45,19 +52,27 @@
4552
(require 'dash)
4653
(require 's)
4754

55+
;; forward declare some yasnippet code.
56+
(defvar yas-indent-line)
57+
(declare-function yas-expand-snippet "yasnippet")
58+
4859
;;; customization code.
4960

5061
(defgroup numpydoc nil
5162
"NumPy docstrings."
5263
:group 'convenience
5364
:prefix "numpydoc-")
5465

55-
(defcustom numpydoc-prompt-for-input t
56-
"If t, use minibuffer prompt to enter some docstring components.
57-
An interactive convenience function, `numpydoc-toggle-prompt', is
58-
provided to toggle this value via command execution."
66+
(defcustom numpydoc-insertion-style 'prompt
67+
"Which insertion guide to use when generating the docstring.
68+
When set to 'prompt the minibuffer will be used to prompt for
69+
docstring components. Setting to 'yas requires yasnippet to be
70+
installed and `yas-expand-snippet' will be used to insert components.
71+
When nil, template text will be inserted."
5972
:group 'numpydoc
60-
:type 'boolean)
73+
:type '(choice (const :tag "None" nil)
74+
(const :tag "Prompt" prompt)
75+
(const :tag "Yasnippet" yas)))
6176

6277
(defcustom numpydoc-quote-char ?\"
6378
"Character for docstring quoting style (double or single quote)."
@@ -110,6 +125,15 @@ text, and below the Examples section."
110125
type
111126
defval)
112127

128+
(defconst numpydoc--yas-replace-pat "--NPDOCYAS--"
129+
"Temporary text to be replaced for yasnippet usage.")
130+
131+
(defun numpydoc--prompt-p ()
132+
(eq numpydoc-insertion-style 'prompt))
133+
134+
(defun numpydoc--yas-p ()
135+
(eq numpydoc-insertion-style 'yas))
136+
113137
(defun numpydoc--arg-str-to-struct (argstr)
114138
"Convert ARGSTR to an instance of `numpydoc--arg'.
115139
The argument takes on one of four possible styles:
@@ -301,19 +325,23 @@ This function assumes the cursor to be in the function body."
301325

302326
(defun numpydoc--insert-short-and-long-desc (indent)
303327
"Insert short description with INDENT level."
304-
(let ((ld nil))
328+
(let ((ld nil)
329+
(tmps (cond ((numpydoc--yas-p) numpydoc--yas-replace-pat)
330+
(t numpydoc-template-short)))
331+
(tmpl (cond ((numpydoc--yas-p) numpydoc--yas-replace-pat)
332+
(t numpydoc-template-long))))
305333
(insert "\n")
306334
(numpydoc--insert indent
307335
(concat (make-string 3 numpydoc-quote-char)
308-
(if numpydoc-prompt-for-input
336+
(if (numpydoc--prompt-p)
309337
(read-string
310338
(format "Short description: "))
311-
numpydoc-template-short)
339+
tmps)
312340
"\n\n")
313341
(make-string 3 numpydoc-quote-char))
314342
(forward-line -1)
315343
(beginning-of-line)
316-
(if numpydoc-prompt-for-input
344+
(if (numpydoc--prompt-p)
317345
(progn
318346
(setq ld (read-string (concat "Long description "
319347
"(or press return to skip): ")
@@ -324,7 +352,7 @@ This function assumes the cursor to be in the function body."
324352
(numpydoc--fill-last-insertion)
325353
(insert "\n")))
326354
(insert "\n")
327-
(numpydoc--insert indent numpydoc-template-long)
355+
(numpydoc--insert indent tmpl)
328356
(insert "\n"))))
329357

330358
(defun numpydoc--insert-item (indent name &optional type)
@@ -336,21 +364,25 @@ This function assumes the cursor to be in the function body."
336364

337365
(defun numpydoc--insert-item-and-type (indent name type)
338366
"Insert parameter with NAME and TYPE at level INDENT."
339-
(let ((tp type))
367+
(let ((tp type)
368+
(tmpt (cond ((numpydoc--yas-p) numpydoc--yas-replace-pat)
369+
(t numpydoc-template-type-desc))))
340370
(unless tp
341-
(setq tp (if numpydoc-prompt-for-input
371+
(setq tp (if (numpydoc--prompt-p)
342372
(read-string (format "Type of %s: "
343373
name))
344-
numpydoc-template-type-desc)))
374+
tmpt)))
345375
(numpydoc--insert indent (format "%s : %s\n" name tp))))
346376

347377
(defun numpydoc--insert-item-desc (indent element)
348378
"Insert ELEMENT parameter description at level INDENT."
349-
(let ((desc (concat (make-string 4 ?\s)
350-
(if numpydoc-prompt-for-input
379+
(let* ((tmpd (cond ((numpydoc--yas-p) numpydoc--yas-replace-pat)
380+
(t numpydoc-template-desc)))
381+
(desc (concat (make-string 4 ?\s)
382+
(if (numpydoc--prompt-p)
351383
(read-string (format "Description for %s: "
352384
element))
353-
numpydoc-template-desc))))
385+
tmpd))))
354386
(numpydoc--insert indent desc)
355387
(numpydoc--fill-last-insertion)
356388
(insert "\n")))
@@ -371,20 +403,22 @@ This function assumes the cursor to be in the function body."
371403

372404
(defun numpydoc--insert-return (indent fnret)
373405
"Insert FNRET (return) description (if exists) at INDENT level."
374-
(when (and fnret (not (string= fnret "None")))
375-
(insert "\n")
376-
(numpydoc--insert indent
377-
"Returns\n"
378-
"-------\n"
379-
fnret)
380-
(insert "\n")
381-
(numpydoc--insert indent
382-
(concat (make-string 4 ?\s)
383-
(if numpydoc-prompt-for-input
384-
(read-string "Description for return: ")
385-
numpydoc-template-desc)))
386-
(numpydoc--fill-last-insertion)
387-
(insert "\n")))
406+
(let ((tmpr (cond ((numpydoc--yas-p) numpydoc--yas-replace-pat)
407+
(t numpydoc-template-desc))))
408+
(when (and fnret (not (string= fnret "None")))
409+
(insert "\n")
410+
(numpydoc--insert indent
411+
"Returns\n"
412+
"-------\n"
413+
fnret)
414+
(insert "\n")
415+
(numpydoc--insert indent
416+
(concat (make-string 4 ?\s)
417+
(if (numpydoc--prompt-p)
418+
(read-string "Description for return: ")
419+
tmpr)))
420+
(numpydoc--fill-last-insertion)
421+
(insert "\n"))))
388422

389423
(defun numpydoc--insert-exceptions (indent fnexcepts)
390424
"Insert FNEXCEPTS (exception) elements at INDENT level."
@@ -399,20 +433,52 @@ This function assumes the cursor to be in the function body."
399433

400434
(defun numpydoc--insert-examples (indent)
401435
"Insert function examples block at INDENT level."
402-
(when numpydoc-insert-examples-block
403-
(insert "\n")
404-
(numpydoc--insert indent
405-
"Examples\n"
406-
"--------\n"
407-
(concat numpydoc-template-desc "\n"))))
436+
(let ((tmpd (cond ((numpydoc--yas-p) numpydoc--yas-replace-pat)
437+
(t numpydoc-template-desc))))
438+
(when numpydoc-insert-examples-block
439+
(insert "\n")
440+
(numpydoc--insert indent
441+
"Examples\n"
442+
"--------\n"
443+
(concat tmpd "\n")))))
444+
445+
(defun numpydoc--yasnippetfy ()
446+
"Take the template and convert to yasnippet then execute."
447+
;; replace the template
448+
(save-excursion
449+
(python-nav-beginning-of-defun)
450+
(let ((i 1)
451+
(start (point)))
452+
(goto-char start)
453+
(while (re-search-forward numpydoc--yas-replace-pat nil t)
454+
(replace-match (format "${%s}" i))
455+
(setq i (+ 1 i)))))
456+
;; execute the yasnippet
457+
(save-excursion
458+
(let ((ds-start (progn
459+
(python-nav-beginning-of-statement)
460+
(forward-char 3)
461+
(point)))
462+
(ds-end (progn
463+
(python-nav-end-of-statement)
464+
(forward-char -3)
465+
(point))))
466+
(goto-char ds-start)
467+
(set-mark-command nil)
468+
(goto-char ds-end)
469+
(kill-region 1 1 t)
470+
(yas-expand-snippet (current-kill 0 t)
471+
nil nil '((yas-indent-line 'nothing))))))
408472

409473
(defun numpydoc--insert-docstring (indent fndef)
410474
"Insert FNDEF with indentation level INDENT."
411475
(numpydoc--insert-short-and-long-desc indent)
412476
(numpydoc--insert-parameters indent (numpydoc--def-args fndef))
413477
(numpydoc--insert-return indent (numpydoc--def-rtype fndef))
414478
(numpydoc--insert-exceptions indent (numpydoc--def-raises fndef))
415-
(numpydoc--insert-examples indent))
479+
(numpydoc--insert-examples indent)
480+
(when (numpydoc--yas-p)
481+
(numpydoc--yasnippetfy)))
416482

417483
(defun numpydoc--delete-existing ()
418484
"Delete existing docstring."
@@ -433,10 +499,22 @@ This function assumes the cursor to be in the function body."
433499
;;; public API
434500

435501
;;;###autoload
436-
(defun numpydoc-toggle-prompt ()
437-
"Toggle the value of `numpydoc-prompt-for-input'."
502+
(defun numpydoc-use-yasnippet ()
503+
"Enable yasnippet insertion (see `numpydoc-insertion-style')."
504+
(interactive)
505+
(setq numpydoc-insertion-style 'yas))
506+
507+
;;;###autoload
508+
(defun numpydoc-use-prompt ()
509+
"Enable minibuffer prompt insertion (see `numpydoc-insertion-style')."
510+
(interactive)
511+
(setq numpydoc-insertion-style 'prompt))
512+
513+
;;;###autoload
514+
(defun numpydoc-use-templates ()
515+
"Enable template text insertion (see `numpydoc-insertion-style')."
438516
(interactive)
439-
(setq numpydoc-prompt-for-input (not numpydoc-prompt-for-input)))
517+
(setq numpydoc-insertion-style nil))
440518

441519
;;;###autoload
442520
(defun numpydoc-generate ()

0 commit comments

Comments
 (0)
Please sign in to comment.