File: srcfmt.txt

package info (click to toggle)
lix 0.9.24-1
  • links: PTS
  • area: main
  • in suites: buster
  • size: 44,648 kB
  • sloc: sh: 182; makefile: 16
file content (263 lines) | stat: -rw-r--r-- 10,029 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
Lix Source Code Formatting And Style
====================================

How to use and hack the Lix source.



Building Lix
------------

Look in the `build' directory and read the file for your operating system.
That file has instructions on how to install a D compiler, the build system
dub, and the required libraries.



Sending in patches
------------------

I encourage using git to send in patches. Make a github pull request, or
send your commits by email. See the default readme for my contact data.

Please limit the first line of your git commit messages to 50 characters.
If you want to write a longer message, make a blank line below the first,
and then add more lines of at most 72 characters each.



Text encoding
-------------

Everything -- source, levels, gadget definitions, documentation -- is UTF-8.
Never use byte-order marks. Rationale: http://utf8everywhere.org/

Source code shall have LF terminators.

Level and replay files can have LF, CRLF, or CR endings.

Documentation that is system-independent (most files in ./doc/) and
translations (all ./data/transl/* files) shall have CRLF endings.
They shall be accessible in Windows Notepad.



Non-null class references and Optional
--------------------------------------

D doesn't have non-null class references. Every reference may be null.
Because many references are non-null in normal program flow, wrapping
most references in wrappers would be painful. Thus, codebase-wide rules:

*   A field of class type should be non-null outside constructors.
*   A function argument of class type should be non-null, always.
*   A function return value of class type should be non-null, always.
*   Wherever null is an allowed value outside of a constructor, use
    Optional!T instead of T. This relies on the package "optional" in the dub
    registry.

Examples:

    class T { ... }
    T myFreeFoo(T a)
    {
        // You may assume that "a" is always non-null here.
        // You don't need to write "assert (a)" or "if (a !is null)" here,
        // only do that if you expect legacy code to (wrongly) pass nulls.
        // You must return a non-null T.
    }

    class T { ... }
    class U {
        T a;
        this()
        {
            // During the constructor, "a" may be null, but you must
            // have assigned to "a" something non-null by the end.
            a = new T();
        }
        void foo()
        {
            // You may assume that "a" is always non-null here!
        }
    }

    class T { ... }
    import optional;
    Optional!T myFreeFoo(Optional!T a)
    {
        // This can take any non-null T or no!T (see optional's documentation).
        // I discourage to wrap "T null" in "Optional!T".
    }

To assign to T from an Optional!T, unwrap "Optional!T" with "unwrap"
or call "or" or "dispatch" on it, see the documentation of package "optional".

Nullable references are my biggest gripe with D. Maybe something will happen
in a few years! Until then, I'd like to write my code by clearly separating
between nullable and non-nullable. No rule can be perfect today. I hope
mine is not too confusing.

An alternative would be to name all nullable T fields "maybeX" instead of "x",
but, unlike "Optional!T", this would make it syntactially legal to assign
"x = maybeX;" and I prefer an error here.



Source code style
-----------------

Source code style is, in order of influence from lots to little, a mixture
of The D Style, personal taste, Linux kernel style, and vibe.d style.

Indentation is 4 spaces. Rationale: The D Style (http://dlang.org/dstyle.html)
says that each indentation will be 4 spaces, and I have said for 10 years that
each indentation will be 4 spaces. Therefore, indentation will be 4 spaces.

Tab characters are bugs.

Linebreak after 79 characters. Even if only a parenthesis or brace would go
after column 79, linebreak. Reasoning: Every line must display entirely
in git diff, in an 80x24 shell. git diff displays 1 char of its own, then 79
chars of the source.

Exception to linebreaking at 80 characters: You have many long lines,
and they all do very similar things.

Don't nest stuff deeper than 4 or 5 levels, treating classes and functions
as a nesting level. Write more functions instead. D allows you to declare
private functions at module level, or to declare local functions.

Order of fields and members inside a class is similar to the vibe.d ordering:

    1. private fields -- rationale: these come first, so you can't miss any
    2. public fields
    3. enums and constants
    4. constructors and static methods that act like constructors
    5. public methods
    6. protected methods
    7. private methods

Naming: Private fields start with an underscore _, then continue in camelCase.
Variables and functions are named in camelCase, without underscores anywhere.
Classes and structs are named in PascalCase.

Opening braces '{' go on the same line as the declaration for everything
that is not a function declaration:

    if (condition) {
        something();
        somethingElse();
    }

    class A {
    private:
        int _field;

    public:
        enum myConstant = 5;
    }

Empty line between classes and multiple-line functions. No empty line after
access modifiers, but one before access modifiers. Try to avoid 2 or more
blank lines in succession.

Function declarations get the opening brace on a standalone line:

    nothrow void myFunction() const @nogc
    {
        int localFunction(int a)
        {
            // ...
        }
    }

When you can fit an entire function definition into a single line, do that
instead of following the above rule. This happens often with class properties.

    @property int someValue()      { return _someValue;     }
    @property int someValue(int i) { return _someValue = i; }

The D style (http://dlang.org/dstyle.html) recommends properties over
get/set_some_value(). The private variable should be prefixed with an
underscore, because it needs a name different from the property functions.

Digression: The D style also recommends to choose type names LikeThis,
which I do myself, and other identifier names likeThis. I'm in the middle
of a conversion of the D codebase to this convention. My old convention was
to name non-type symbols like_this, as you would do in C. Guideline:
Use one underscore at the beginning of a private field, and no underscores
anywhere else, even if some of my old symbols still violate this guideline.

When a line, conditional expression or parameter list gets very long and
has to be broken into separate lines, you can do this:

    int myFunction(
        LongClassName longArgument1,
        AnotherClassName longArgument2
    ) {
        doSomething();
    }

    if (myLongExpression == someValue
        && anotherLongExpression > someOtherValue
    ) {
        doSomething();
        doSomethingElse();
    }

Reason: Anything else requires a third, made-up level of identation to
differentiate between the expression/parameter list and the body. ") {" is
shorter than the indentation width of 4 spaces, and therefore is a good
visual separator. You can easily reorder lines when each function argument
sits on its own line.

This rule takes precedence over "put the opening brace on the same line".
There is no "the" line anyway.

Don't align things horizontally. If you did that, you would introduce
unnecessary dependencies between lines. You want low coupling between modules;
likewise, you shouldn't couple lines.



Notes about D
-------------

*   To search the web, use "dlang" in your query, e.g., "dlang writefln".
    Alternatively, use "d programming language". Google wants to be smart,
    and brings results like "he'd" or "that'd" when you search for "dlang",
    because it has learned that a single "d" is wanted. The computer wants to
    be smarter than the user again. :-/

*   const int* foo() { ... } is the same as int* foo() const { ... }, both
    are valid D and do what the latter would have done in C++: make the method
    callable on a const class object. If you want to return a const pointer:
    const(int*) foo() { ... }; a pointer to const int: const(int)* foo().
    Mnemonic: When describing a const/immutable type, always use brackets.

*   To do formatted output to stdout, use writefln() in std.stdio, which works
    like printf(). The equivalent to C's sprintf() is format() in std.string,
    which returns a D string.

*   You can do RAII by allocating resources in the constructor this(), and
    deallocate in ~this(). To invoke the deallocation at the correct time,
    use a struct, not a class -- classes are garbage-collected, structs are
    destroyed deterministically at end of scope. Remember that a dynamic
    array of structs is garbage-collected.

*   If you really want to have deterministic destruction for classes,
    call destroy(myclassobject), which calls the destructor. Some large and
    important types are imagined best as classes, not structs, and still need
    deterministic cleanup -- rely on your good taste. destroy(myobject) was
    clear(myobject) in older versions of D. Destroying a class object doesn't
    prevent the GC from running the destructor once again later, when the
    object's fields are all set to their .init values! If you deallocate
    resources in your destructor, wrap them in a check whether the
    resource pointer is not null.

*   std.algorithm is beautiful. You're encouraged to build long pipes
    à la foo.map!(a => a.something).filter!(b => b < 10).reduce!max.
    You might find that such pipes generate badly-readable template error
    messages. If you get errors, rewrite the pipe with foreach loops,
    get everything right, and rewrite back to functional style.