====== 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]]