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.