-
Notifications
You must be signed in to change notification settings - Fork 199
Coding Style
The exact coding style for Naev has been asked before. First off let's mention the two golden rules of Naev coding development:
- We want to welcome rather than scare off contributors. Therefore we are not really strict. However if you do a good job following our coding style you shall be praised for it.
- It is FUNDAMENTAL that everything be clearly documented. We want new and veteran developers to be able to rely on generated API docs. 1
- We want readable and well documented code. Even if you've done an awesome optimization, if we can't understand it, chances are it'll get rewritten or modified in a way that creates bugs. So please focus on readability first and then optimizations second.
To help with this it's generally a good idea to use Doxygen. For example we can do:
/**
* @brief Checks if a foo_t is bar.
*
* Longer explanation of what exactly this does and how it does it here.
*
* @note foo must not be NULL.
*
* @param foo foo_t to check if is bar.
* @return 1 if foo is bar, 0 otherwise.
*/
int foo_isBar( foo_t foo )
{
return (foo == bar);
}
This is a clear way to document. However, Doxygen does not exclude you from documenting the code also. Especially if it's a complicated part.
Many functions in the nlua_*.c
source files (and ai.c
) are exported to Lua. Their doc comments look different, because they're auto-translated for use by LDoc. Directives like @luatparam
, @luareturn
, and @luafunc
correspond to LDoc's @tparam@
, @return
, @func
.
We use 3 spaces instead of tabs. This can be achieved by putting the following in your ~/.vimrc
:
set tabstop=3 " indents
set softtabstop=3 " treat 3 spaces as a single character (when deleting)
set shiftwidth=3 " more indents
set expandtab " use spaces instead of tabs
Generally we declare functions as:
type function( type1* param1, type param2 );
However if there are no parameters we do it as:
type function (void);
With brackets we do:
type function (void)
{
some_code();
}
Variable declaration statements themselves follow the same style as function parameters, except for pointers. Instead of type1* param1
, we place the *
symbol next to the symbol, to make compound declarations clearer: type *pointer, not_pointer;
Naev's tradition is to declare variables at the top of the function right after the opening bracket. This is fine, but for newer code the preference is to declare variables at the start of a scope:
void function (void)
{
for (int i=0; i<N; i++) {
int cats_seen = 0;
some_code();
}
}
We use the following formatting:
if (something) {
do_something();
}
However if it's more readable and there's only one line we can avoid brackets:
if (something)
do_something();
If there are multiple statements it is recommended to use explicit parenthesis:
if ((some_value == 0) && (some_pointer != NULL))
do_something();
It is recommended to compare against NULL instead of just checking the pointer. For example:
if (some_pointer != NULL)
instead of
if (some_pointer)
As it's more explicit on the type.
We prefer to check against conditions instead of for conditions to avoid indentation. For example:
for (i=0;i<N;i++) {
if (!foo_isBar(&foo[i]))
continue;
DEBUG( "foo is bar" );
}
This helps make code more readable.
Many data structures include variable-sized arrays. Naev has a standard tool for this declared and documented in array.h
. Any time you have two variables representing an array and its size, consider using this tool.
translatable string formatting, where to put mission text (vs. what's traditional), how and when to use vn
.
Missions and events are powered by Lua scripts containing an XML comment and a standard set of function definitions.
For an example, see docs/missions/mission_template.lua
.
Naev developers have learned some lessons along the way; not all scripts are going to be perfect role models.
Best practices include:
- Always use gettext for user-visible text. Naev provides 3 flavors to all Lua scripts
_("text")
translates a string.N_("text")
is a no-op (does not translate), but marks its input as a translatable string.n_("%d cat", "%d cats", n)
translates an expression which may be plural. - Keep the global variables clear and simple. Naev will preserve active missions' variables in saved games (except not tables, except yes tables with the
__save
field set). If you refactor the way your globals work, users` active missions might break. - Prefer to put the text in-line. (There's a widespread pattern of putting all the strings at the top of the file, sometimes in tables with names like
misn_desc
. This was done with good intentions, but it tends to backfire.) - Prefer to use
fmt.f()
instead ofstring.format
. For more info: fmt.f, Issue #1905. - Use scientific notation: "250e6" is a less error-prone way to write 250 million than "250000000". (Proper scientific notation would be "2.5e8", but in this author's opinion "250e6" is clearer.)
Some Lua scripts, mainly those in dat/scripts
, are meant to be reused.
The best practice (not always followed by older code) is for libraries to define a single table of functions and return
it.
Usage looks like local nice_lib = require "common.nice_lib"
(in case of a library located at dat/scripts/common/nice_lib.lua
).
This form avoids namespace pollution: this statement doesn't define any variables except nice_lib
.
Ideally, all library scripts should be documented. This is handled by LDoc, which is an improved LuaDoc but still a highly temperamental piece of software. To document dat/scripts/common/nice_lib.lua
you would: add it to dat/scripts/meson.build
, add a module description and @module
directive to a block comment on top of the file, and add doc comments in front of functions.
LDoc is not Doxygen. There is no @brief
directive. Parameters can be typed and/or optional. You can put an example function call after @usage
. Unexpected breakage is the norm; it's a good idea to browse docs/lua/index.html
in your build directory after a meson compile
.
Combined example:
--[[--
A nice library.
@module nice_lib
--]]
local nice_lib = {}
--[[--
Calculate the square of the input.
@usage y = square(x)
@tparam[opt=0] x The number to square.
@treturn number The input times itself.
--]]
function nice_lib.square( x )
return (x or 0)^2
end
return nice_lib
1: The meson build process generates HTML C and Lua docs in the docs
subdir, as long as doxygen
and ldoc
are available. Lua API docs are mirrored here.
Lore is being removed from the Wiki and moved into docs/manual/lore/.