Skip to content

vilarion/Illarion-Coding-Style

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 

Repository files navigation

Illarion Coding Style

July 19, 2020

Author: Vilarion

Version 1.0

The team of Illarion developers has grown over time. And while everyone has their preferred coding style, it is useful to agree on a single one. This makes it easier for others to read your code, understand and modify it. These rules do not only cover the appearance of code, but also how the coding itself is performed, which functionality to use or avoid and best practices. The purpose of this document is not to hamper you in your coding efforts with an overly exhaustive set of rules, but to guide you towards clean code for the benefit of all.

There are many languages involved in Illarion development. They range from mark-up languages like HTML or the typesetting language LaTeX to a whole array of different programming languages like C++ , Java, Lua, PHP or GLSL. Examples given, either conforming or non-conforming, will be in C++ and Lua. You can easily derive how they would look like in the language you are currently using. Rules are designed to be as general as possible to apply to most languages used in Illarion development.

Examples are tagged with icons to annotate whether or not they are conforming. Non-conforming examples are designed such, that only the current issue is being violated. They are correct in every other aspect, so that you can focus on the issue at hand. This is what correct examples look like:

C++Lua
✔️
for (auto &item: items) {
    item.age();
}
for _, item in pairs(items) do
    item.age()
end

A violating example looks like this:

C++Lua
for (auto &item: items)
{
    item.age();
}
for _, item in pairs(items)
do
    item.age()
end
Formatting
  1. Write lines not longer than 120 characters
  2. Write one statement per line
  3. Frame code blocks with one line on each end
  4. Seperate adjacent code blocks with an empty line
  5. Indent four spaces inside code blocks
  6. Use space after a delimiter
  7. Use space before and after binary operators
  8. Avoid space after function names
  9. Avoid trailing white space
  10. Use lowerCamelCase for variable and function names
  11. Use UpperCamelCase for class names
  12. Use proper British English
  13. Use block comments for license headers
Coding
  1. Select expressive names
  2. Write only useful comments
  3. Avoid deprecated code
  4. Avoid duplicating code
  5. Use libraries
  6. Keep it as local as possible
  7. Write short, expressive functions
  8. Avoid unnecessary code and magic numbers
  9. Refrain from using goto
  10. Use correct license headers

Formatting

Write lines not longer than 120 characters

Long lines of code tend to get difficult to read. If your lines get longer than 120 characters, reconsider what you are writing. In fact, most statements should fit into a line of 80 characters. Maybe there is a better way to express what you want to do. If you still need longer lines, break them up, indenting them further than the next statement.

This rule does not apply to continuous text e.g. in HTML. Breaking up continuous text can lead to strange diffs where changing a single word could put a whole paragraph into the diff.

Write one statement per line

On the other hand, do not cram statements into one line until it reaches its length limit. One statement per line allows for good readability. Compare:

C++Lua
✔️
auto strength = 10;
auto wisdom = 8;
local strength = 10
local wisdom = 8
auto strength = 10; auto wisdom = 8;
local strength = 10 local wisdom = 8

Frame code blocks with one line on each end

Each local code block should be framed by one line on each end:

C++Lua
✔️
for (auto &item: items) {
    item.age();
}
for _, item in pairs(items) do
    item.age()
end
for (auto &item: items)
{
    item.age();
}
for _, item in pairs(items)
do
    item.age()
end

Seperate adjacent code blocks with an empty line

You can also use an empty line to structure logical groups.

C++Lua
✔️
auto minutes = 3;
auto seconds = minutes * 60;

for (auto &item: items) {
    for (auto i = 0; i < 5; ++i) {
        item.age(seconds);
    }
}
local minutes = 3
local seconds = minutes * 60

for _, item in pairs(items) do
    for i = 1, 5 do
        item.age(seconds)
    end
end
auto minutes = 3;
auto seconds = minutes * 60;
for (auto &item: items) {

    for (auto i = 0; i < 5; ++i) {
        item.age(seconds);
    }
}
local minutes = 3

local seconds = minutes * 60
for _, item in pairs(items) do
    for i = 1, 5 do
        item.age(seconds)
    end
end

Indent four spaces inside code blocks

Local code blocks have to be indented by four spaces. Do not use tabulators.

C++Lua
✔️
{
    auto result = database.query();
    result.validate();
}
do
    local npc = getNPC()
    npc:talk("Hello!")
end
{
        auto result = database.query();
        result.validate();
}
do
  local npc = getNPC()
  npc:talk("Hello!")
end

Use space after a delimiter

Each delimiting character has to be followed by exactly one space:

C++Lua
✔️
for (const auto number: {42, 0, 1}) {
    cout << number;
}
for number in {1, 2, 3} do
    print(number)
end
for (const auto number:{42, 0, 1}) {
    cout << number;
}
for number in {1,2,3} do
    print(number)
end

Use space before and after binary operators

All binary operators have to be pre- and succeeded by one space:

C++Lua
✔️
cout << 1;
auto i = 0;
i += 2;
auto b = 3 * i;
print("a" .. "b")
local i = 0
i = i + 2
local b = 3 * i
cout<<1;
auto i=0;
i +=2;
auto b =3*  i;
print("a".."b")
local i =0
i =i+ 2
local b= 3 * i

Avoid space after function names

This speaks for itself:

C++Lua
✔️
std::move(object);
print(1)
std::move (object);
print (1)

Avoid trailing white space

White space at the end of a line will produce strange diffs, so make sure you do not insert any.

Use lowerCamelCase for variable and function names

Lower camel case means that the first letter of the first word is written in lower case while consecutive words are directly appended but start with upper case:

C++Lua
✔️
int playerCounter;
Player destroyerOfWorlds;
void fooBar();
local playerCounter
local robertOppenheimer

function fooBar()
end
int playercounter;
Player destroyer_Of_Worlds;
void foo_bar();
local playercounter
local robert_Oppenheimer

function foo_bar()
end

Use UpperCamelCase for class names

Upper camel case means that a name consists of possibly multiple directly concatenated words, each starting with a capital letter:

C++Lua
✔️
class PlayerManager
struct Position;
class SelectionDialog;
PlayerCraft = {
    products = {},
    ...
}
class playerManager
struct position;
class selection_dialog;
player_craft = {
    products = {},
    ...
}

Use proper British English

It is important to use proper language as well as the proper language. Others will have to read what you write. Once we decided to use British English as official staff language, so this decision also applies to all written code. Small mistakes might and will happen, but that is no excuse to be lazy about it. Use a dictionary if you are not sure.

Use block comments for license headers

Using block comments for license headers at the beginning of each file allows editors to fold them, reducing the noise for you while you are working on actual code.

Coding

Select expressive names

Pick meaningful names. This is one of the most important aspects of good code. Do not use strange abbreviations. Variable names should describe their content, not an underlying data structure or implementation detail. Function names should describe an action for most functions and a question for boolean functions:

C++Lua
✔️
int playerCounter;
std::vector<Player> players;

for (auto i = 0; i < 10; ++i); // i is a common counter name

void savePlayers();
bool isYellow();
int getStrength();
local playerCounter

for i = 1, 10 do -- i is a common counter name
end

function useItem(user, item)
end
int plCtr;
std::vector<Player> playerVector;
void f();
local plyCt

function calculate() -- calculate what?
end

Write only useful comments

Express yourself in code instead of comments. Do not be redundant in comments. Do not use comments to rant or be otherwise unprofessional. One example for a good comment would be the description of a complex algorithm. You can cite the paper where you got it from and briefly express what the algorithm does. Further examples would be documenting a function's precondition or a class' invariant.

C++Lua
int a = 0; // counts players
int playerCounter = 0; // set to 0

while (true) {
} // end while

int b = ++a + 2 - 3; // this code is shit!!!

// characterAge: the age of the characters
// returns the number of characters
int numberOfCharacters(int characterAge);
local agility = 0 -- holds agility
local attribute = agility or 0 -- set attribute to agility or 0 if agility is nil

-- begin setting attributes
agility = 5
-- end setting attributes

Avoid deprecated code

Deprecated code will create work when we decide to use a newer version of some language. Do not use it, there is no excuse. Others will have to replace that code in time. If you come across deprecated code, get rid of it.

You should be aware of what is marked as deprecated in the language version you are currently using.

C++Lua
✔️
std::unique_ptr<Network>;
void save() noexcept;
~Player();
local M = {}
...
return M

length = #{4, 3, 2}
log = math.log(1234, 10)
std::auto_ptr<Network>;
void save() throw();
~Player() throw();
module("base.common", package.seeall)

length = table.maxn{4, 3, 2}
log = math.log10(1234)

Avoid duplicating code

Do not duplicate code. Copy&paste is bad. You or someone else will change that code in one place later and forget the other occurrences or not even know about them. Use functions if you need a piece of code more than once. Also use functions to clarify. If you can name it, you can put it into a function.

Use libraries

Prefer using language features or language libraries over reinventing the wheel. There is a reason those things exist. Your own code will just be inefficient, more verbose and may even contain bugs.

Keep it as local as possible

Keep the scope of variables as narrow as possible. The shorter their life span is, the less error-prone is your design.

Write short, expressive functions

Functions should be short and do one thing, specified by their name. If they are long and do things they are not supposed to do, it gets difficult to understand them or reason about them.

Avoid unnecessary code and magic numbers

Be precise in what you write. Write enough so that the code is clear and meaningful, but do not be verbose. Sometimes it is not enough to give a magic number a name, but you might need to change its type as well to better reflect its content.

C++Lua
✔️
auto taskInterval = std::chrono::seconds(60);
addUnsignedCharToBuffer(((data >> CHAR_BIT) & UCHAR_MAX));
return player;
activePlayers = 0
local strength = getAttribute(Character.strength)

if attribute == Character.dexterity then
    ...
end

do
    ...
end
int taskInterval = 60; // 60 what?
addUnsignedCharToBuffer(((data >> 8) & 255));
return (player);
a = 0; -- the ; is unnecessary in Lua, don't use it
i = (a * 3) + 2
local strength = getAttribute(5)

if (i == 4) then
    ...
end

if true
    ...
end

Refrain from using goto

A form of the goto control statement exists in most languages. Do not use it.

Use correct license headers

Every user-written source file which runs on the server needs to be prefixed with the AGPLv3 header. This is true for everything we write in C++, Lua or PHP. Everything which will eventually run on a client machine needs to be prefixed with the GPLv3 header.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published