Lazarus IDE Tools/nl

From Lazarus wiki
Jump to navigationJump to search

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 si 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.

Basis voorbeeld:

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

Selecteer de regels tussen begin en end. Kies dan voor Extract Procedure (Rechtermuisklik, Refactoring, 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.

Local Variables and Parameters:
"Extract Proc" scans for used variables and automatically creates the parameter list and local variables. Example:

procedure TForm1.DoSomething(var Erni, Bert: integer);
var
  i: Integer; // Comment
begin
  Erni:=Erni+Bert;
  for i:=Erni to 5 do begin
  |
  end;
end;

Select the for loop and create a new Procedure "NewProc". The local variable i is only used in the selection, so it will be moved to the new procedure. Erni is also used in the remaining code, so it will become a parameter.

Result:

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

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

You can see "i" was moved to the new procedure (Note: including its comment) and Erni.

Limitations:
Pascal is a very powerful language, so don't expect it will work with every code. Current limits/ToDos:

  • check if selection bounds on statement bounds
  • heuristic for parameter specifiers 'var'. At the moment all parameters are "const". If you need "var", you have to edit it manually.
  • "with" statements

Find Declaration

Position the cursor on an identifier and do 'Find Declaration'. Then it will search the declaration of this identifier, open the file and jump to it.

Every find declaration sets a Jump Point. That means you jump with find declaration to the declaration and easily jump back with Search -> Jump back.

There are some differences to Delphi: The codetools work on sources following the normal pascal rules, instead of using the compiler output. The compiler returns the final type. The codetools see the sources and all steps in between. For example:

The 'Visible' property is first defined in TControl (controls.pp), then redefined in TCustomForm and finally redefined in TForm. Invoking find declaration on Visible will you first bring to Visible in TForm. Then you can invoke Find Declaration again to jump to Visible in TCustomForm and again to jump to Visible in TControl.

Same is true for types like TColor. For the compiler it is simply a 'longint'. But in the sources it is defined as

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

And the same for forward defined classes: For instance in TControl, there is a private variable

FHostDockSite: TWinControl;

Find declaration on TWinControl jumps to the forward definition

TWinControl = class;

And invoking it again jumps to the real implementation

TWinControl = class(TControl)

This way you can track down every identifier and find every overload.

Goto Include Directive

"Goto Include Directive" in the search menu of the IDE jumps to {$I filename} statement where the current include file is used.

Publish Project

Creates a copy of the whole project. If you want to send someone just the sources and compiler settings of your code, this function is your friend.

A normal project directory contains a lot of information. Most of it is not needed to be published: The .lpi file contains session information (like caret position and bookmarks of closed units) and the project directory contains a lot of .ppu, .o files and the executable. To create a lpi file with only the base information and only the sources, along with all sub directories use "Publish Project".

In the dialog you can setup the exclude and include filter, and with the command after you can compress the output into one archive.