Code Conventions
Mandatory coding standards for the WvdS FPC RAD Suite.
Naming Conventions
Types
| Category | Prefix | Example |
| Class | TWvdS* | TWvdSBuildConfig, TWvdSProjectManager |
| Interface | IWvdS* | IWvdSLogger, IWvdSParser |
| Record | TWvdS* | TWvdSBuildResult, TWvdSToolPath |
| Enum | TWvdS* | TWvdSProjectType, TWvdSBuildStatus |
| Exception | EWvdS* | EWvdSFileNotFound, EWvdSParseError |
| Callback | TWvdS* | TWvdSBuildCallback, TWvdSProgressHandler |
External
API types (VSCode, Node.js) retain their original names.
Unit Names
Microsoft-style namespaces with WvdS prefix:
WvdS.{Domain}.{Layer}.pas
Examples:
WvdS.Build.Models.pas
WvdS.Build.Service.pas
WvdS.Projects.SettingsDialog.pas
WvdS.VSCode.Commands.pas
Layer Suffixes
| Suffix | Content | Example |
| .Models | Records, enums, types | WvdS.Build.Models |
| .Service | Business logic | WvdS.Build.Service |
| .Dialog | WebView dialogs | WvdS.Projects.SettingsDialog |
| .Provider | VSCode API wrapper | WvdS.Designer.EditorProvider |
Variables and Parameters
| Category | Prefix | Example |
| Private fields | F | FProjectPath, FConfig |
| Parameters | A | APath, AOptions, ACallback |
| Local variables | None | Result, I, Config |
Resourcestrings
Prefix by feature:
| Prefix | Feature |
| rsCore* | Core Extension |
| rsBuild* | Build Extension |
| rsProject* | Projects Extension |
| rsDesigner* | UI Designer |
| rsPreview* | UI Preview |
| rsMeta* | UI Meta |
| rsPackaging* | Packaging |
| rsTool* | Toolchain |
Code Structure
Unit Layout
unit WvdS.{Feature}.{Layer};
{$mode objfpc}{$H+}
interface
uses
// 1. System Units
SysUtils, Classes,
// 2. WvdS Common
WvdS.System, WvdS.Collections,
// 3. Feature-specific
WvdS.{Feature}.Models;
type
// Type definitions
function PublicFunction(const AParam: string): Boolean;
procedure PublicProcedure(AValue: Integer);
implementation
uses
// Private uses (units needed only here)
WvdS.VSCode.Strings;
// Private types and variables
type
TInternalHelper = class
end;
var
InternalState: TObject;
// Implementations
function PublicFunction(const AParam: string): Boolean;
begin
// ...
end;
procedure PublicProcedure(AValue: Integer);
begin
// ...
end;
initialization
// Initialization
finalization
// Cleanup
end.
Class Layout
type
TWvdSExampleClass = class(TObject)
private
FName: string;
FValue: Integer;
procedure SetName(const AValue: string);
protected
procedure DoInternalWork; virtual;
public
constructor Create(const AName: string);
destructor Destroy; override;
procedure Execute;
property Name: string read FName write SetName;
property Value: Integer read FValue write FValue;
end;
Documentation
(*
@abstract(Short description in one sentence.)
Detailed description of purpose and usage.
Can span multiple sentences.
@param(APath Full path to the file)
@param(AOptions Optional configuration, can be nil)
@returns(True if successful, False on error)
@raises(EWvdSFileNotFound when file does not exist)
@raises(EWvdSAccessDenied when no read permissions)
Security:
- CWE-22: Path is validated against path traversal
@seealso(RelatedFunction)
@seealso(TWvdSRelatedClass)
*)
function ProcessFile(const APath: string; AOptions: TOptions): Boolean;
// Short comment for single line
Result := CalculateValue;
// Multi-line comment for more complex logic
// Explains the why, not the what
if (Value > Threshold) and (Mode = mAdvanced) then
begin
// We need to use the advanced algorithm here,
// as the simple one becomes inaccurate for large values
Result := AdvancedCalculation(Value);
end;
Constants Instead of Magic Numbers
// FORBIDDEN
if Length(Name) > 64 then
if Timeout > 30000 then
// CORRECT
const
MAX_PROJECT_NAME_LENGTH = 64;
DEFAULT_TIMEOUT_MS = 30000;
if Length(Name) > MAX_PROJECT_NAME_LENGTH then
if Timeout > DEFAULT_TIMEOUT_MS then
Terminology
Use consistent terms:
| Use | Avoid |
| Path | Url, Location, Dir (inconsistent) |
| Config | Settings, Options, Prefs (inconsistent) |
| Create | Make, Build (for objects) |
| Generate | Create (for output) |
| Validate | Check, Verify (for inputs) |
| Initialize | Setup, Init (inconsistent) |
| Execute | Run, Process (inconsistent) |
Indentation
2 Spaces for indentation (not tabs)
begin on its own line for blocks
end at the same indentation level as the corresponding begin
// CORRECT
procedure Example;
begin
if Condition then
begin
DoSomething;
DoMore;
end
else
begin
DoAlternative;
end;
end;
// FORBIDDEN
procedure Example; begin
if Condition then begin DoSomething; DoMore; end
else DoAlternative;
end;
Line Length
// For long parameter lists
function VeryLongFunctionName(
const AFirstParameter: string;
const ASecondParameter: Integer;
AThirdParameter: TOptions
): Boolean;
// For long conditions
if (FirstCondition) and
(SecondCondition) and
(ThirdCondition) then
begin
// ...
end;
Forbidden Patterns
Empty Exception Handlers
// FORBIDDEN
try
DoSomething;
except
// Do nothing - swallowing errors!
end;
// CORRECT
try
DoSomething;
except
on E: Exception do
LogError(rsUnexpectedError, [E.ClassName, E.Message]);
end;
TODO/FIXME in Production Code
// FORBIDDEN in main branch
// TODO: Implement this later
// FIXME: This is broken
// ALLOWED only in feature branches, must be removed before merge
Hardcoded Strings
// FORBIDDEN
ShowMessage('File not found');
ShowMessage('Datei nicht gefunden');
// CORRECT
ShowMessage(rsFileNotFound); // resourcestring
Global Variables in Services
// FORBIDDEN
var
GlobalConfig: TConfig; // Hard to test!
// CORRECT - Pass parameters
function ProcessWithConfig(const AConfig: TConfig): Boolean;
Testability
Services must be testable without VSCode/UI:
// FORBIDDEN - UI in Service
procedure ValidateAndShowError(const AName: string);
begin
if not IsValid(AName) then
ShowMessage('Invalid name'); // UI dependency!
end;
// CORRECT - Return value
function ValidateName(const AName: string; out AError: string): Boolean;
begin
Result := IsValid(AName);
if not Result then
AError := rsInvalidName;
end;
pas2js Compatibility (IMPORTANT)
pas2js is NOT 100% compatible with FPC! These restrictions MUST be observed.
Unsupported Features
| Feature | FPC | pas2js | Workaround |
class var | Yes | No | Use unit-level variable |
Inline var | Yes | No | Declare in var block |
Int64 | Yes | No | Use Integer |
| | | |