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 method keyword instead of procedure or function in modern code.
  • Use the block keyword for Block declarations.
  • Use namespace instead of unit for new files.
  • Prefer modern Code File Structure and unified type syntax for new code, while preserving classic interface/implementation layout when editing older files that already use it.
  • Prefer explicit Nullability annotations such as nullable and not nullable at 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 type blocks for type declarations.
  • Do not put an empty line between type and the first type.
  • Separate multi-line types with one empty line.
  • Leave one empty line after a visibility header such as public, private or protected before 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 end of 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 T prefix 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 I prefix 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 f prefix: fName
  • Local Variables use an l prefix: lName
  • Parameters use an a prefix: aName
  • Loop variables and very short-lived temporaries may use short lowercase names such as i, j, x or p
  • Lambda parameters may use either single-letter names or short lowercase words, depending on which reads more clearly

Member Layout

Inside a Class or Record:

  • 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 public and private for every member.
  • Leave a single blank line before the closing end of 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 then before a single statement, but when the branch opens a block, then begin must stay hanging on the same line.
  • Break after do before a single statement, but when a block is needed, do begin must stay hanging on the same line.
  • Inline if Something then exit; style should be avoided.
  • If either side of an if/else uses a begin/end block, the other side should too.
  • else should start on its own line, not after end.

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>, not List< String >.
  • Keep typecasts and nullability annotations compact: aValue as not nullable, nullable String.

Blank line conventions:

  • No blank line between type and 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(...) and not assigned(...) for reference checks.
  • Prefer coalesce(...), coalesceEmpty(...), or valueOrDefault(...) when they clearly express fallback behavior.
  • Use length(...) = 0 and length(...) > 0 for empty or non-empty string checks.
  • When whitespace-only values should count as empty, use length(aValue:Trim) = 0 or length(aValue:Trim) > 0.
  • Do not introduce String.IsNullOrEmpty(...) or String.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 each when the loop index is not required.
  • Use index-based for loops when the numeric index matters.
  • Prefer Sequences and LINQ-style helpers when they improve clarity.
  • Prefer local var declarations close to their first use instead of wide method-level var sections.
  • Use if Expressions when they are clearly shorter and more readable than a full statement block.
  • When selecting the first non-empty string, prefer coalesceEmpty(...) over if 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 readonly for collection properties that expose a stable instance.
  • Use lazy when deferred initialization improves clarity or cost.
  • Use locked on when 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 then break onto a new line?
  • Are if and else balanced in their use of begin/end?
  • Are blank lines used only for real visual separation?
  • Are nullability and string checks using the clearest idiom?
  • Are comments useful and minimal?

See Also