Code layout

Readable code starts with a consistent layout of the code. In this section the layout rules for code at Charta Software are described.

Indentation

Code is indented to allow the reader to clearly see the flow of control. The used indentation level is 2 spaces without using tab characters. In general the contents of a block is indented. A block can be delimited by different constructs:

  • The contents of flow control statement like if, while and try:
    1 2 3 4 if Condition then TrueStatement else FalseStatement;
  • The contents of a declaration section like var, const and type:
    1 2 3 var Variable1: TInteger32; Variable2: TInteger32;
  • The parameters of a long function call:
    1 2 3 4 5 FunctionA( Parameter1, Parameter2, Parameter3 );

Finally, the begin and end keyword are not indented together with the blocks they delimit:

1 2 3 4 5 while Condition do begin Statement1; Statement2; end;

New lines

One statement per line

Every statement uses at least one line of code. This means that no two statements may share a line. Also, the keywords delimiting structured constructs cannot share a line with the parts that make up the contents of those constructs. More precisely, the following constructs always are located on their own line of code:

  • The block statement keywords begin and end.
  • The declaration statement keywords like type, const and var.
  • Every declaration that occurs within a declaration statement.
  • The keyword uses
  • Every unit reference that occurs in a uses list.
  • The visibility specifiers private, protected and public

Statements spanning more than one line

The fact that every statement uses at least one line of code also means that some statements use more than one line. The flow control statements all use more than one line because the statements they contain are always located after the line with the introductory keyword (and condition).

When a construct uses more than one line empty lines are added before and after the construct to further enhance readability. These lines are always added except in the following cases:

  • When a construct is the first in the list of nested constructs the leading empty line is dropped.
  • When a construct is the last in the list of nested constructs the trailing empty line is dropped.

Function calls spanning more than one line

Function calls also can be distributed across multiple lines. Whenever a function call grows too large to be fit on a reasonably wide screen one can decide to split the call into multiple lines. When a function call is split, every parameter to the function should be placed on its own line with the proper indentation. This means that no parameter may share the line with an other parameter and that either all parameters are on the same line or every parameter is on its own line.

1 2 3 4 5 FunctionA( Parameter1, Parameter2, Parameter3 );

When a function contains only one array parameter, and the array spans multiple lines, the [ and ] should be placed on the same line as the corresponding ( and ):

1 2 3 4 5 FunctionWithArray([ Element1, Element2, Element3 ]);

Conditions spanning more than one line

Sometimes the condition of an if, while or repeat spans more than one line. In that case the condition always starts on a new line and it properly indented:

  • 1 2 3 4 if LongCondition and OtherLongCondition then
  • 1 2 3 4 while LongCondition and OtherLongCondition do
  • 1 2 3 4 repeat until LongCondition and OtherLongCondition;

Grouping statements

Multiple statements can be grouped by applying the rules for adding empty lines to multi-line statements to a group of statements. Grouping statements this way can make the structure of code more clear. Statements should be grouped when the following conditions are met:

  • Each individual statement in the group occupies only one line.
  • All statements in the group are either assignments or function calls or start with the exact same expression.
  • The resulting group does not contain another group.
  • If not grouping the statements results in a series of groups each consisting of a single statement.

Alignment on delimiters

Alignment of assignments

Assignment statements can be aligned on the assignment sign (:=) by placing all the assignment signs in the same column that starts one space after the longest left-hand side. Assignment statements should be aligned if both of the following conditions are met:

  • The assignments are part of the same statement group.
  • The left-hand sides of the assignment statements share the same complexity. The occurrence of the dot operator increases the complexity of the left-hand side.

Please note that these rules mean that assignments should not be aligned when assignment blocks are separated using an empty line and that statement grouping takes precedence over assignment alignment.

1 2 3 4 Person := TPerson.Create(); Person.FirstName := 'Tester'; Person.LastName := 'Bugfree'; Person.Update(Database);

Alignment of declarations

Variable declarations within the same declaration section should be aligned on the colon (:). The colon should be placed right after the identifier and all the types should be in the same column that starts one space after the colon that is placed after the longest identifier.

1 2 3 type Short: TType1; LongerVariable: TType2;

Alignment within class declarations

  • Within a visibility section, for declarations starting with the keywords constructor, destructor, procedure, function and property, align on the name following the keyword. This means that all the names are located in the same column as the name of a constructor (as constructor is the longest keyword), even when no constructor is present. There is only one space between constructor and its name.

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 TTest = class(TObject) private FValue: TInteger32; procedure SetValue(const Value: TInteger32); public constructor Create(); virtual; destructor Destroy(); override; procedure Test(); function GetTest(): TInteger32; property Value: TInteger32 read FValue write SetValue; end;
  • Within a visibility section, for declarations starting with the keywords class constructor, class destructor, class procedure, class function and class property, align on the name following the keywords. This means that all the names are located in the same column as the name of a class constructor (as class constructor is the longest keyword), even when no class constructor is present. There is only one space between class constructor and its name.

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 TTest = class(TObject) private class function GetValue(): TInteger32; class procedure SetValue(const Value: TInteger32); public class constructor Create(); class destructor Destroy(); class procedure Test(); class function GetTest(): TInteger32; class property Value: TInteger32 read GetValue write SetValue; end;
  • Within a visibility section, for declarations starting with the keywords class var, follow the same rules as a var section. This means that class var is placed on its own line, followed by a properly indented line for each class variable. The types for all the class variables within one class var section start in the same column.

    1 2 3 4 5 6 7 8 9 10 TTest = class(TObject) private class var Variable: TInteger32; LongerVariable: TInteger32; class var AnotherVariable: TInteger32; AnotherLongerVariable: TInteger32; end;