Oxygene Code Style Guide
This document provides a practical style guide for structuring and formatting code written in Oxygene.
The Oxygene language intentionally allows several valid coding styles. This guide documents the preferred style used in modern Elements code and libraries. It is not a compiler rule, but following it tends to improve readability, keeps code consistent across projects, and matches how our IDEs and open-source codebases are generally maintained.
Older or compatibility-focused code may intentionally differ, especially when preserving Delphi compatibility or a pre-existing file structure. In those cases, local consistency is usually more important than mechanically restyling everything to match the modern recommendations below.
General Principles
- Match the surrounding file first.
- Prefer modern Oxygene style for new code.
- Avoid unnecessary style-only churn in legacy, generated or imported code.
- Optimize for readability over terseness.
- Keep formatting consistent within a file, type and feature area.
Keywords and Core Syntax
- All keywords should be written in lowercase.
- Use the
methodkeyword instead ofprocedureorfunctionin modern code. - Use the
blockkeyword for Block declarations. - Use
namespaceinstead ofunitfor new files. - Prefer modern Code File Structure and unified type syntax for new code, while preserving classic
interface/implementationlayout when editing older files that already use it. - Prefer explicit Nullability annotations such as
nullableandnot nullableat important API boundaries.
File and Type Layout
For new files, prefer a modern file shape similar to:
namespace Sample.App;
uses
RemObjects.Elements.RTL;
type
MessageService = public class
public
method Send(aText: not nullable String);
begin
writeLn(aText);
end;
end;
Recommended layout rules:
- Start the file with
namespace. - Follow with
uses, if needed. - Use
typeblocks for type declarations. - Do not put an empty line between
typeand the first type. - Separate multi-line types with one empty line.
- Leave one empty line after a visibility header such as
public,privateorprotectedbefore the first member in that block. - Keep related single-line aliases or short enums grouped together, with no unnecessary blank lines inside the group.
- Surround each non-single-line member with one empty line.
- Leave one empty line before the closing
endof a type after the last non-single-line member. - End the file in whatever form the chosen Code File Structure requires.
If a file already uses classic interface/implementation structure, or exists to mirror Delphi-style source layout, keep that structure unless there is a specific reason to refactor it.
Casing and Naming Conventions
Oxygene is case-insensitive, but it preserves case and can warn about inconsistent capitalization. Those warnings should be avoided.
- Keywords should be lowercase.
- Type names should use PascalCase.
- Avoid the
Tprefix common in some Pascal dialects for new type names. - Avoid unnecessary all-uppercase abbreviation prefixes where possible; use Namespaces instead to provide context.
- Interface names should use an
Iprefix followed by a PascalCased name. - Public Members should use PascalCase unless platform conventions strongly suggest otherwise.
Variable prefixes in modern Elements code commonly follow this pattern:
- Fields use an
fprefix:fName - Local Variables use an
lprefix:lName - Parameters use an
aprefix:aName - Loop variables and very short-lived temporaries may use short lowercase names such as
i,j,xorp - Lambda parameters may use either single-letter names or short lowercase words, depending on which reads more clearly
Member Layout
- Group related single-line members together.
- Separate groups of single-line members with a single blank line.
- Leave a single blank line after a visibility section header before the first member in that block.
- Surround multi-line methods, constructors and implemented properties with a single blank line.
- Keep fields and backing storage grouped consistently, usually in
private. - Use visibility sections intentionally, instead of alternating between
publicandprivatefor every member. - Leave a single blank line before the closing
endof a type after the last non-single-line member.
For method-like members, prefer placing the implementation begin on the line after the declaration:
method RefreshData(aForce: Boolean);
begin
if aForce then
LoadData();
end;
This recommendation also applies to constructors, operators, and property getter/setter bodies when they are implemented inline.
Control Flow and begin/end
The language permits several layouts for if statements. Our preferred style is:
- Break after
thenbefore a single statement, but when the branch opens a block,then beginmust stay hanging on the same line. - Break after
dobefore a single statement, but when a block is needed,do beginmust stay hanging on the same line. - Inline
if Something then exit;style should be avoided. - If either side of an
if/elseuses abegin/endblock, the other side should too. elseshould start on its own line, not afterend.
Bad:
if Something then exit;
Better:
if Something then
exit;
Bad:
if Something then
DoThis
else begin
DoThat;
AndThisOtherThing;
end;
Better:
if Something then begin
DoThis;
end
else begin
DoThat;
AndThisOtherThing;
end;
Statements with a begin/end block should not be nested inside a statement where the outer block was omitted.
Bad:
if Something then
if SomethingElse then
for each a in list do begin
...
end;
Better:
if Something then begin
if SomethingElse then begin
for each a in list do begin
...
end;
end;
end;
For for, while, loop, locking and similar statements, begin must stay on the same line as do when a block is needed:
for each a in list do begin
Process(a);
end;
Guard Clauses and Nesting
Prefer shallow nesting and early exits over wrapping the whole body of a method in one large condition.
if not assigned(aRequest) then
exit;
if aRequest.Cancelled then
exit;
ProcessRequest(aRequest);
This style tends to make methods easier to scan and reduces indentation drift.
Spacing and Blank Lines
Consistent spacing should be used throughout a file.
- Use spaces consistently for indentation; avoid mixing tabs and spaces in a single file.
- Use spaces around
:=,=,<>, comparisons, and most binary operators. - Do not insert unnecessary alignment spaces to create visual columns.
- Keep generic declarations compact:
List<String>, notList< String >. - Keep typecasts and nullability annotations compact:
aValue as not nullable,nullable String.
Blank line conventions:
- No blank line between
typeand the first type. - One blank line between multi-line types.
- One blank line between groups of related single-line members.
- One blank line between multi-line methods, constructors and implemented properties.
Groups of members may be visually separated with a single comment line // or a short triple-comment separator when that genuinely improves navigation through a large type.
Strings, Nullability and Common Idioms
Modern Elements code commonly uses a few recurring idioms:
- Prefer
assigned(...)andnot assigned(...)for reference checks. - Prefer
coalesce(...),coalesceEmpty(...), orvalueOrDefault(...)when they clearly express fallback behavior. - Use
length(...) = 0andlength(...) > 0for empty or non-empty string checks. - When whitespace-only values should count as empty, use
length(aValue:Trim) = 0orlength(aValue:Trim) > 0. - Do not introduce
String.IsNullOrEmpty(...)orString.IsNullOrWhiteSpace(...)in modern code.
Examples:
if not assigned(aSession) then
exit;
if length(aName) = 0 then
exit;
if length(aToken:Trim) = 0 then
raise new Exception("Token is required.");
Collections, Loops and Expressions
- Prefer
for eachwhen the loop index is not required. - Use index-based
forloops when the numeric index matters. - Prefer Sequences and LINQ-style helpers when they improve clarity.
- Prefer local
vardeclarations close to their first use instead of wide method-levelvarsections. - Use
ifExpressions when they are clearly shorter and more readable than a full statement block. - When selecting the first non-empty string, prefer
coalesceEmpty(...)overif length(...) > 0 then ... else ....
Examples:
for each aItem in aItems do
Handle(aItem);
var lMessage := if lSuccess then
"Completed."
else
"Failed.";
Properties and Inline Initialization
Modern Oxygene code frequently uses concise property declarations and inline initialization:
- Use auto-properties for simple state.
- Use inline getter or setter bodies only when they remain small and obvious.
- Prefer
readonlyfor collection properties that expose a stable instance. - Use
lazywhen deferred initialization improves clarity or cost. - Use
locked onwhen synchronized access is intended.
Example:
property Items: List<String> := new List<String>; readonly;
property CorrelationId: not nullable String read fCorrelationId write begin
if length(value:Trim) = 0 then
raise new Exception("CorrelationId cannot be empty.");
fCorrelationId := value.Trim;
end; locked on self;
Comments
Comments should be sparse and purposeful.
- Prefer short
//comments for rationale. - Avoid comments that merely restate what the code already says.
- Keep TODOs, warnings and hints meaningful and actionable.
- Use separator comments only where they improve navigation through a large type or feature block.
Modern vs. Legacy Code
The recommendations above reflect the preferred style for new Oxygene code and for modern codebases.
When editing older code, especially compatibility layers or Delphi-style source files:
- preserve the existing file structure unless there is a specific reason to refactor it
- preserve Delphi-compatible declarations where needed
- avoid broad formatting churn that obscures the functional change
- improve local consistency first, instead of partially converting the file to a different style
In short: use the modern style for new code, but match the local style when maintaining older code.
Checklist
Before finalizing Oxygene code, ask:
- Are keywords lowercase?
- Does the file match the surrounding codebase?
- Are type and member names consistently cased?
- Are parameter, local and field prefixes consistent?
- Does every
thenbreak onto a new line? - Are
ifandelsebalanced in their use ofbegin/end? - Are blank lines used only for real visual separation?
- Are nullability and string checks using the clearest idiom?
- Are comments useful and minimal?