动机
今天整理 yasnippet 脚本时, 发现一个脚本在写 __init__ 时可以自动按参数生成 docstring, 以下是我测试的例子:
我输入了doc, python, yas 这三个参数, 脚本自动为我准备好了对应的 docstring 中的参数文档
def __init__(self, doc, a=b, c=d):
"""
Keyword Arguments:
doc --
a -- (default b)
c -- (default d)
"""
然后我就在想可不可以增加这个脚本,让它可以根据输入的参数,自动生成 self.x=x 的语句( 这样做肯定有多余的语句, 但是删除比自己写快多了)
相关资料整理
原 snippet 脚本, 可以看到调用了 python-args-to-docstring
# -*- mode: snippet -*-
# name: init_docstring
# key: init
# group : definitions
# --
def __init__(self$1):
\"\"\"$2
${1:$(python-args-to-docstring)}
\"\"\"
$0
引用的 elisp 函数
(defun python-split-args (arg-string)
"Split a python argument string into ((name, default)..) tuples"
(mapcar (lambda (x)
(split-string x "[[:blank:]]*=[[:blank:]]*" t))
(split-string arg-string "[[:blank:]]*,[[:blank:]]*" t)))
(split-string STRING &optional SEPARATORS OMIT-NULLS TRIM)
根据 SEPARATORS(正则表达式) 的匹配位置来分割 STRING, (匹配 SEPARATORS 的部分会移除)分割后的 substring 放在 list 中返回.
这个函数首先会根据 「,」和附近的空格对输入的 string 进行分割得到 list, 之后再对每个元素进行「=」的分割, 得到变量名和对应的默认值(如果存在)
(defun python-args-to-docstring ()
"return docstring format for the python arguments in yas-text"
(let* ((indent (concat "\n" (make-string (current-column) 32)))
(args (python-split-args yas-text))
(max-len (if args (apply 'max (mapcar (lambda (x) (length (nth 0 x))) args)) 0))
(formatted-args (mapconcat
(lambda (x)
(concat (nth 0 x) (make-string (- max-len (length (nth 0 x))) ? ) " -- "
(if (nth 1 x) (concat "\(default " (nth 1 x) "\)"))))
args
indent)))
(unless (string= formatted-args "")
(mapconcat 'identity (list "Keyword Arguments:" formatted-args) indent))))
(indent (concat "\n" (make-string (current-column) 32))) 得到的是 \n+多个空格组成的字符串(这也意味着这个脚本比较适合用于空格缩进的python)
args 为处理后得到的 list
max-len 得到了最大变量名的长度, 用于保证 docstring 中的对齐
(formatted-args (mapconcat
(lambda (x)
(concat (nth 0 x) (make-string (- max-len (length (nth 0 x))) ? ) " -- "
(if (nth 1 x) (concat "\(default " (nth 1 x) "\)"))))
args
indent))
这么长一段都是在生成每个变量对应的文档
在let* 的 body 中添加了docstring 的第一行
结果
yas脚本
# -*- mode: snippet -*-
# name: init_docstring
# key: init
# group : definitions
# --
def __init__(self$1):
\"\"\"$2
${1:$(python-args-to-docstring)}
\"\"\"
${1:$(my-python-args-to-assign)}
$0
elisp函数
(defun my-python-args-to-assign()
(let* ((indent (concat "\n" (make-string (current-column) 32)))
(args (python-split-args yas-text)))
(mapconcat
(lambda (x)
(concat "self." (nth 0 x) " = " (nth 0 x)))
args
indent)))
效果
def __init__(self, doc, a=b, c=d):
"""
Keyword Arguments:
doc --
a -- (default b)
c -- (default d)
"""
self.doc = doc
self.a = a
self.c = c