Lazarus IDE Tools/nl

From Lazarus wiki
Jump to navigationJump to search

Deutsch (de) English (en) español (es) suomi (fi) français (fr) 日本語 (ja) 한국어 (ko) Nederlands (nl) português (pt) русский (ru) slovenčina (sk) 中文(中国大陆) (zh_CN)

Overzicht

De IDE gebruikt een verzameling tools voor het "parsen" (uit elkaar pluizen) van de source code en bewerken van tekst, de zogenaamde "codetools". Deze tools bieden functionaliteiten als het vinden van een declaratie, code completion en allerlei andere code bewerkingsfuncties. Deze tools kunnen je een hoop tijd en dubbel werk besparen. Je kunt ze helemaal naar eigen inzicht inrichten en iedere functies is bereikbaar via een "short cut" (Zie de Editor Options!).

De tools werken alleen met source code en begrijpen de verschillende Pascal varianten (FPC, Delphi en Kylix) dus zijn gecompileerde units of een compiler niet nodig. Je kunt tegelijkertijd Delphi en FPC code bewerken en zelfs met verschillende Delphi en FPC versies tegelijkertijd. Hierdoor wordt het overzetten van Delphi code naar Lazarus nog makkelijker.

Ovezicht van IDE shortcuts

Method Jumping Ctrl+Shift+Up (Schakelt tussen de definitie en the implementatie van een method)
Code Templates Ctrl+J
Code Completion (Class Completion)   Ctrl+Shift+C
Identifier Completion Ctrl+Space

Method Jumping

Om te springen van de definitie van een procedure (of function) naar de implementatie hiervan kun je Ctrl+Shift+Up gebruiken.

Bijvoorbeeld:

interface

procedure DoSomething; // procedure definition
 
implementation
 
procedure DoSomething; // procedure body 
begin
end;

Als de cursor ergens in de procedure "body" staat en je geeft Ctrl+Shift+Up, dan zal de cursor naar de definitie van de procedure springen. Druk je nogmaals op Ctrl+Shift+Up dan gaat de cursor weer naar de implementatie van de procedure net achter de eerste 'begin'.

Dit werkt natuurlijk ook bij methods (procedures van een classe).

Hint: 'Method Jumping' springt naar de procedure met dezelfde naam en parameter lijst. Als er echter geen procedure gevonden kan worden die precies gelijk is, wordt er gesprongen naar een procedure die het meest overeenkomt, de cursor wordt dan geplaatst op het punt waar de eerste afwijking is gevonden, in Delphi gebeurt dit helaas niet. Dit kan handig zijn bij het wijzigen van de parameterlijst.

Bijvoorbeeld een procedure met een andere parameterlijst:

interface

procedure DoSomething(p: char); // procedure definition

implementation
  
procedure DoSomething(p: string); // procedure body
begin
end;

Hier zal de cursor dus geplaatst worden voor 'string' als er gebruik gemaakt wordt van Method Jumping. Ook het wijzigen van een procedure naam is hiermee eenvoudiger:

Bijvoorbeeld:
Je hebt de procedure 'DoSomething' hernoemd naar 'MakeIt':

interface

procedure MakeIt; // procedure definition

implementation

procedure DoSomething; // procedure body
begin
end;

Als je dan op ctrl+shift+C drukt, zal de IDE beginnen met zoeken naar de juiste body. Omdat het die niet exact kan vinden zal het gaan zoeken naar een andere geschikte kandidaat. Omdat je maar een procudure wijzigde zal de oude DoSomething procedure de enige zijn zonder een definitie en zal de IDE hiernaar toe springen en de cursor plaatsen voor 'DoSomething'.

Include Files

Met behulp van de {$I bestandsnaam} directive kunnen bestanden als onderdeel van de source worden opgenomen. Lazarus en FPC maken erg veel gebruik van deze zogenaamde "Include files", omdat ze de code leesbaarder houden en de code redundancy verminderen. De leesbaarheid wordt verhoogt doordat er geen onoverzichtelijke {$IFDEF} constructies gebruikt hoeven worden voor de ondersteuning van meerdere platforms.

De Lazarus IDE kent een goede ondersteuning van het gebruik van include files. Als bijvoorbeeld de implementatie van een Unit in een inlcude file is opgenomen, zal het springen van de definitie naar de implementatie ook dan goed gaan en de IDE zal dus de include file openen en daar de implementatie van de procedure vinden!

Ook code completion houdt hier rekening mee. Als de implementatie van een classe in een Include file is geplaatst zal het toevoegen van een method in de definitie, de automatisch implementatie (ctrl+shift+C) ook in de include file plaatsen. Je kunt dus de volledige implementatie van ee class in een include file doen. (Dit is in Lazarus eigenlijk altijd zo gedaan.)

Pas op! Als wel de include file is geopend met de implementatie van een classe zal Method Jumping of Find Decleration niet de unit openen waarin de include file is opgenomen. Dus altijd eerst de unit zelf openen en pas dan de include file met de implementatie.

De IDE analyseert de code en controleert daarbij de include directives. Voor later gebruik wordt de informatie over de relaties tussen units en include files opgeslagen in een bestand (includelinks.xml). Als je dan een volgende keer de include file opent zal de IDE bij een Find Declaration of een Method Jump weten welke unit geopend moet worden.

Code Templates

"Code templates" zijn stukjes tekst die met een bepaalde toets-combinatie worden omgezet naar een stuk code.

De standaard toets-combinatie voor Code Templates is Ctrl+J. De stukjes tekst (templates) definieer je via Environment -> Editor Options -> CodeTools. Voor het gebruik ervan tik je dan de tekst gevolgd door ctrl+J.

Bijvoorbeeld: Als je 'classf' intikt, de cursor achter de f laat staan en dan op Ctrl+J drukt, zal 'classf' worden vervangen door

T = class(T)
private

public
  constructor Create;
  destructor Destroy; override;
end;

waarbij de cursor achter de eerste T staat.

Je kunt ook in de IDE een lijst met templates op vragen door de cursor op een lege plek te plaatsen en dan op ctrl+J te drukken. Er zal dan een popup veschijnen met een lijst van de beschikbare templates. Door het gebruik van de pijltjes-toetsen of door het intikken van een aantal letters kun je een keuze maken. Deze keuze bevestig je dan met Enter waarbij de gekozen template wordt uitgevoerd. Zoals gewoonlijk kun je met ESC de popup sluiten, zonder dat er een template wordt uitgevoerd.

Code Completion

Code Completion kan via het menu (Edit -> Complete Code) of via de short cut Ctrl+Shift+C opgeroepen woden.

Verschil met Delphi: De functionaliteit die in Delphi "Code Completion" heet, is in Lazarus opgenomen als "Identifier completion". Deze functie wordt in beide omgevingen opgeroepen met Ctrl+Spatie.

Code completion kent een aantal varianten:

Welke variant wordt opgeroepen is afhankelijk van de cursor positie in de Editor.

Class Completion

Class completion is de meest veelzijdige functie. Je schrijft een class en voegt de methodes en properties toe en Code Completion zal de methode implementaties, de methodes voor het lezen en schrijven van de properties en de bijbehorende private vrariabelen voor je toevoegen.

Bijvoorbeeld. Definieer een class (Gebruik de code templates om je wat tikwerk te besparen):

TExample = class(TObject)
public
  constructor Create;
  destructor Destroy; override;
end;

Zet daarna de cursor ergens in deze tekst en druk op Ctrl+Shift+C. Het gevolg zal zijn dat in de implementatie sectie van je unit de volgende tekst is verschenen, zodat je gelijk verder kunt met het schrijven van de Create method::

{ TExample }

constructor TExample.Create;
begin
  |
end;

destructor TExample.Destroy;
begin
  inherited Destroy;
end;

Let op: De '|' geeft hier de plaats van de cursor aan!

Hint: Zoals we gezien hebben kunnen je met Ctrl+Shift+Up heen en weer schakelen tussen de definitie en de implementatie.

Zoals je ziet heeft de IDE ook een aanroep naar 'inherited Destroy' toegevoegd. Dit gebeurt als een methode met override is gedefinieerd in de ancestor (de class waar jouw class van is afgeleid).

Voeg nu de method DoSomething toe aan de class definitie:

TExample = class(TObject)
public
  constructor Create;
  procedure DoSomething(i: integer);
  destructor Destroy; override;
end;

en druk op Ctrl+Shift+C. De IDE zal het volgende toevoegen

procedure TExample.DoSomething(i: integer);
begin
  |
end;

Afhankelijk van de door jouw ingestelde voorkeur wordt deze methode toegevoegd tussen Create and Destroy zoals in de class definitie of op alfabetische volgorde. Deze voorkeur kun je instellen in Environment > Codetools Options -> Code Creation.

Complete Properties
Voeg een property toe aan de class:

TExample = class(TObject)
public
  constructor Create;
  procedure DoSomething(i: integer);
  destructor Destroy; override;
  property AnInteger: Integer;
end;

Druk op Ctrl+Shift+C en het volgende wordt in de implementatie sectie toegevoegd:

procedure TExample.SetAnInteger(const AValue: integer);
begin
  |if FAnInteger=AValue then exit;
  FAnInteger:=AValue;
end;

Code completion heeft een Schrijf methode toegevoegd en daarin wat algemene code gezet. Maar dat is niet het enige! Druk op Ctrl+Shift+Up om terug te keren naar de class definitie en zie wat daar is gewijzigd:

TExample = class(TObject)
private
  FAnInteger: integer;
  procedure SetAnInteger(const AValue: integer);
public
  constructor Create;
  procedure DoSomething(i: integer);
  destructor Destroy; override;
  property AnInteger: integer read FAnInteger write SetAnInteger;
end;

Het toegevoegde property is uitgebreid door aan te geven welke private variabele door dit property gelezen wordt en zoals gezegd de schrijf methode voor die variabele. We zien ook de nieuwe private sectie waarin deze variabele en de bijbehorende schrijfmethode (ook wel 'setter' genoemd) zijn opgenomen. Het is algemeen gebruikelijk om de naam van private variabelen vooraf te laten gaan door een 'F' en de schrijfmethode (setter) te laten beginnen met 'Set'. Mocht je dat anders willen dan kun je dit wijzigen in Environment > Codetools Options -> Code Creation.

Je kunt ook een "read only" property maken door:

property PropName: PropType read;

in te tikken. Als je dan Ctrl+Shift+C drukt wordt dit

property PropName: PropType read FPropName;

In theorie kun je een "write only" property maken door:

 property PropName: PropType write;

in te tikken, wat dan door Code Completion wordt aangevuld tot

property PropName: PropType write SetPropName;

Het nut van een property dat je alleen kunt schrijven is mij niet helemaal duidelijk, maar het is dus mogelijk. Een "read only" property met een lees methode (ook wel 'Getter' genoemd):

property PropName: PropType read GetPropName;

Deze regel wordt dan niet veranderd, maar code completion zal wel de GetPropName function toevoegen:

function GetpropName: PropType;

Je kunt ook een property maken met een "stored" modifier:

property PropName: PropType stored;

dit zal worden uitgebreid door CodeCompletion tot:

property PropName: PropType read FPropName write SetPropName stored PropNameIsStored;

De stored modifier bepaald of een property wordt opgeslagen met de form definitie. Als het niet is opgegeven wordt de waarde True aangenomen.

Tip: Identifier completion herkent ook incomplete properties en zal de standaard namen voorstellen. Dus bijvoorbeeld::

property PropName: PropType read |;

Als de cursor een spatie achter 'read' staat en je drukt op Ctrl+Space dan krijg je een lijstje te zien met de variable 'FPropName' en de 'setter' 'SetPropName'.

Forward Procedure Completion

"Forward Procedure Completion" is een onderdeel van de Code Completion en voegt procedure implementaties toe die missen. Dit onderdeel van Code Completion wordt opgeroepen als de cursor op een procedure definitie is geplaatst.

Bijvoorbeeld: Voeg een nieuwe procedure toe aan de interface sectie:

procedure DoSomething;

Zet de cursor er op en druk Ctrl+Shift+C. Er zal dan in de implementatie sectie een raamwerk voor de procedure worden gemaakt:

procedure DoSomething;
begin
  |
end;

Tip: Ook hier kun je tussen de definitie en de implementatie heen en weer springen met Ctrl+Shift+Up.

Waar de nieuwe procedure implementatie wordt geplaatst is afhankelijk van de voorkeur zoals die is ingesteld in Environment > Codetools Options -> Code Creation. Als er al een aantal procedures zijn opgenomen, zal de IDE proberen de implementaties in dezelfde volgorde te zetten als de definities. Bijvoorbeeld:

 procedure Proc1;
 procedure Proc2; // Nieuw
 procedure Proc3;

Als de implementaties van Proc1 en Proc3 al aanwezig zijn, zal de implementatie van Proc2 hier tussen geplaatst worden.

Meerdere procedures in een keer kan ook:

procedure Proc1_Old; // Bestaat al, implementatie aanwezig.
procedure Proc2_New; // Implementatie niet aanwezig.
procedure Proc3_New; //  "
procedure Proc4_New; //  "
procedure Proc5_Old; // Bestaat al, implementatie aanwezig.

In dit geval zullen er door Code Completion 3 procedure implementaties worden toegevoegd (Proc2_New, Proc3_New, Proc4_New).

Waarom heet dit nu "Forward Procedure Completion"? Eigenlijk heel simpel, omdat het ook werkt bij procedures die met de "forward" modifier zijn gedefinieerd!

Event Assignment Completion

"Event Assignment Completion" is ook een onderdeel van de Code Completion en completeerd een

Event := |

statement. Druk je nu op Ctrl+Shift+C zoals in het volgende voorbeeld:

procedure TForm1.Form1Create(Sender: TObject);
begin
  OnPaint:=|
end;

De '|' is natuurlijk weer de plaats van de cursor! Als je nu op press Ctrl+Shift+C drukt het statement zal worden gecompleteerd tot:

OnPaint:=@Form1Paint;

In de definitie van TForm1 zal deze methode worden opgenomen en in de implementatie sectie zal het skelet van de procedure gezet worden:

procedure TForm1.Form1Paint(Sender: TObject);
begin
  |
end;

Eigenlijk precies zo als bij het toevoegen van een methode in de Object Inspector.

Let op:
De cursor moet achter de ':=' staan, omdat anders "Local Variable Completion" zal worden aangeroepen. Dit zal echter mislukken want OnPaint is immers al gedefinieerd.

Tip:
Je kunt zelf de naam van de method bepalen door bijvoorbeeld:

 OnPaint:=@ThePaintMethod;

te typen en daarna Ctrl+Shift+C te geven.

Local Variable Completion

"Local Variable Completion" is het onderdeel van Code Completion dat een lokale variabele toevoegt voor een Identifier := Waarde; statement. Je kunt het oproepen als de cursor op de identifier staat.

Bijvoorbeeld:

procedure TForm1.Form1Create(Sender: TObject);
begin
  i:=3;
end;

Plaats de cursor op de 'i' of er net achter, druk dan op Ctrl+Shift+C en dit is het resultaat:

procedure TForm1.Form1Create(Sender: TObject);
var
  i: Integer;
begin
  i:=3;
end;

Code Completion controleert eerst of de identifier 'i' al gedefinieerd is en zo niet dan zal het de declaratie 'var i: integer;' toevoegen. Het type van de identifier is gebaseerd op de toegekende waarde. Getallen worden standaard een Integer.

Een ander voorbeeld, die wat meer de ingebouwde intelligentie aantoont: type

 TWhere = (Behind, Middle, InFront);

 procedure TForm1.Form1Create(Sender: TObject);
 var
   a: array[TWhere] of char;
 begin
   for Where:=Low(a) to High(a) do writeln(a[Where]);
 end;

Plaats de cursor op 'Where' en druk op Ctrl+Shift+C. Je krijgt dan:

procedure TForm1.Form1Create(Sender: TObject);
var
  a: array[TWhere] of char;
  Where: TWhere;
begin
  for Where:=Low(a) to High(a) do writeln(a[Where]);
end;

Commentaar en Code Completion

Code Completion zal het commentaar zoveel mogelijk daar houden waar het hoort. Bij voorbeeld:

 FList: TList; // lijst van TComponent
 FInt: integer;

Als er een nieuwe variabele tussen wordt gezet, zal het commentaar op de regel van FList blijven. Het zelfde geldt voor

 FList: TList; { lijst van TComponent
   Dit is commentaar over meerdere regels, dat begint
   op de FList regel, so Code Completion neemt aan dat 
   het bij FList hoort en zal deze deze relatie niet
   verbreken. Nieuwe code wordt hierna geplaatst. }
 FInt: integer;

Maar in het volgende voorbeeld:

 FList: TList; // list of TComponent
   { Dit commentaar behoort tot het volgende statement.
     Nieuwe code kan dus hierboven worden toegevoegd en
     na het commentaar van de FList regel. }
 FInt: integer;

Refactoring

Invert Assignments

Samenvatting
: "Invert Assignments" draait geselecteerde pascal statements om. Je kunt het gebruiken voor het omvormen van "bewaar" code naar "inlees" code.

Een voorbeeld zal een en ander wat duidelijker maken:

procedure DoSomething;
begin
  AValueStudio:= BValueStudio;
  AValueAppartment :=BValueAppartment;
  AValueHouse:=BValueHouse;
end;

Selecteer de regels met toekenningen (assignments) en kies dan voor "Invert Assignments". Dit vind je door rechts te klikken in de geselecteerde code en dan te kiezen voor "Refactoring". Alle toekennen zullen nu worden omgedraaid. Het resultaat ziet er dan als volgt uit:

procedure DoSomething;
begin
  BValueStudio     := AValueStudio;
  BValueAppartment := AValueAppartment;
  BValueHouse      := AValueHouse;
end;

Extract Procedure

Samenvatting
: "Extract Procedure" maakt van de geselecteerde code een nieuwe procedure. Dit is makkelijk bij het splitsen van een hele grote procedure in een aantal kleinere of het maken van een nieuwe procedure omdat de betreffende code op meerdere plaatsen gebruikt wordt. Extract procedure kun je oproepen via Edit -> Extract Procedure of via Rechtermuisklik -> Refactoring -> Extract Procedure.

De basis:

procedure DoSomething;
begin
  Statement1;
  Statement2;
  Statement3;
end;

Selecteer de regels tussen begin en end. Kies dan voor Extract Procedure. Er zal dan een dialoog waarmee je het type procedure kunt kiezen en een naam voor de procedure kunt invoeren. Bijvoorbeeld: procedure, "NewProc". Result:

procedure NewProc;
begin
  Statement1;
  Statement2;
  Statement3;
end;

procedure DoSomething;
begin
  NewProc;
end;

Zoals je ziet is er een nieuwe procedure gemaakt "NewProc", met de geselecteerd statements en is de oorspronkelijke code vervangen door een aanroep van de nieuwe procedure. Had je voor het type "Private Method" gekozen, dan was er in de type definitie van je klasse in de private sectie een definitie van de nieuwe procedure gemaakt naast natuurlijk de implementatie van deze procedure.

Lokale Variabelen en Parameters:
"Extract Procedure" zal automatisch de code scannen voor lokale variabele en deze aanmaken. Ook eventuele parameters worden automatisch aangemaakt. Bijvoorbeeld:

procedure TForm1.DoSomething(var Ernie, Bert: integer);
var
  I: Integer; // Commentaar
begin
  Ernie:=Ernie+Bert;
  for I:=Ernie to 5 do begin
  |
  end;
end;

Selecteer nu de for-loop selecteert en maak met behulp van "Extract Procedure" een nieuwe procedure "NewProc" aan. Omdat de variabele I alleen in de geselecteerde code wordt gebruikt, wordt deze naar de nieuwe procedure verplaatst. Ernie wordt echter ook nog in de resterende code gebruikt dus zal het een parameter worden.

Het resultaat:

procedure NewProc(const Erni: integer);
var
  i: Integer; // Commentaar
begin
  for i:=Ernie to 5 do begin
  |
  end;
end;

procedure TForm1.DoSomething(var Erni, Bert: integer);
begin
  Ernie:=Ernie+Bert;
  NewProc(Ernie);
end;

Zoals je ziet is de I in zijn geheel verplaatst naar de nieuwe procedure inclusief het bijbehorende commentaar.

Beperkingen:
Omdat pascal een hoop mogelijkheden heeft, moet je niet verwachten dat het altijd even goed werkt. Huidige beperkingen en "ToDo"s:

  • Controle op het overeenkomen van selectie grenzen met statement grenzen
  • betere bepaling van het type van de parameters. Op het moment zijn parameters "const", als het "var" moeten zijn, moet je dat zelf wijzigen.
  • "with" statements

Find Declaration

Find Declaration kun je oproepen via Search -> Find Declaration at cursor of via Rechtermuisklik -> Find Declaration of via ctrl+muisklik. (In het laatste geval verschijnt er een lijn onder een identifier als de ctrl-toets is ingedrukt en de muiscursor komt boven de identifier. Hoe dan ook, de IDE gaat op zoek naar de declaratie van deze identifier, opent (indien nodig) de file en plaats de cursor op de regel waar de identifier is gedefinieerd.

Iedere zoek opdracht (Find Declaration) zorgt er voor dat er een Jump Point wordt gezet. Je kunt dus eenvoudig terug springen naar het punt waar je de zoek aktie heb gestart via Search -> Jump back (ctrl+H).

De codetools houden rekening met de "normale" pascal regels. Hoewel de compiler alleen het laatste resultaat geeft, zien de codetools alle tussenliggende stappen. Bijvoorbeeld:

De 'Visible' property wordt voor het eerst gedefinieerd in TControl (controls.pp), later nog eens opnieuw in TCustomForm en uiteindelijk nog eens in TForm. Gebruik je dus Find Declaration op de Visible property van een Form, dan zal er eerst naar de definitie van TForm gesprongen worden, vandaar kun je dan springen naar de declaratie van TCustomForm en dan pas naar het visible property van TControl.

Iets soort gelijks geldt bijvoorbeeld voor TColor. Uiteindelijk is TColor niets anders dan een 'longint'. Maar in de sources is het als volgt gedefinieerd:

TGraphicsColor = -$7FFFFFFF-1..$7FFFFFFF;
TColor = TGraphicsColor;

Een voorbeeld van een 'forward' gedefinieerd control. In TControl is een private variabele gedefinieerd:

FHostDockSite: TWinControl;

Gebruik je in TControl Find declaration voor TWinControl, dan zal er naar de forward definitie gesprongen worden.

TWinControl = class;

Gebruik je hier dan nog eens de Find Declaratie, pas dan zal er naar de werkelijke definitie gesprongen worden:

TWinControl = class(TControl)

Het mag duidelijk zijn dat je op deze manier iedere identifier kunt opsporen en iedere overload terugvinden.

Goto Include Directive

"Goto Include Directive" te vinden in het search menu van de IDE springt naar het {$I filename} statement waar het huidige bestand wordt gebruikt.

Publish Project

Maakt een kopie van je hele project. Als je iemand de code van je project wilt sturen is dit een handige functie. Ook voor het maken van een backup van je sources is dit natuurlijk uitermate geschikt.

In de project directory staat een helehoop informatie. Het meeste daarvan is niet strikt noodzakelijk voor het project, maar maken dat je als je het project opnieuw opent precies op dat punt bent waar je was bij het sluiten ervan. De .lpi file bevat deze sessie informatie (zoals de positie van de tekst cursor en bookmarks van gesloten units). Daarnaast staan er in de project directory een aantal .ppu en .o bestanden en natuurlijk de executable zelf. De .ppu en .o zijn tussenbestanden die gemaakt worden tijdens het compileren van je programma. Publish Project maakt dus een lpi file met de hoogstnodige informatie en plaatst deze met alle source files in een aparte subdirectory.

In de dialoog die verschijnt als je deze functie start kun je een filter opgeven van de bestanden die meegenomen moeten worden of juist niet. Met het "command after" zou je de bestanden eventueel direct in een zip of soortegelijk archief kunnen zetten.