Razvoj razširitev

Navodila za razvoj novih razširitev za WvdS FPC RAD Studio.

Predpogoji

  • Nastavljeno razvojno okolje (glejte Začetek)
  • Osnovno razumevanje Pascala in pas2js
  • Poznavanje VSCode Extension API

Ustvarjanje nove razširitve

1. Ustvari strukturo imenikov

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

2. Ustvari 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": "{Naslov}",
        "category": "WvdS"
      }
    ]
  },
  "extensionDependencies": [
    "wvds.wvds-vscode-core"
  ]
}

3. Ustvari build.cfg

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

4. Ustvari 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 ukaza
  RegisterCommand('wvds.{name}.{command}', @Handle{Command});
 
  // Beleženje
  LogInfo('WvdS {Name} razširitev aktivirana');
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} razširitev deaktivirana');
end;
 
initialization
  ExportActivateDeactivate(@Activate, @Deactivate);
 
end.

5. Prevedi

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

6. Testiraj

  1. F5 v VS Code → Extension Development Host
  2. Ukazna paleta → WvdS: {Ukaz}

Upoštevanje plastne arhitekture

Modelna plast

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

Storitvena plast

// {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
  // Obdelava...
  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);
  // Izvajanje...
end;
 
end.

Plast razširitve

// extension_main.pas - samo UI logika
procedure HandleCommand(Args: TJSValueDynArray);
var
  Config: TWvdS{Feature}Config;
begin
  // 1. Pridobi vnos iz UI
  Config := GetConfigFromDialog;
 
  // 2. Pokliči storitev (brez logike tukaj!)
  if not ValidateConfig(Config) then
  begin
    ShowWarningMessage(rs{Feature}InvalidInput);
    Exit;
  end;
 
  // 3. Izvedi
  try
    ExecuteFeature(Config);
    ShowInfoMessage(rs{Feature}Success);
  except
    on E: Exception do
      ShowErrorMessage(Format(rs{Feature}Failed, [E.Message]));
  end;
end;

Uporaba VSCode API

Ukazi

// Registracija ukaza
RegisterCommand('wvds.{name}.{action}', @Handler);
 
// Izvajanje ukaza
ExecuteCommand('wvds.other.action');

Konfiguracija

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

Izhodni kanal

// Uporaba kanala (zagotovljen s strani Core)
LogInfo('Informacija');
LogWarn('Opozorilo');
LogError('Napaka: %s', [E.Message]);
LogDebug('Razhroščevanje: %s', [Data]);  // Samo pri logLevel=debug

WebView

// Ustvari WebView panel
var Panel := CreateWebviewPanel(
  'wvds.{name}.view',
  'Naslov',
  ViewColumn.One,
  WebviewOptions
);
 
// Nastavi HTML
Panel.Webview.Html := LoadTemplate('template.html');
 
// Sporočanje
Panel.Webview.OnDidReceiveMessage(@HandleMessage);
Panel.Webview.PostMessage(JSObject);

StatusBar

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

Resourcestrings za i18n

Vsi nizi morajo biti definirani v 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';

Obravnava napak

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;
Nikoli praznih except blokov! Vsako izjemo je treba obravnavati ali beležiti.

Razhroščevalno beleženje

{$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:

  • Prevedi z -dDEBUG
  • Zaženi razširitev s parametrom –debug

Pisanje testov

// 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.

Kontrolni seznam pred potrditvijo

[ ] SoC upoštevano (Model/Service/Extension ločeni)
[ ] Brez zakodiranih nizov (uporabljeni resourcestrings)
[ ] Prevodi prisotni (EN, DE, SL, HR)
[ ] Obravnava napak implementirana
[ ] Razhroščevalno beleženje dodano
[ ] Varnostne smernice preverjene
[ ] Testi enot napisani
[ ] README posodobljen
[ ] CHANGELOG posodobljen

Glejte tudi

Zuletzt geändert: dne 29.01.2026 ob 22:23