Search code examples
pycharmeditorsublimetext3docstring

automatically reformat docstrings to respect right margin


Often I edit a docstring and find my edit pushes a line's width past the desired right margin. As a result, many lines of text below this edit maybe need be reformatted before my docstring is once again acceptable.
What's a simple and safe way to automatically fix this?

For example:

class WellDocumentedClass:
    """The first line of this is <72 characters wide. And is above lots|
    more text. Lorem ipsum dolor sit amet, consectetur adipiscing elit.|
    et mauris ac eros placerat auctor. Mauris mollis est scelerisse    |
    accumsan dapibus. Ut imperdiet suscipit lacinia. Maecenas volutpat |
    iaculis malesuada. Sed venenatis ipsum gravida molestolaoreet. Fuse|
    facilisis neque nec mauris maximus rutrum. Suspendisse at vestibulo|
    orci, ut feugiat odio. Aliquam erat volutpat. Nulla accumsan justo |
    ligula, at imperdiet quam ultrices non. Cras vitae vehicula ligula.|
    Quisque quam massa, dignissim in volutpat in, mattis eu urna.      |
    """
    pass

Oh no! I accidentally omitted the word "docstring" from the first line. It was so perfectly formatted!

class WellDocumentedClass:
    """The first line of this docstring is <72 characters wide. And is |above lots
    more text. Lorem ipsum dolor sit amet, consectetur adipiscing elit.|
    et mauris ac eros placerat auctor. Mauris mollis est scelerisse    |
    accumsan dapibus. Ut imperdiet suscipit lacinia. Maecenas volutpat |
    iaculis malesuada. Sed venenatis ipsum gravida molestolaoreet. Fuse|
    facilisis neque nec mauris maximus rutrum. Suspendisse at vestibulo|
    orci, ut feugiat odio. Aliquam erat volutpat. Nulla accumsan justo |
    ligula, at imperdiet quam ultrices non. Cras vitae vehicula ligula.|
    Quisque quam massa, dignissim in volutpat in, mattis eu urna.      |
    """
    pass

Argh. Time to use my mouse and press enter a lot... unless... what do you do in moments like this?


Solution

  • You can achieve this in Sublime Text 3 by creating a (relatively) simple plugin:

    • Tools menu -> Developer -> New Plugin...
    • Select all and replace with the following
    import sublime
    import sublime_plugin
    import textwrap
    
    
    class WrapTextCommand(sublime_plugin.TextCommand):
        def run(self, edit, width=0):
            # use the narrowest ruler from the view if no width specified, or default to 72 if no rulers are enabled
            width = width or next(iter(self.view.settings().get('rulers', [])), 72)
            new_sel = list()
            # loop through the selections in reverse order so that the selection positions don't move when the selected text changes size
            for sel in reversed(self.view.sel()):
                # make sure the leading indentation is selected, for `dedent` to work properly
                sel = sel.cover(self.view.line(sel.begin()))
                # determine how much indentation is at the first selected line
                indentation_amount = self.view.indentation_level(sel.begin()) * self.view.settings().get('tab_size', 4)
                # create a wrapper that will keep that indentation
                wrapper = textwrap.TextWrapper(drop_whitespace=True, width=width, initial_indent=' ' * indentation_amount, subsequent_indent=' ' * indentation_amount)
                # unindent the selected text before then reformatting the text to fill the available (column) space
                text = wrapper.fill(textwrap.dedent(self.view.substr(sel)))
                # replace the selected text with the rewrapped text
                self.view.replace(edit, sel, text)
                # resize the selection to match the new wrapped text size
                new_sel.append(sublime.Region(sel.begin(), sel.begin() + len(text)))
            self.view.sel().clear()
            self.view.sel().add_all(new_sel)
    
    • save it, in the folder ST recommends (Packages/User/) as something like wraptext.py
    • create a keybinding to invoke the new wrap functionality:
    { "keys": ["alt+."], "command": "wrap_text" },
    
    • enjoy

    wrap docstring