Browse Source

Add style guide (draft)

Alois Mahdal 9 years ago
parent
commit
6d244fb69c
1 changed files with 410 additions and 0 deletions
  1. 410
    0
      notes/style.md

+ 410
- 0
notes/style.md View File

@@ -0,0 +1,410 @@
1
+Coding style - draft
2
+============
3
+
4
+This is the official coding style for FastFoo project.
5
+
6
+
7
+Basic principles
8
+----------------
9
+
10
+Even though different language, the style is inspired by Python community, more specifically by PEP20 and PEP8.
11
+
12
+Especially the main principles (PEP20) are honored throughout the Fastfoo project, so it may be easier for you to understand recommendations here if you already know
13
+PEP20.  So **go read it now**---as the name hints, it's **only 20 lines**.  You can read it offline:
14
+
15
+    echo "import this" | python
16
+
17
+PEP8 is official coding style, being a Python commumity's way of achieving some of PEP20's principles.  So although many things won't make sense for Bash,
18
+I recommend to read it or run through it at the very least.
19
+
20
+
21
+Code layout
22
+-----------
23
+
24
+### Indentation ###
25
+
26
+Indentation is done exclusively by *space*.
27
+
28
+Indentation level is *almost exclusively* 4 spaces.
29
+
30
+    for i in $(seq 1 5);
31
+    do
32
+        something_with $i
33
+    done
34
+
35
+One exception is *vertical pipelines*:
36
+
37
+    cat list \
38
+      | while read line;
39
+        do
40
+            something_with $line
41
+            ...
42
+        done \
43
+      | sort \
44
+      | uniq
45
+
46
+where each pipe is indented by 2 spaces and followed by 1 space. This
47
+makes the pipe *float* in the indentation space, preserving visual
48
+aspect of indentation inside any loops etc (which also may help your
49
+editor auto-indent correctly--when inside the loop).
50
+
51
+
52
+### Line length ###
53
+
54
+79 characters.
55
+
56
+For docstrings, it's 72 characters *including* mandatory *she-space*
57
+prefix (`# `).  Add 4 in case of function docstrings.
58
+
59
+
60
+<!-- ### Blank lines ###
61
+
62
+TODO: decide -->
63
+
64
+
65
+### Imports ###
66
+
67
+Imports come *after* module docstring, *before* variable declarations.
68
+
69
+    #!/bin/bash
70
+    # license text
71
+
72
+    #
73
+    # A modest module
74
+    #
75
+
76
+    ffoo import cfgrep
77
+    ffoo import exits
78
+    ffoo import pretty
79
+
80
+    #
81
+    # Some var
82
+    #
83
+    SOME_VAR=159
84
+
85
+
86
+Imports should be grouped in this order:
87
+
88
+ 1. standard library modules,
89
+ 2. related third party modules,
90
+
91
+with empty line between the groups, and each group being sorted
92
+alphabetically.
93
+
94
+
95
+
96
+Comments
97
+--------
98
+
99
+First, don't overuse comments. Don't use comments to state the same what
100
+the code states.  The question the comment is answering must be **why**,
101
+not **what**.
102
+
103
+If you need too many comments, it may mean that something else is wrong
104
+with your code--more comments won't fix it.
105
+
106
+
107
+### Comment formatting ###
108
+
109
+Any comment must start with at least single space.
110
+
111
+    # hello
112
+
113
+Exceptions from this rule are TODO/FIXME comments--see below.
114
+
115
+
116
+### Block comments ###
117
+
118
+
119
+### Inline comments ###
120
+
121
+Inline comment should be aligned to 4 spaces, and must be separated from the code by at least
122
+2 spaces.  Don't do this:
123
+
124
+    foo # hello
125
+    bar1 # world
126
+
127
+but rather this:
128
+
129
+    foo   # hello
130
+    bar1  # world
131
+
132
+or this (easier when another line is added):
133
+
134
+    foo     # hello
135
+    bar1    # world
136
+
137
+
138
+### TODOs, FIXMEs, workarounds ###
139
+
140
+These must be one-liners, so that they can be easily understood by grepping.
141
+
142
+    #TODO: Review the loop
143
+    #FIXME: We throw away stdout due to bug in Glib
144
+    #       http://bugs.gnome.org/bug/12345
145
+
146
+Exception is URL, that can span to next line.
147
+
148
+Note that for both TODOs and FIXMEs, it's always better to have a tracker
149
+that is referenced in place--that's especially true if you can't fit the
150
+description into one line.
151
+
152
+
153
+Docstrings
154
+----------
155
+
156
+Docstrings are special kind of block comments that usually span many lines
157
+and are primary means of providing documentation for functions, modules
158
+and variables.
159
+
160
+Generic form of docstring is
161
+
162
+    #
163
+    # One-line summary
164
+    #
165
+    # More detailed explanation
166
+    # written in Markdown.
167
+    #
168
+    # Can have multiple paragraphs etc, as long
169
+    # as there is an empty comment at the end
170
+    #
171
+
172
+In other words, one empty comment, one-line comment for summary,
173
+one empty comment, and the rest, followed by one more empty comment.
174
+This means that the line preceding the whole block as well as the one
175
+following *must not* be comments.
176
+
177
+Assigning docstring to the related item is done by specific juxtaposition
178
+to this item.
179
+
180
+
181
+#### Module docstring ####
182
+
183
+Module docstring is placed directly after the first empty line in the module
184
+file.  Typically this will be after shebang, additional lines like Vim editor
185
+directives and possibly author and license information.
186
+
187
+For example:
188
+
189
+    #!/bin/bash
190
+    #
191
+    # Some crazy walll of license text ... or not...
192
+    #
193
+    # Authors: Me <and@only.me>
194
+    #
195
+
196
+    #
197
+    # My cool module
198
+    #
199
+    # This module is so useful that I can't even start
200
+    # describing it!
201
+    #
202
+
203
+
204
+#### Variable docstring ####
205
+
206
+To add docstring to a global variable, prepend it directly to the variable
207
+declaration:
208
+
209
+    #
210
+    # My cool var
211
+    #
212
+    # This variable means something to me
213
+    # but I'm not going to tell you!
214
+    #
215
+    MY_COOL_VAR=$1
216
+
217
+i.e. an empty line, and empty comment, the docstring and immediately the variable
218
+and at least one empty line.
219
+
220
+It is possible to group related variables together by adding all assignments
221
+right after the docstring like this:
222
+
223
+    #
224
+    # I don't have numpad
225
+    #
226
+    # I prefer using words for numbers, so I will
227
+    # always use these variables instead.
228
+    #
229
+    WORD_ONE=1
230
+    WORD_TWO=2
231
+    WORD_THREE=3
232
+
233
+This is equivalent for copying the same docstring to each of the variables.
234
+
235
+
236
+#### Function docstring ####
237
+
238
+For functions, the docstring is part of the body, and follows the first declarative
239
+line (the one ending with `{`):
240
+
241
+    my_cool_fun() {
242
+        #
243
+        # I don't have numpad
244
+        #
245
+        # I prefer using words for numbers, so I will
246
+        # always use these variables instead.
247
+        #
248
+        local foo   # fooizer
249
+        local bar   # barrator
250
+        do_something_with $foo $bar
251
+    }
252
+
253
+This also means that unlike modules and variable docstrings function docstrings:
254
+
255
+ *  are indented with 4 additional spaces,
256
+ *  are not preceded by empty line (but the declaration instead),
257
+ *  and may, or may not be followed by empty line---here anything that is not `    #` (i.e.
258
+    normal code as well) counts as elimiter.
259
+
260
+
261
+Common language constructs
262
+--------------------------
263
+
264
+
265
+### functions ###
266
+
267
+
268
+Preferred way of declaration is a variation of most common 
269
+Bourne shell way with K&R-style brackets:
270
+
271
+    myfun() {
272
+        foo
273
+        bar
274
+        baz
275
+    }
276
+
277
+That is,
278
+
279
+ *  multi-line;
280
+
281
+ *  no `function` keyword;
282
+
283
+ *  single space between parentheses and opening curly bracket;
284
+
285
+ *  closing curly bracket is alone -- *no redirection here*.
286
+
287
+
288
+### `if`, `while`, `for` ###
289
+
290
+    if foo;
291
+    then
292
+        bar
293
+    elif baz;
294
+    then
295
+        quux
296
+    else
297
+        idk
298
+    fi
299
+
300
+    while foo;
301
+    do
302
+        bar
303
+        baz
304
+        quux
305
+    done
306
+
307
+    for foo in bar baz;
308
+    do
309
+        quux $foo
310
+    done
311
+
312
+That is,
313
+
314
+ *  `then`, `do` and `done` are always alone, aligned with opening
315
+    keyword (`if`, `while`, `for`) or intermediary keyword (`elif`, `else`);
316
+
317
+ *  command part (i.e. condition for `if`/`elif`/`while`, or expansion in
318
+    `for`) is terminated by semicolon;
319
+
320
+ *  commands in loop body are not terminated by semicolon.
321
+
322
+
323
+### `case` ###
324
+
325
+    case "$foo" in
326
+        bar)
327
+            baz
328
+            ;;
329
+        *)
330
+            quux
331
+            ;;
332
+    esac
333
+
334
+That is, three levels:
335
+
336
+ *  `case` and `esac` on level 1,
337
+
338
+ *  patterns on level 2,
339
+
340
+ *  commands and terminators on level 3.
341
+
342
+
343
+Let's get idio(ma)tic
344
+---------------------
345
+
346
+
347
+### condensed case table ###
348
+
349
+However, a "condensed" version is possible --- and often recommended:
350
+
351
+    case "$foo" in
352
+        bar)    bar_something_up    ;;
353
+        baz)    do_bazzy_thing      ;;
354
+        *)      quux; exit          ;;
355
+    esac
356
+    #   ^ 4     ^ 12                ^ 32
357
+
358
+That is,
359
+
360
+ *  a "table" layout, where pattern, commands and terminator form a "row";
361
+
362
+ *  each "column" is aligned 4 spaces after `case`;
363
+
364
+ *  commands aligned together, to next 4-space boundary after longest
365
+    pattern;
366
+
367
+ *  just as terminators, after longest command;
368
+
369
+ *  in case of multiple commands,  semicolon and space is used as
370
+    delimiter.
371
+
372
+
373
+### the new case - argument router ###
374
+
375
+This is a cross-breed of infinite *while* loop and *case* switch,
376
+a construct I call *argument router*.
377
+
378
+To understand how this is useful, I'll show you a more complete example:
379
+
380
+    verbose=false
381
+    file="-"
382
+    item=""
383
+
384
+    while true; do case "$1" in
385
+        -f|--file)      file="$2"; shift 2   ;;
386
+        -q|--quiet)     verbose=false; shift ;;
387
+        -v|--verbose)   verbose=true; shift  ;;
388
+        --help)         show_help; exit      ;;
389
+        --)             item="$2"; break     ;;
390
+        -*)             show_usage; exit 2   ;;
391
+        *)              item="$1"; break     ;;
392
+    esac done
393
+
394
+    test -n "item" || show_usage
395
+
396
+As you can see it's a variant of *condensed case table*, wrapped in
397
+an infinite *while* loop.  It may break some rules, but for some great
398
+advantages:  Whole CLI is described in just few lines--you just need to
399
+read header to understand how variables may be set.
400
+
401
+As opposed to nesting *case* in *while* properly, 2 lines (which
402
+are always the same) are saved vertically, and 4 spaces per pattern
403
+are saved horizontally, which may become extremely useful for longer
404
+option/variable names.
405
+
406
+As opposed to `getopts`, you miss some validation mechanisms and option
407
+bundling, but OTOH you don't need to understand the getopts syntax--what
408
+you see here in Bash is what you get.  From my experience (writing rather
409
+simple scripts with simple interfaces), this is worth.
410
+