]> git.madduck.net Git - etc/vim.git/blob - docs/technical_philosophy.md

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Implement `# fmt: off` and `# fmt: on`
[etc/vim.git] / docs / technical_philosophy.md
1 # Technical Overview
2
3 ## The philosophy behind *Black*
4
5 *Black* reformats entire files in place.  It is not configurable.  It
6 doesn't take previous formatting into account.  It doesn't reformat
7 blocks that start with `# fmt: off` and end with `# fmt: on`.  It also
8 recognizes [YAPF](https://github.com/google/yapf)'s block comments to
9 the same effect, as a courtesy for straddling code.
10
11 ## How *Black* formats files
12
13 *Black* ignores previous formatting and applies uniform horizontal
14 and vertical whitespace to your code.  The rules for horizontal
15 whitespace are pretty obvious and can be summarized as: do whatever
16 makes `pycodestyle` happy.
17
18 As for vertical whitespace, *Black* tries to render one full expression
19 or simple statement per line.  If this fits the allotted line length,
20 great.
21
22 ```py3
23 # in:
24
25 l = [1,
26      2,
27      3,
28 ]
29
30 # out:
31
32 l = [1, 2, 3]
33 ```
34
35 If not, *Black* will look at the contents of the first outer matching
36 brackets and put that in a separate indented line.
37
38 ```py3
39 # in:
40
41 l = [[n for n in list_bosses()], [n for n in list_employees()]]
42
43 # out:
44
45 l = [
46     [n for n in list_bosses()], [n for n in list_employees()]
47 ]
48 ```
49
50 If that still doesn't fit the bill, it will decompose the internal
51 expression further using the same rule, indenting matching brackets
52 every time.  If the contents of the matching brackets pair are
53 comma-separated (like an argument list, or a dict literal, and so on)
54 then *Black* will first try to keep them on the same line with the
55 matching brackets.  If that doesn't work, it will put all of them in
56 separate lines.
57
58 ```py3
59 # in:
60
61 def very_important_function(template: str, *variables, file: os.PathLike, debug: bool = False):
62     """Applies `variables` to the `template` and writes to `file`."""
63     with open(file, 'w') as f:
64         ...
65
66 # out:
67
68 def very_important_function(
69     template: str,
70     *variables,
71     file: os.PathLike,
72     debug: bool = False,
73 ):
74     """Applies `variables` to the `template` and writes to `file`."""
75     with open(file, 'w') as f:
76         ...
77 ```
78
79 You might have noticed that closing brackets are always dedented and
80 that a trailing comma is always added.  Such formatting produces smaller
81 diffs; when you add or remove an element, it's always just one line.
82 Also, having the closing bracket dedented provides a clear delimiter
83 between two distinct sections of the code that otherwise share the same
84 indentation level (like the arguments list and the docstring in the
85 example above).
86
87 Unnecessary trailing commas are removed if an expression fits in one
88 line.  This makes it 1% more likely that your line won't exceed the
89 allotted line length limit.
90
91 *Black* avoids spurious vertical whitespace.  This is in the spirit of
92 PEP 8 which says that in-function vertical whitespace should only be
93 used sparingly.  One exception is control flow statements: *Black* will
94 always emit an extra empty line after ``return``, ``raise``, ``break``,
95 ``continue``, and ``yield``.  This is to make changes in control flow
96 more prominent to readers of your code.
97
98 That's it.  The rest of the whitespace formatting rules follow PEP 8 and
99 are designed to keep `pycodestyle` quiet.
100
101 ## Line length
102
103 You probably noticed the peculiar default line length.  *Black* defaults
104 to 88 characters per line, which happens to be 10% over 80.  This number
105 was found to produce significantly shorter files than sticking with 80
106 (the most popular), or even 79 (used by the standard library).  In
107 general, [90-ish seems like the wise choice](https://youtu.be/wf-BqAjZb8M?t=260).
108
109 If you're paid by the line of code you write, you can pass
110 `--line-length` with a lower number.  *Black* will try to respect that.
111 However, sometimes it won't be able to without breaking other rules.  In
112 those rare cases, auto-formatted code will exceed your allotted limit.
113
114 You can also increase it, but remember that people with sight disabilities
115 find it harder to work with line lengths exceeding 100 characters.
116 It also adversely affects side-by-side diff review  on typical screen
117 resolutions.  Long lines also make it harder to present code neatly
118 in documentation or talk slides.
119
120 If you're using Flake8, you can bump `max-line-length` to 88 and forget
121 about it.  Alternatively, use [Bugbear](https://github.com/PyCQA/flake8-bugbear)'s
122 B950 warning instead of E501 and keep the max line length at 80 which
123 you are probably already using.  You'd do it like this:
124
125 ```ini
126 [flake8]
127 max-line-length = 80
128 ...
129 select = C,E,F,W,B,B950
130 ignore = E501
131 ```
132
133 You'll find *Black*'s own .flake8 config file is configured like this.
134 If you're curious about the reasoning behind B950, Bugbear's documentation
135 explains it.  The tl;dr is "it's like highway speed limits, we won't
136 bother you if you overdo it by a few km/h".
137
138 ## Empty lines
139
140 *Black* will allow single empty lines left by the original editors,
141 except when they're added within parenthesized expressions.  Since such
142 expressions are always reformatted to fit minimal space, this whitespace
143 is lost.
144
145 It will also insert proper spacing before and after function definitions.
146 It's one line before and after inner functions and two lines before and
147 after module-level functions.  *Black* will put those empty lines also
148 between the function definition and any standalone comments that
149 immediately precede the given function.  If you want to comment on the
150 entire function, use a docstring or put a leading comment in the function
151 body.
152
153 ## Editor integration
154
155 * Visual Studio Code: [joslarson.black-vscode](https://marketplace.visualstudio.com/items?itemName=joslarson.black-vscode)
156
157 Any tool that can pipe code through *Black* using its stdio mode (just
158 [use `-` as the file name](http://www.tldp.org/LDP/abs/html/special-chars.html#DASHREF2)).
159 The formatted code will be returned on stdout (unless `--check` was
160 passed).  *Black* will still emit messages on stderr but that shouldn't
161 affect your use case.
162
163 There is currently no integration with any other text editors. Vim and
164 Atom/Nuclide integration is planned by the author, others will require
165 external contributions.
166
167 Patches welcome! ✨ 🍰 ✨