====== Razvoj razširitev ======
Navodila za razvoj novih razširitev za WvdS FPC RAD Studio.
===== Predpogoji =====
* Nastavljeno razvojno okolje (glejte [[.:start|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 ====
- F5 v VS Code -> Extension Development Host
- 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 =====
* [[.:architektur|Pregled arhitekture]]
* [[.:code-konventionen|Kodne konvencije]]
* [[.:sicherheit|Varnostne smernice]]
* [[.:testing|Testiranje]]
* [[.:vscode-wrapper|VSCode ovojni API]]