1
1
; ;; -*- lexical-binding : t -*-
2
+
3
+ ; Copyright (C) miscellaneous contributors, see git history
4
+ ; Copyright (C) 2024 Daniel Hornung <[email protected] >
5
+ ;
6
+ ; This program is free software: you can redistribute it and/or modify
7
+ ; it under the terms of the GNU General Public License as
8
+ ; published by the Free Software Foundation, either version 3 of the
9
+ ; License, or (at your option) any later version.
10
+ ;
11
+ ; This program is distributed in the hope that it will be useful,
12
+ ; but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ ; GNU General Public License for more details.
15
+ ;
16
+ ; You should have received a copy of the GNU General Public License
17
+ ; along with this program. If not, see <https://www.gnu.org/licenses/>.
18
+
2
19
(require 'yasnippet )
3
20
(defvar yas-text )
4
21
5
- (defvar python-split-arg-arg-regex
6
- " \\ ([[:alnum:]*]+\\ )\\ (:[[:blank:]]*[[:alpha:]]*\\ )?\\ ([[:blank:]]*=[[:blank:]]*[[:alnum:]]*\\ )?"
22
+ (defvar yas-python-regex-identifier " [[:alnum:]_]+" " Simplified Python identifier." )
23
+ (defvar yas-python-regex-quoted-or-identifier (concat
24
+ " \\ ("
25
+ yas-python-regex-identifier
26
+ " \\ )"
27
+ " \\ |" " \" .*\" "
28
+ " \\ |" " '.*'"
29
+ )
30
+ " Simplified Python identifier or quoted string.
31
+ Does not work well with multiple or escaped quotes" )
32
+
33
+ (defvar python-split-arg-regex
34
+ (concat
35
+ " \\ (" yas-python-regex-identifier " \\ )" ; name
36
+ " \\ (:[[:blank:]]*\\ ([][:alpha:]_[]*\\ )\\ )?" ; type
37
+ " \\ ([[:blank:]]*=[[:blank:]]*\\ ("
38
+ yas-python-regex-quoted-or-identifier ; default
39
+ " \\ )\\ )?"
40
+ )
7
41
" Regular expression matching an argument of a python function.
8
- First group should give the argument name." )
42
+ Groups:
43
+ - 1: the argument name
44
+ - 3: the type
45
+ - 5: the default value" )
9
46
10
47
(defvar python-split-arg-separator
11
48
" [[:space:]]*,[[:space:]]*"
12
49
" Regular expression matching the separator in a list of argument." )
13
50
14
51
(defun python-split-args (arg-string )
15
- " Split a python argument string ARG-STRING into a tuple of argument names."
16
- (mapcar (lambda (x )
17
- (when (string-match python-split-arg-arg-regex x)
18
- (match-string-no-properties 1 x)))
19
- (split-string arg-string python-split-arg-separator t )))
52
+ " Split python argument string ARG-STRING.
53
+
54
+ The result is a list ((name, type, default), ...) of argument names, types and
55
+ default values. An argument named `self` is omitted."
56
+ (remove
57
+ nil
58
+ (mapcar (lambda (x ) ; organize output
59
+ (when (and
60
+ (not (equal " self" x))
61
+ (string-match python-split-arg-regex x)
62
+ )
63
+ (list
64
+ (match-string-no-properties 1 x) ; name
65
+ (match-string-no-properties 3 x) ; type
66
+ (match-string-no-properties 5 x) ; default
67
+ )))
68
+ (split-string arg-string python-split-arg-separator t ))))
20
69
21
70
(defun python-args-to-docstring ()
22
71
" Return docstring format for the python arguments in yas-text."
@@ -26,7 +75,9 @@ First group should give the argument name.")
26
75
(formatted-args (mapconcat
27
76
(lambda (x )
28
77
(concat (nth 0 x) (make-string (- max-len (length (nth 0 x))) ? ) " -- "
29
- (if (nth 1 x) (concat " \( default " (nth 1 x) " \) " ))))
78
+ (if (nth 1 x) (concat (nth 1 x) " : " ))
79
+ (if (nth 2 x) (concat " \( default " (nth 2 x) " \) " ))
80
+ ))
30
81
args
31
82
indent)))
32
83
(unless (string= formatted-args " " )
@@ -36,11 +87,51 @@ First group should give the argument name.")
36
87
" return docstring format for the python arguments in yas-text"
37
88
(let* ((args (python-split-args yas-text))
38
89
(format-arg (lambda (arg )
39
- (concat (nth 0 arg) " : " (if (nth 1 arg) " , optional" ) " \n " )))
90
+ (concat (nth 0 arg) " : " ; name
91
+ (if (nth 1 arg) (nth 1 arg)) ; type TODO handle Optional[Foo] correctly
92
+ (if (nth 2 arg) (concat (when (nth 1 arg) " , " )
93
+ " default=" (nth 2 arg))) ; default
94
+ " \n " )))
40
95
(formatted-params (mapconcat format-arg args " \n " ))
41
96
(formatted-ret (mapconcat format-arg (list (list " out" )) " \n " )))
42
97
(unless (string= formatted-params " " )
43
98
(mapconcat 'identity
44
99
(list " \n Parameters\n ----------" formatted-params
45
100
" \n Returns\n -------" formatted-ret)
46
101
" \n " ))))
102
+
103
+
104
+ ; ; Tests
105
+
106
+ (ert-deftest test-split ()
107
+ " For starters, only test a single string for expected output."
108
+ (should (equal
109
+ (python-split-args " _foo='this', bar: int = 2, baz: Optional[My_Type], foobar" )
110
+ (list '(" _foo" nil " 'this'" )
111
+ '(" bar" " int" " 2" )
112
+ '(" baz" " Optional[My_Type]" nil )
113
+ '(" foobar" nil nil )))
114
+ ))
115
+
116
+ (ert-deftest test-argument-self ()
117
+ " If an argument is called `self`, it must be omitted"
118
+ (should (equal
119
+ (python-split-args " self, _foo=\" this\" " )
120
+ (list '(" _foo" nil " \" this\" " )
121
+ ))
122
+ ))
123
+
124
+ ; ; For manual testing and development:
125
+
126
+ ; ; (setq yas-text "foo=3, bar: int = 2, baz: Optional[MyType], foobar")
127
+ ; ; (split-string yas-text python-split-arg-separator t)
128
+ ; ;
129
+ ; ; (save-match-data
130
+ ; ; (setq my-string "_foo: my_bar = 'this'")
131
+ ; ; (string-match python-split-arg-regex my-string)
132
+ ; ; (match-string 5 my-string)
133
+ ; ; )
134
+ ; ;
135
+ ; ; (python-split-args yas-text)
136
+ ; ; (python-args-to-docstring)
137
+ ; ; (python-args-to-docstring-numpy)
0 commit comments