Lazarus IDE Tools/nl
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:
- Class Completion: completes properties, adds method bodies, add private variables and private access methods
- Forward Procedure Completion: adds procedure bodies
- Event Assignment Completion: completes event assignments and adds method definition and body
- Local Variable Completion: adds local variable definitions
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 part of the Code Completion and adds a local variable definition for a Identifier:=Term; statement. It is invoked, when the cursor is on the identifier of the assignment.
For example:
procedure TForm1.Form1Create(Sender: TObject); begin i:=3; end;
Place the cursor on the 'i' or just behind it. Then press Ctrl+Shift+C for code completion and you will get:
procedure TForm1.Form1Create(Sender: TObject); var i: Integer; begin i:=3; end;
The codetools first checks, if the identifier 'i' is already defined and if not it will add the declaration 'var i: integer;'. The type of the identifier is guessed from the term right to the assignment ':=' operator. Numbers like the 3 defaults to Integer.
Another example: 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;
Place the cursor on 'Where' and press Ctrl+Shift+C for code completion. You get:
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;
Comments and Code Completion
Code completion tries to keep comments where they belong. For example:
FList: TList; // list of TComponent FInt: integer;
When inserting a new variable between FList and FInt, the comment is kept in the FList line. Same is true for
FList: TList; { list of TComponent This is a comment over several lines, starting in the FList line, so codetools assumes it belongs to the FLIst line and will not break this relationship. Code is inserted behind the comment. } FInt: integer;
If the comment starts in the next line:
FList: TList; // list of TComponent { This comment belongs to the statement below. New code is inserted above this comment and behind the comment of the FList line. } FInt: integer;
Refactoring
Invert Assignments
- Abstract
- : "Invert Assignments" takes some selected pascal statements and inverts all assignments from this code. This tool is usefull for transforming a "save" code to a "load" one and inverse operation.
Example:
procedure DoSomething; begin AValueStudio:= BValueStudio; AValueAppartment :=BValueAppartment; AValueHouse:=BValueHouse; end;
Select the lines with assignments (between begin and end) and do Invert Assignments. All assignments will be inverted and identation will be add automatically. For example:
Result:
procedure DoSomething; begin BValueStudio := AValueStudio; BValueAppartment := AValueAppartment; BValueHouse := AValueHouse; end;
Extract Procedure
- Abstract
- : "Export Procedure" takes some selected pascal statements and creates a new procedure/method from this code. This tool is useful to split big procedures or to easily create a new procedure from some code.
Basic example:
procedure DoSomething; begin CallSomething; end;
Select the line "CallSomething;" and do Extract Proc. A dialog pop ups and you can select the type and name of the procedure to create. For example: procedure, "NewProc". Result:
procedure NewProc; begin CallSomething; end; procedure DoSomething; begin NewProc; end;
You can see, that the new procedure "NewProc" was created with the selection as body and the old code was replaced by a call.
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.