3

I am trying to write a docstring that follows Google's style guide such that it looks something like:

def testFunction(a, b):
    """
    Args:
        a (str):
        b (str):

    Returns:
        Description of return

I am doing this through a yasnippet following the accepted answer to this question, and everything works except for the indentation of the "return description" because my emacs isn't allowing an indentation beyond the first level within the docstring using TAB. Using indent within a lisp function gives the correct indentation (under Args:), but using TAB within the buffer (or in the snippet) does not. As such my docstring looks like:

def testFunction(a, b):
    """
    Args:
        a (str):
        b (str):

    Returns:
    Description of return

Any ideas to overcome the general problem of not being able to force another layer of indent within the docstring using TAB in python-mode?

I've tried making a barebones init.el to avoid loading anything that may create this behavior but still get the same issue. I am using emacs version 24.5.1, in case that provides any clues.

chasely
  • 221
  • 1
  • 7

2 Answers2

3

I had this issue for quite a while and I couldn't figure out how to change it. It didn't affect me much during my day to day work however, so I usually fixed it manually.

I did however find a fix recently however. It is detailed here:

https://github.com/jorgenschaefer/elpy/issues/498

Basically, I now use the mmm-mode-package to make all docstrings behave as they are in rst-mode. This may be overkill for the Google style, but you could use almost the same configuration but change the sub-mode to use e.g., text-mode instead.

The configuration I'm using can be seen below (using use-package):

(use-package mmm-mode
  :ensure t
  :defer t
  :commands mmm-mode
  :init
  (add-hook 'python-mode-hook 'mmm-mode)
  :config

  ;; Add python + rst major mode configuration.
  (defun rst-python-statement-is-docstring (begin)
    "Return true if beginning of statement is BEGIN."
    (save-excursion
      (save-match-data
        (python-nav-beginning-of-statement)
        (looking-at-p begin))))

  (defun rst-python-front-verify ()
    "Verify that we're looking at a python docstring."
    (rst-python-statement-is-docstring (match-string 0)))

  (setq mmm-parse-when-idle 't)
  (add-to-list 'mmm-save-local-variables 'adaptive-fill-regexp)
  (add-to-list 'mmm-save-local-variables 'fill-paragraph-function)

  (mmm-add-classes
   '((rst-python-docstrings
      :submode rst-mode
      :face mmm-comment-submode-face
      :front "u?\\(\"\"\"\\|\'\'\'\\)"
      :front-verify rst-python-front-verify
      :back "~1"
      :end-not-begin t
      :save-matches 1
      :insert ((?d embdocstring nil @ "u\"\"\"" @ _ @ "\"\"\"" @))
      :delimiter-mode nil)))

  (mmm-add-mode-ext-class 'python-mode nil 'rst-python-docstrings))

I should note that mixing major modes in this fashion is usually frowned upon (as you can read in the github thread) and it may introduce various subtle issues. Personally however, I haven't run into any issues yet.

Xaldew
  • 1,181
  • 9
  • 20
  • This does indeed work, though I think I'll use the manual method instead of mixing major nodes. – chasely Mar 20 '16 at 01:16
0

As a simpler (read: less-sophisticated) alternative to @Xaldew's nice answer, I created a keyboard binding that indents to column 8 (since that's generally where I need to be and can't get to with Google-style docstrings) with a minimum of 4 spaces inserted (for wrapped descriptions). I used C-<tab> since it was unused in my Python mode, but maybe choose something that works for you.

I use elpy, so I put it in the use-package block below. The relevant portion is:

(use-package elpy
  ;; omitted
  :bind
  (("C-<tab>" . (lambda () (interactive) (indent-to-column 8 4))))
  ;; omitted
  )
goodmami
  • 101
  • 2