Razvoj Extensiona

Upute za razvoj novih Extensiona za WvdS FPC RAD Studio.

Preduvjeti

  • Postavljeno razvojno okruženje (vidi Start)
  • Osnovno razumijevanje Pascala i pas2js
  • Poznavanje VSCode Extension API

Kreiranje novog Extensiona

1. Kreiranje strukture direktorija

cd sources/extensions
mkdir -p wvds.vscode.{name}/pas
mkdir -p wvds.vscode.{name}/dist
mkdir -p wvds.vscode.{name}/images

2. Kreiranje package.json

{
  "name": "wvds-vscode-{name}",
  "displayName": "WvdS VSCode {Name}",
  "description": "{Opis}",
  "version": "0.1.0",
  "publisher": "wvds",
  "license": "MIT",
  "icon": "images/icon.png",
  "engines": {
    "vscode": "^1.85.0"
  },
  "categories": ["Other"],
  "main": "./dist/extension_main.js",
  "activationEvents": [
    "onCommand:wvds.{name}.{command}"
  ],
  "contributes": {
    "commands": [
      {
        "command": "wvds.{name}.{command}",
        "title": "{Title}",
        "category": "WvdS"
      }
    ]
  },
  "extensionDependencies": [
    "wvds.wvds-vscode-core"
  ]
}

3. Kreiranje build.cfg

[build]
compiler=pas2js
target=vscode-extension
entry=extension_main.pas
 
[pas2js]
options=-Jc -Jirtl.js -Tbrowser
units=../../common;./pas
output=./dist

4. Kreiranje extension_main.pas

unit extension_main;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  JS,
  VSCode.API,
  VSCode.Commands,
  VSCode.ExtensionExports,
  WvdS.VSCode.Strings;
 
procedure Activate(AContext: TExtensionContext);
procedure Deactivate;
 
implementation
 
procedure Handle{Command}(Args: TJSValueDynArray);
begin
  ShowInfoMessage(rs{Name}CommandExecuted);
end;
 
procedure DoActivate(AContext: TExtensionContext);
begin
  // Registracija naredbe
  RegisterCommand('wvds.{name}.{command}', @Handle{Command});
 
  // Logging
  LogInfo('WvdS {Name} Extension aktiviran');
end;
 
procedure Activate(AContext: TExtensionContext);
begin
  try
    DoActivate(AContext);
  except
    on E: Exception do
    begin
      LogError(rsActivationFailed, [E.Message]);
      ShowErrorMessage(Format(rsActivationFailed, [E.Message]));
    end;
  end;
end;
 
procedure Deactivate;
begin
  LogInfo('WvdS {Name} Extension deaktiviran');
end;
 
initialization
  ExportActivateDeactivate(@Activate, @Deactivate);
 
end.

5. Kompilacija

cd sources/extensions/wvds.vscode.{name}
pas2js -Jc -Jirtl.js -Tbrowser \
  -Fu../../common \
  -Fu./pas \
  -FE./dist \
  pas/extension_main.pas

6. Testiranje

  1. F5 u VS Code → Extension Development Host
  2. Paleta naredbi → WvdS: {Command}

Poštivanje slojevite arhitekture

Model Layer

// {Feature}.Models.pas
unit {Feature}.Models;
 
{$mode objfpc}{$H+}
 
interface
 
type
  // Records za strukture podataka
  TWvdS{Feature}Config = record
    Name: string;
    Value: Integer;
    Enabled: Boolean;
  end;
 
  // Enumi
  TWvdS{Feature}Status = (
    fsIdle,
    fsProcessing,
    fsCompleted,
    fsFailed
  );
 
  // Nizovi
  TWvdS{Feature}ConfigArray = array of TWvdS{Feature}Config;
 
implementation
 
end.

Service Layer

// {Feature}.Service.pas
unit {Feature}.Service;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  {Feature}.Models;
 
// Validacija
function ValidateConfig(const AConfig: TWvdS{Feature}Config): Boolean;
 
// Operacije
function ProcessData(const AInput: string): TWvdS{Feature}Config;
 
// Poslovna logika
procedure ExecuteFeature(const AConfig: TWvdS{Feature}Config);
 
implementation
 
uses
  SysUtils, WvdS.VSCode.Strings;
 
function ValidateConfig(const AConfig: TWvdS{Feature}Config): Boolean;
begin
  Result := (AConfig.Name <> '') and (AConfig.Value >= 0);
end;
 
function ProcessData(const AInput: string): TWvdS{Feature}Config;
begin
  // Obrada...
  Result.Name := AInput;
  Result.Value := Length(AInput);
  Result.Enabled := True;
end;
 
procedure ExecuteFeature(const AConfig: TWvdS{Feature}Config);
begin
  if not ValidateConfig(AConfig) then
    raise Exception.Create(rs{Feature}InvalidConfig);
  // Izvršavanje...
end;
 
end.

Extension Layer

// extension_main.pas - samo UI-logika
procedure HandleCommand(Args: TJSValueDynArray);
var
  Config: TWvdS{Feature}Config;
begin
  // 1. Dohvat inputa iz UI-a
  Config := GetConfigFromDialog;
 
  // 2. Poziv servisa (nema logike ovdje!)
  if not ValidateConfig(Config) then
  begin
    ShowWarningMessage(rs{Feature}InvalidInput);
    Exit;
  end;
 
  // 3. Izvršavanje
  try
    ExecuteFeature(Config);
    ShowInfoMessage(rs{Feature}Success);
  except
    on E: Exception do
      ShowErrorMessage(Format(rs{Feature}Failed, [E.Message]));
  end;
end;

Korištenje VSCode API

Commands

// Registracija naredbe
RegisterCommand('wvds.{name}.{action}', @Handler);
 
// Izvršavanje naredbe
ExecuteCommand('wvds.other.action');

Configuration

// Čitanje postavke
var Value := GetConfiguration('wvds.{name}').Get('setting', DefaultValue);
 
// Pisanje postavke
GetConfiguration('wvds.{name}').Update('setting', NewValue, True);

Output Channel

// Korištenje kanala (osigurava Core)
LogInfo('Informacija');
LogWarn('Upozorenje');
LogError('Greška: %s', [E.Message]);
LogDebug('Debug: %s', [Data]);  // Samo kod logLevel=debug

WebView

// Kreiranje WebView Panela
var Panel := CreateWebviewPanel(
  'wvds.{name}.view',
  'Naslov',
  ViewColumn.One,
  WebviewOptions
);
 
// Postavljanje HTML-a
Panel.Webview.Html := LoadTemplate('template.html');
 
// Messaging
Panel.Webview.OnDidReceiveMessage(@HandleMessage);
Panel.Webview.PostMessage(JSObject);

StatusBar

var StatusItem := CreateStatusBarItem(StatusBarAlignment.Left, 100);
StatusItem.Text := '$(check) Spremno';
StatusItem.Show;

Resourcestrings za i18n

Svi stringovi moraju biti definirani u WvdS.VSCode.Strings.pas!

// WvdS.VSCode.Strings.pas
resourcestring
  // {Feature} Extension
  rs{Feature}CommandExecuted = '{Feature} command executed';
  rs{Feature}InvalidConfig = 'Invalid configuration';
  rs{Feature}Success = 'Operation completed successfully';
  rs{Feature}Failed = 'Operation failed: %s';
// WvdS.VSCode.Strings.DE.pas
resourcestring
  rs{Feature}CommandExecuted = '{Feature}-Befehl ausgeführt';
  rs{Feature}InvalidConfig = 'Ungültige Konfiguration';
  rs{Feature}Success = 'Vorgang erfolgreich abgeschlossen';
  rs{Feature}Failed = 'Vorgang fehlgeschlagen: %s';

Error Handling

procedure SafeOperation;
begin
  try
    DoRiskyOperation;
  except
    on E: EFileNotFoundException do
    begin
      LogError(rsFileNotFound, [E.FileName]);
      ShowErrorMessage(Format(rsFileNotFound, [E.FileName]));
    end;
    on E: EAccessDenied do
    begin
      LogError(rsAccessDenied, [E.Path]);
      ShowErrorMessage(Format(rsAccessDenied, [E.Path]));
    end;
    on E: Exception do
    begin
      LogError(rsUnexpectedError, [E.ClassName, E.Message]);
      ShowErrorMessage(Format(rsUnexpectedError, [E.ClassName, E.Message]));
    end;
  end;
end;
Nikada prazni except-blokovi! Svaka Exception mora biti obrađena ili logirana.

Debug-Logging

{$IFDEF DEBUG}
procedure LogDebugTrace(const AMessage: string);
begin
  if not DebugEnabled then Exit;
  WriteDebugLog(Format('[%s] %s at %s', [
    FormatDateTime('hh:nn:ss.zzz', Now),
    AMessage,
    GetCallStack
  ]));
end;
{$ENDIF}

Aktivacija:

  • Kompilacija s -dDEBUG
  • Pokretanje Extensiona s –debug parametrom

Pisanje testova

// Tests/{Feature}.Tests.pas
unit {Feature}.Tests;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  TestFramework, {Feature}.Service, {Feature}.Models;
 
type
  T{Feature}ServiceTest = class(TTestCase)
  published
    procedure TestValidateConfig_ValidInput_ReturnsTrue;
    procedure TestValidateConfig_EmptyName_ReturnsFalse;
    procedure TestProcessData_ValidInput_ReturnsConfig;
  end;
 
implementation
 
procedure T{Feature}ServiceTest.TestValidateConfig_ValidInput_ReturnsTrue;
var
  Config: TWvdS{Feature}Config;
begin
  Config.Name := 'Test';
  Config.Value := 42;
  Config.Enabled := True;
 
  CheckTrue(ValidateConfig(Config));
end;
 
procedure T{Feature}ServiceTest.TestValidateConfig_EmptyName_ReturnsFalse;
var
  Config: TWvdS{Feature}Config;
begin
  Config.Name := '';
  Config.Value := 42;
 
  CheckFalse(ValidateConfig(Config));
end;
 
end.

Checklista prije commita

[ ] SoC poštovan (Model/Service/Extension odvojeni)
[ ] Nema hardkodiranih stringova (korišteni resourcestrings)
[ ] Prijevodi prisutni (EN, DE, SL, HR)
[ ] Error Handling implementiran
[ ] Debug-Logging dodan
[ ] Sigurnosne smjernice provjerene
[ ] Unit-Testovi napisani
[ ] README ažuriran
[ ] CHANGELOG ažuriran

Vidi također

Zuletzt geändert: 29.01.2026. u 22:30