====== Sviluppo estensioni ======
Guida allo sviluppo di nuove estensioni per WvdS FPC RAD Studio.
===== Prerequisiti =====
* Ambiente di sviluppo configurato (vedere [[.:start|Start]])
* Conoscenza base di Pascal e pas2js
* Familiarita con l'API delle estensioni VSCode
===== Creare una nuova estensione =====
==== 1. Creare la struttura directory ====
cd sources/extensions
mkdir -p wvds.vscode.{name}/pas
mkdir -p wvds.vscode.{name}/dist
mkdir -p wvds.vscode.{name}/images
==== 2. Creare package.json ====
{
"name": "wvds-vscode-{name}",
"displayName": "WvdS VSCode {Name}",
"description": "{Descrizione}",
"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. Creare build.cfg ====
[build]
compiler=pas2js
target=vscode-extension
entry=extension_main.pas
[pas2js]
options=-Jc -Jirtl.js -Tbrowser
units=../../common;./pas
output=./dist
==== 4. Creare 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
// Registrare comando
RegisterCommand('wvds.{name}.{command}', @Handle{Command});
// Logging
LogInfo('Estensione WvdS {Name} attivata');
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('Estensione WvdS {Name} disattivata');
end;
initialization
ExportActivateDeactivate(@Activate, @Deactivate);
end.
==== 5. Compilare ====
cd sources/extensions/wvds.vscode.{name}
pas2js -Jc -Jirtl.js -Tbrowser \
-Fu../../common \
-Fu./pas \
-FE./dist \
pas/extension_main.pas
==== 6. Testare ====
- F5 in VS Code -> Extension Development Host
- Palette comandi -> ''WvdS: {Command}''
===== Rispettare l'architettura a strati =====
==== Layer Model ====
// {Feature}.Models.pas
unit {Feature}.Models;
{$mode objfpc}{$H+}
interface
type
// Record per strutture dati
TWvdS{Feature}Config = record
Name: string;
Value: Integer;
Enabled: Boolean;
end;
// Enum
TWvdS{Feature}Status = (
fsIdle,
fsProcessing,
fsCompleted,
fsFailed
);
// Array
TWvdS{Feature}ConfigArray = array of TWvdS{Feature}Config;
implementation
end.
==== Layer Service ====
// {Feature}.Service.pas
unit {Feature}.Service;
{$mode objfpc}{$H+}
interface
uses
{Feature}.Models;
// Validazione
function ValidateConfig(const AConfig: TWvdS{Feature}Config): Boolean;
// Operazioni
function ProcessData(const AInput: string): TWvdS{Feature}Config;
// Logica di business
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
// Elaborazione...
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);
// Esecuzione...
end;
end.
==== Layer Extension ====
// extension_main.pas - solo logica UI
procedure HandleCommand(Args: TJSValueDynArray);
var
Config: TWvdS{Feature}Config;
begin
// 1. Ottenere input dalla UI
Config := GetConfigFromDialog;
// 2. Chiamare il servizio (nessuna logica qui!)
if not ValidateConfig(Config) then
begin
ShowWarningMessage(rs{Feature}InvalidInput);
Exit;
end;
// 3. Esecuzione
try
ExecuteFeature(Config);
ShowInfoMessage(rs{Feature}Success);
except
on E: Exception do
ShowErrorMessage(Format(rs{Feature}Failed, [E.Message]));
end;
end;
===== Usare l'API VSCode =====
==== Commands ====
// Registrare comando
RegisterCommand('wvds.{name}.{action}', @Handler);
// Eseguire comando
ExecuteCommand('wvds.other.action');
==== Configuration ====
// Leggere impostazione
var Value := GetConfiguration('wvds.{name}').Get('setting', DefaultValue);
// Scrivere impostazione
GetConfiguration('wvds.{name}').Update('setting', NewValue, True);
==== Output Channel ====
// Usare il channel (fornito da Core)
LogInfo('Informazione');
LogWarn('Avviso');
LogError('Errore: %s', [E.Message]);
LogDebug('Debug: %s', [Data]); // Solo con logLevel=debug
==== WebView ====
// Creare pannello WebView
var Panel := CreateWebviewPanel(
'wvds.{name}.view',
'Titolo',
ViewColumn.One,
WebviewOptions
);
// Impostare HTML
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) Pronto';
StatusItem.Show;
===== Resourcestrings per i18n =====
**Tutte le stringhe devono essere definite in WvdS.VSCode.Strings.pas!**
// WvdS.VSCode.Strings.pas
resourcestring
// Estensione {Feature}
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 ausgefuhrt';
rs{Feature}InvalidConfig = 'Ungultige Konfiguration';
rs{Feature}Success = 'Vorgang erfolgreich abgeschlossen';
rs{Feature}Failed = 'Vorgang fehlgeschlagen: %s';
===== Gestione errori =====
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;
**Mai blocchi except vuoti!** Ogni eccezione deve essere gestita o loggata.
===== 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}
Attivazione:
* Compilare con ''-dDEBUG''
* Avviare l'estensione con parametro ''--debug''
===== Scrivere test =====
// 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.
===== Checklist prima del commit =====
[ ] SoC rispettato (Model/Service/Extension separati)
[ ] Nessuna stringa hardcoded (usati resourcestrings)
[ ] Traduzioni presenti (EN, DE, SL, HR)
[ ] Gestione errori implementata
[ ] Debug logging aggiunto
[ ] Linee guida sicurezza verificate
[ ] Unit test scritti
[ ] README aggiornato
[ ] CHANGELOG aggiornato
===== Vedi anche =====
* [[.:architektur|Panoramica architettura]]
* [[.:code-konventionen|Convenzioni del codice]]
* [[.:sicherheit|Linee guida sulla sicurezza]]
* [[.:testing|Testing]]
* [[.:vscode-wrapper|API Wrapper VSCode]]