====== Razvoj Extensiona ======
Upute za razvoj novih Extensiona za WvdS FPC RAD Studio.
===== Preduvjeti =====
* Postavljeno razvojno okruženje (vidi [[.:start|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 ====
- F5 u VS Code -> Extension Development Host
- 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 =====
* [[.:architektur|Pregled arhitekture]]
* [[.:code-konventionen|Konvencije koda]]
* [[.:sicherheit|Sigurnosne smjernice]]
* [[.:testing|Testing]]
* [[.:vscode-wrapper|VSCode Wrapper API]]