-
Notifications
You must be signed in to change notification settings - Fork 0
Code formatting recommendations
This file contains recommendations on how code contributed to GAP itself (kernel and library) should be formatted.
Formatting code in a clean, readable way is an important for code hygiene, and as such a well-established norm for any larger software project, such as GAP. To whit, most of these projects recommend or even rigorously enforce code formatting guidelines. A few examples:
- LLVM coding standards
- Google’s C++ style guide
- Chromium’s style guide
- Mozilla’s style guide
- WebKit’s style guide
While we do not wish to force any specific formatting rules on anybody, we nevertheless reserve the liberty of rejecting code contributions for GAP which are formatted in a fashion that makes understanding the code difficult. We may then point potential contributors to this guideline to help them get an idea of what we consider good formatting.
That said, in the end, common sense has precedence -- this is not about counting individual spaces, but about readability and internal consistency of code.
Beyond that, for those of us who wish to write code in a globally consistent way, this guide helps us to coordinate by making some specific suggestions as to how to format specific kinds of code. These are not binding, but we encourage everybody to consider following them, even package authors.
- Do not reformat big bunches of existing code in the GAP codebase right now!
- We may eventually do this, but for now, only new code contribution should be (re)formatted according to these rules. Any such reformatting complicates merges between
master
andhpcgap-default
, and makes the output ofgit annotate
less useful. * Never make mix reformatting and logic changes - If you fix a bug or add a feature, and at the same time (in the same commit) reformat the code, then this makes it hard to spot what you actually did by looking at the diff. Thus, it makes code review very hard if not impossible.
- We may eventually do this, but for now, only new code contribution should be (re)formatted according to these rules. Any such reformatting complicates merges between
- When editing an existing function, always try to match the surrounding code.
- So if the function uses 2-space indents, do that, too.
- Rationale: Code can be very confusing if in the middle of it the formatting changes - some of the worst examples mix tabs and spaces, leading to a result that is hard to read with any tab setting. Please avoid that.
- For GAP code, you can use
Display(func);
to let GAP format your function, to get a basic idea how it should look.- Unfortunately, this strips comments, so for now this is of limited use. We hope to be able to provide a proper code reformatter for GAP code in the future
- For C code, clang-format is a great formatting tool
- We will provide a settings file for it which respects the recommendations described below, and which you can use to format a function or a whole file
- But remember, for now this should be limited to new code only.
- update the GAP code printing code in GAP as needed to match this
- e.g. get rid of the extra space after
local
and beforethen
anddo
? See issue https://github.com/gap-system/gap/issues/473
- e.g. get rid of the extra space after
The following are for both kernel (C) and library (GAP) code:
- never use tabs to indent code! Use simple whitespace instead.
- different people use different tab settings; 4 and 8 are common, but also 2, 3 and others are not unheard of. As a result, people have to adjust their tab settings to read your code. And if the next function was written by somebody with another tab setting, one has to adjust the tab settings again as one switches between the two function
- NEVER EVER mix tabs and spaces.
- This results in the worst of all situations.
- Unfortunately, at least on popular editor (emacs) uses this as a default setting. Please turn it of.
- we recommend 4 spaces per indention level
- a frequently seen alternative in existing code is to use 2 spaces, which is also fine.
- but please do not use anything other than 2 or 4
- maximal line length: 78 (with exceptions possible in justified cases; use common sense)
- historically, this is to accommodate people editing code on terminals which are limited to 80 characters (minus 2 to accommodate for e.g. scrollbars)
- while terminal may not be relevant anymore, overlong lines still make reading harder (this is why newspapers use columns: wide text is difficult to parse correctly for humans)
- that said, if your code is indented deeply, it may certainly be OK to break this rule, but please think about it first
- avoid trailing spaces on lines
- end your files with a single newline
-
Use common sense above all :-)
-
Use hugging braces (preserves vertical screen space). But place
else
on new line, to make it easy to rearrange, remove or addelse
/else if
blocks.
// good
int f() {
if (cond) {
statement;
}
else if (otherCond) {
statement;
}
else {
statement;
}
}
// bad: opening braces on new line; braces indented differently than code before and after.
int f()
{
if (cond)
{
statement;
}
else
{
statement;
}
}
-
separate reserved keywords from opening parentheses by a single whitespace
- good:
while (1) {
- bad:
while(1) {
- good:
-
separate parentheses and curly braces by a single whitespace
- good:
if (cond) {
- bad
if (cond){
- good:
-
follow commas by a single whitespace
- good:
int x, y, z
orf(a, b, c)
- bad:
int x,y,z
orf(a, b,c)
- good:
-
follow semicolons by a single whitespace, if there is more after it on the line
for (int a = 0; b < c; d++)
doSomething(e); doSomething(f); // This is probably bad style anyway
-
no inner bracket padding
- good:
f(a + b)
,if (cond)
- bad
f( a + b )
,if ( cond )
- good:
-
surround binary operators by spaces
- good:
x = 1 + (a * b);
- bad:
x=1+(a*b);
- good:
-
no space between unary operators and their arguments
- good:
*x = -z + &y + 1
- bad:
* x = - z + & y + 1
- good:
-
pointer declarations:
*
should hug the variable name and be separate from the type name- good:
foo(int *x)
orint *x, y;
- bad:
foo(int* x)
orint* x, y;
- good:
-
do not put whitespace after a cast
- good:
const char *ptr = (const char *)foobar;
- bad:
const char *ptr = (const char *) foobar;
- good:
-
Do not fill up comments with pointless spaces
- good:
/* special case for the empty blist */
- bad:
/* special case for the empty blist */
- good:
-
Both single-line
// ...
and multi-line comments/* ... */
are allowed. For comments that really are just on a single line, using// ...
is slightly preferred.
- switch / case: (TODO: finalize this: indent
case
or not???)
switch (cond) {
case 1:
statement;
break;
default:
statement;
break;
}
switch (cond) {
case 1: statement; break;
default: statement; break;
}
- for loops
for (int i = 0; i < 10; ++i) {
statement;
}
- for and while loops with empty loop body: use braces
// good
while (cond) { }
// bad
while (cond);
Usage of certain C99 extensions is allowed (please ask if in doubt).
- declare variables anywhere you like, even in for loop condition
int x;
statement;
int y;
for (int i = 0; i < 10; ++i) {
statement;
}
-
use of single line
//
comments is allowed -
Things defined in
inttypes.h
respectively `stdint.h may be used -
... ?
This should essentially match the way GAP prints functions.
This means in general it is similar to the way our C code is formatted, with some notable exceptions:
- inner bracket padding is allowed, especially if it improves readability
- ...
TODO: make sure GAP prints functions the way we want ;-), see also https://github.com/gap-system/gap/issues/473
Calls to InstallMethod
should be formatted as follows: Both the name of the operation, and the info string, should be put on the first line, to help people who want to grep for method installations. Other parameters can be spilled on subsequent lines, and they are indented by one level.
The exception is the final parameter with the function to be installed. If it is a full-blown function(...) ... end
construct, it should start on indention level 0. If it is a lambda or a function name, treat it like as other parameters.
# default: a full blown function, which starts at indention level 0
InstallMethod( SomeOperation, "for blue objects",
[ IsObject ],
1,
function( obj )
if not IsGroup( obj ) then
return 0;
else
return 42
fi;
end );
# if the function is a lambda, or the name of an existing function,
# then put it at indention level 1:
InstallMethod( SomeOperation, "for blue objects",
[ IsObject ],
1,
obj -> 2 * obj );
# You do not have to spill the extra parameters to new lines
InstallMethod( SomeOperation, "for blue objects",
[ IsObject ], 1, SomeOtherFunction );
The GAP manual describes the conventions for GAP names, which, if used consistently, make it very easy to guess the (order of) parameters of operations. Please try to follow them. Read up on them in Chapter 5 of the GAP reference manual.
The following is .clang-format
file I once created to reformat the GAP C code. I have not verified whether it matches the above 100%, but it should be at least reasonably close and thus provide a good starting point.
# For information on the settings in this file, please consult
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
# Basic indention settings
UseTab: Never
IndentWidth: 4
ColumnLimit: 78
MaxEmptyLinesToKeep: 2
BreakBeforeBraces: Attach
Cpp11BracedListStyle: false
AlignTrailingComments: true
SpacesBeforeTrailingComments: 4
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
BinPackParameters: false
PointerBindsToType: false
Here is a simple .editorconfig
file (see http://EditorConfig.org)
# See http://EditorConfig.org
# This is the top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file.
# But do not trim trailing whitespaces -- while we
# want that eventually, it just causes pain right now,
# if it pollutes commits.
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = false
indent_style = space
indent_size = 4
charset = utf-8
# In Makefile one must use tab indentation
[Makefile*]
indent_style = tab
Provide settings for emacs, vim and other popular editors which make it convenient for people to use our style.