183 lines
8.1 KiB
EmacsLisp
183 lines
8.1 KiB
EmacsLisp
![]() |
;;; python-docstring.el --- Smart Python docstring formatting
|
||
|
|
||
|
;; Copyright (c) 2014-2015 The Authors
|
||
|
;;
|
||
|
;; Permission is hereby granted, free of charge, to any person obtaining
|
||
|
;; a copy of this software and associated documentation files (the
|
||
|
;; "Software"), to deal in the Software without restriction, including
|
||
|
;; without limitation the rights to use, copy, modify, merge, publish,
|
||
|
;; distribute, sublicense, and/or sell copies of the Software, and to
|
||
|
;; permit persons to whom the Software is furnished to do so, subject to
|
||
|
;; the following conditions:
|
||
|
;;
|
||
|
;; The above copyright notice and this permission notice shall be
|
||
|
;; included in all copies or substantial portions of the Software.
|
||
|
;;
|
||
|
;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
|
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
|
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
|
;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||
|
;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||
|
;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||
|
;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
|
||
|
;;; Commentary:
|
||
|
|
||
|
;; python-docstring-mode.el is a minor mode for intelligently
|
||
|
;; reformatting (refilling) and highlighting Python docstrings. It
|
||
|
;; understands both epytext and Sphinx formats (even intermingled!),
|
||
|
;; so it knows how to reflow them correctly. It will also highlight
|
||
|
;; markup in your docstrings, including epytext and reStructuredText.
|
||
|
|
||
|
;;; Code:
|
||
|
|
||
|
(defcustom python-docstring-sentence-end-double-space t
|
||
|
"If non-nil, use double spaces when formatting text.
|
||
|
|
||
|
Operates simililarly to `sentence-end-double-space'. When nil, a
|
||
|
single space is used."
|
||
|
:type 'boolean
|
||
|
:group 'python-docstring)
|
||
|
|
||
|
(defvar python-docstring-script
|
||
|
(concat (if load-file-name
|
||
|
(file-name-directory load-file-name)
|
||
|
default-directory)
|
||
|
"docstring_wrap.py")
|
||
|
"The location of the docstring_wrap.py script.")
|
||
|
|
||
|
;;;###autoload
|
||
|
(defun python-docstring-fill ()
|
||
|
"Wrap Python docstrings as epytext or ReStructured Text."
|
||
|
(interactive)
|
||
|
(let ((fill-it-anyway nil))
|
||
|
(catch 'not-a-string
|
||
|
(let* ((to-forward
|
||
|
(save-excursion
|
||
|
(let* ((orig-point (point))
|
||
|
(syx (syntax-ppss))
|
||
|
(in-string (if (nth 3 syx) t
|
||
|
(progn
|
||
|
(setf fill-it-anyway t)
|
||
|
(throw 'not-a-string nil))))
|
||
|
(string-start (+ (goto-char (nth 8 syx))
|
||
|
3))
|
||
|
(rawchar (if (eql (char-before (point)) ?r)
|
||
|
1
|
||
|
0))
|
||
|
;; at the beginning of the screen here
|
||
|
(indent-count (- (- string-start (+ rawchar 3))
|
||
|
(save-excursion
|
||
|
(beginning-of-line)
|
||
|
(point))))
|
||
|
(string-end
|
||
|
(- (condition-case () ; for unbalanced quotes
|
||
|
(progn (forward-sexp)
|
||
|
(point))
|
||
|
(error (point-max)))
|
||
|
3))
|
||
|
(orig-offset (- orig-point string-start)))
|
||
|
(let*
|
||
|
((offset-within
|
||
|
(progn
|
||
|
(shell-command-on-region
|
||
|
string-start string-end
|
||
|
(format
|
||
|
(concat "python3 %s --offset %s --indent %s --width %s"
|
||
|
(unless python-docstring-sentence-end-double-space
|
||
|
" --single-space"))
|
||
|
(shell-quote-argument python-docstring-script)
|
||
|
orig-offset
|
||
|
indent-count
|
||
|
fill-column
|
||
|
)
|
||
|
:replace t)
|
||
|
(goto-char string-start)
|
||
|
(forward-sexp)
|
||
|
(string-to-number
|
||
|
(buffer-substring-no-properties string-start orig-point))
|
||
|
)))
|
||
|
(delete-region string-start (+ 1 (point)))
|
||
|
offset-within)))))
|
||
|
(forward-char to-forward)))
|
||
|
(if fill-it-anyway
|
||
|
(call-interactively 'fill-paragraph))))
|
||
|
|
||
|
(defvar python-docstring-field-with-arg-re
|
||
|
"^\\s-*\\([@:]\\)\\(param\\|parameter\\|arg\\|argument\\|type\\|keyword\\|kwarg\\|kwparam\\|raise\\|raises\\|except\\|exception\\|ivar\\|ivariable\\|cvar\\|cvariable\\|var\\|variable\\|type\\|group\\|todo\\|newfield\\)\\s-+\\([a-zA-Z_][a-zA-Z0-9_,. ]*?\\)\\(:\\)")
|
||
|
|
||
|
(defvar python-docstring-field-no-arg-re
|
||
|
"^\\s-*\\([@:]\\)\\(raise\\|raises\\|return\\|returns\\|rtype\\|returntype\\|type\\|sort\\|see\\|seealso\\|note\\|attention\\|bug\\|warning\\|warn\\|version\\|todo\\|deprecated\\|since\\|status\\|change\\|changed\\|permission\\|requires\\|require\\|requirement\\|precondition\\|precond\\|postcondition\\|postcod\\|invariant\\|author\\|organization\\|org\\|copyright\\|(c)\\|license\\|contact\\|summary\\|params\\|param\\)\\(:\\)")
|
||
|
|
||
|
(defvar python-docstring-epytext-markup-link "[UL]{\\([^}]*?\\)\\(<.*?>\\|\\)?}")
|
||
|
(defvar python-docstring-epytext-markup-style-code "C{\\(.*?\\)}")
|
||
|
(defvar python-docstring-epytext-markup-style-italic "I{\\(.*?\\)}")
|
||
|
(defvar python-docstring-epytext-markup-style-bold "B{\\(.*?\\)}")
|
||
|
|
||
|
;; hack for sphinx
|
||
|
(defvar python-docstring-sphinx-markup-link "\\(:[^:]+?:\\)\\(`.+?`\\)")
|
||
|
(defvar python-docstring-sphinx-markup-code "``\\(.+?\\)``")
|
||
|
|
||
|
(defvar python-docstring-keywords
|
||
|
`((,python-docstring-field-with-arg-re 1 font-lock-keyword-face t)
|
||
|
(,python-docstring-field-with-arg-re 2 font-lock-type-face t)
|
||
|
(,python-docstring-field-with-arg-re 3 font-lock-variable-name-face t)
|
||
|
(,python-docstring-field-with-arg-re 4 font-lock-keyword-face t)
|
||
|
(,python-docstring-field-no-arg-re 1 font-lock-keyword-face t)
|
||
|
(,python-docstring-field-no-arg-re 2 font-lock-type-face t)
|
||
|
(,python-docstring-field-no-arg-re 3 font-lock-keyword-face t)
|
||
|
|
||
|
;; :foo:`bar`
|
||
|
(,python-docstring-sphinx-markup-link 1 font-lock-function-name-face t)
|
||
|
(,python-docstring-sphinx-markup-link 2 font-lock-constant-face t)
|
||
|
;; ``bar``
|
||
|
(,python-docstring-sphinx-markup-code 0 font-lock-constant-face t)
|
||
|
;; inline markup - 1
|
||
|
(,python-docstring-sphinx-markup-code 1 '(bold italic) t)
|
||
|
|
||
|
;; L/U - 1
|
||
|
(,python-docstring-epytext-markup-link 0 font-lock-constant-face t)
|
||
|
;; Inline Markup - 1
|
||
|
(,python-docstring-epytext-markup-link 1 font-lock-function-name-face t)
|
||
|
;; Link - 2
|
||
|
(,python-docstring-epytext-markup-link 2 font-lock-keyword-face t)
|
||
|
|
||
|
;; C/I/B - 0
|
||
|
(,python-docstring-epytext-markup-style-code 0 font-lock-constant-face t)
|
||
|
;; inline markup - 1
|
||
|
(,python-docstring-epytext-markup-style-code 1 '(bold italic) t)
|
||
|
;; C/I/B - 0
|
||
|
(,python-docstring-epytext-markup-style-bold 0 font-lock-constant-face t)
|
||
|
;; inline markup - 1
|
||
|
(,python-docstring-epytext-markup-style-bold 1 (quote bold) t)
|
||
|
;; C/I/B - 0
|
||
|
(,python-docstring-epytext-markup-style-italic 0 font-lock-constant-face t)
|
||
|
;; inline markup - 1
|
||
|
(,python-docstring-epytext-markup-style-italic 1 (quote italic) t)))
|
||
|
|
||
|
;;;###autoload
|
||
|
(define-minor-mode python-docstring-mode
|
||
|
"Toggle python-docstring-mode.
|
||
|
With no argument, this command toggles the mode.
|
||
|
Non-null prefix argument turns on the mode.
|
||
|
Null prefix argument turns off the mode."
|
||
|
;; The initial value.
|
||
|
nil
|
||
|
;; The indicator for the mode line.
|
||
|
" DS"
|
||
|
;; The minor mode bindings.
|
||
|
`(([(meta q)] . python-docstring-fill))
|
||
|
;; &rest BODY
|
||
|
(if python-docstring-mode
|
||
|
(font-lock-add-keywords nil python-docstring-keywords)
|
||
|
(font-lock-remove-keywords nil python-docstring-keywords)))
|
||
|
|
||
|
;;;###autoload
|
||
|
(defun python-docstring-install ()
|
||
|
"Add python-docstring-mode as a hook to python.mode."
|
||
|
(add-hook 'python-mode-hook (lambda () (python-docstring-mode t))))
|
||
|
|
||
|
(provide 'python-docstring)
|
||
|
|
||
|
;;; python-docstring.el ends here
|