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.
|