Difference between revisions of "Codetools/de"

From Lazarus wiki
Jump to navigationJump to search
Line 163: Line 163:
 
Tipp: Um eine Unit einfach zu durchlaufen und die Knoten zu erstellen, verwenden Sie '''CodeToolBoss.Explore'''.
 
Tipp: Um eine Unit einfach zu durchlaufen und die Knoten zu erstellen, verwenden Sie '''CodeToolBoss.Explore'''.
  
=CleanPos and CursorPos=
+
=Clean Position und Kursorposition=
  
There are several methods to define a position in the codetools.
+
Es gibt verschiedene Methoden, um in den CodeTools eine Position zu definieren.
  
Absolute position are related to a source as string and starts at 1. For example a TCodeBuffer holds the file content as one string in the property Source.
+
Die absolute Position bezieht sich auf den Quelltext als Zeichenkette und beginnt bei 1. Beispielsweise hält ein TCodeBuffer den Dateiinhalt als Zeichenkette in der Eigenschaft 'Source'.
Caret or cursor positions are given as X,Y, where Y is the line number starting at 1 and X is the column number starting at 1. A TCodeBuffer provides the member functions '''LineColToPosition''' and '''AbsoluteToLineCol'' to convert.
+
Die Positionen der Einfügemarke oder des Kursors sind als X,Y gegeben, wobei Y die Zeilennummer ist (bei 1 beginnend) und X die Spaltennummer (bei 1 beginnend). Ein TCodeBuffer stellt zum Umwandeln die Funktionen '''LineColToPosition''' und '''AbsoluteToLineCol'' zur Verfügung.
When working with multiple source files, like a unit, that can consists of several include files, the clean position relates to the absolute position in the stripped code '''Src'''. Src which is a string and clean positions start at 1. Cursor positions are specified as TCodeXYPosition (Code,X,Y). A TCodeTool provides the functions '''CaretToCleanPos''', '''CleanPosToCaret''' to convert.
+
Wenn Sie mit mehreren Quelltextdateinen arbeiten, wie einer Unit, die aus verschiedenen Includedateien bestehen kann, dann bezieht sich 'Clean Position' auf die absolute Position im bereinigten Code '''Src'''. Src ist eine Zeichenkette und Clean Positions beginnt bei 1. Die Kursorpositionen werden angegeben als TCodeXYPosition (Code,X,Y). Ein TCodeTool stellt zum Umwandeln die Funktionen '''CaretToCleanPos''', '''CleanPosToCaret''' zur Verfügung.
  
 
=Inserting, deleting, replacing - the TSourceChangeCache=
 
=Inserting, deleting, replacing - the TSourceChangeCache=

Revision as of 15:09, 21 April 2011

Deutsch (de) English (en) français (fr) русский (ru)

Was sind die CodeTools

Die CodeTools sind ein Lazarus-Package mit Werkzeugen zum Parsen, Untersuchen, Bearbeiten und Refaktorieren von Pascal-Quelltexten. Die CodeTools sind ein Modul für sich und unter der GPL lizensiert. Zahlreiche Beispiele, wie Sie die CodeTools in Ihren eigenen Programmen einsetzen können, finden Sie unter 'components/codetools/examples'.

svn:

Verwendung der CodeTools ohne die IDE (gut für Testzwecke)

Sie können die CodeTools ohne die IDE verwenden. Dies kann zum Testen eines neuen Werkzeugs genutzt werden. Ein einfaches Beispiel ist

 <lazarusdir>/components/codetools/examples/methodjumping.lpi

Um die find declaration zu testen, müssen die CodeTools die Quellen analysieren, besonders die RTL- und FCL-Quellen. Die Beispiele benutzen folgende Umgebungsvariablen:

  • FPCDIR: der Pfad zu den FPC-Quelltexten, die Vorgabe ist ~/freepascal/fpc.
  • PP: der Pfad zum ausführbaren Kompiler (/usr/bin/fpc oder /usr/bin/ppc386 oder C:\lazarus\ppc386.exe). Die CodeTools müssen den Kompiler nach den Einstellungen fragen. Als Vorgabe wird mittels der Variablen 'PATH' nach 'fpc' gesucht.
  • FPCTARGETOS: weist die CodeTools an, nach einem weiteren Betriebssystem zu suchen ("cross compiling"). Zum Beispiel: linux, freebsd, darwin, win32, win64, wince
  • FPCTARGETCPU: sucht nach einer anderen CPU. Zum Beispiel: i386, powerpc, x86_64, arm, sparc
  • LAZARUSDIR: der Pfad zu den Lazarus-Quelltexten. Wird nur benötigt, wenn Sie diese untersuchen wollen.

FPC ist ein sehr komplexes Projekt mit vielen Suchpfaden, Include-Dateien und Makros. Die CodeTools müssen alle diese Pfade und Makros kennen, um diesen Dschungel zu analysieren. Um dies alles einfach einzurichten, enthalten die CodeTools vordefinierte Schablonen für FPC-, Lazarus-, Delphi- und Kylix-Quellverzeichnisse. Betrachten Sie für ein find declaration folgendes Beispiel

 <lazarusdir>/components/codetools/examples/finddeclaration.lpi

Weil die FPC-Quellen mehrere Versionen einiger Units enthalten und die FPC-Quellen oft geändert werden, verwenden die CodeTools keine starre Pfadtabelle. Stattdessen scannen sie zuerst die gesamte FPC Verzeichnisstruktur und versuchen mittels einiger Regeln herauszufinden, welche Quelle die richtige ist für das gegenwärtige ZielOS und ZielCPU. Dieser Scan kann eine Weile dauern. Alle Beipiele speichern das Ergebnis in codetools.config, damit diese Suche beim nächsten Start übersprungen wird.

Wann immer die FPC-Quellen verschoben wurden oder eine Unit umbenannt wurde, löschen Sie einfach die Datei codetools.config. Die Lazarus-IDE hat ihre eigene Konfigurationsdatei und erledigt den Rescan, wann immer das Compiler executable geändert wurde oder der Benutzer dies erzwingt mit 'Einstellungen > FPC-Quelltextverzeichnis neu einlesen'.

Verwendung der CodeTools in der IDE mit dem IDEIntf

Siehe das <lazarusdir>/examples/idequickfix/quickfixexample.lpk Package. Es demonstriert:

  • Wie man ein IDE-Package schreibt. Wenn Sie es installieren, wird es ein Quick-Fix-Element registrieren.
  • Wie man ein Quick-Fix-Element schreibt für die Compilermeldung 'Parameter "Sender" not used'
  • Wie man die CodeTools verwendet, um
    • eine Unit zu analysieren
    • Dateiname, Zeile, Spalte in eine CodeTools Quelltextposition umzuwandeln
    • ein CodeTools-Element an der Cursorposition zu finden
    • ein 'procedure'-Element und das 'begin..end'-Element zu finden
    • für eine Anweisung eine günstige Einfügeposition am Anfang eines 'begin..end'-Blocks zu ermitteln
    • die Einrückung einer Zeile zu erhalten, damit sich die neue Zeile in die Prozedur einfügt
    • Code einzufügen mit den CodeTools

Codetools-Regeln für FPC-Quelltexte

Wenn die CodeTools die Quelltexte nach einer FPC ppu-Datei durchsuchen, verwenden sie einen Satz von Regeln. Sie können Ihre eigenen Regeln Schreiben, aber normalerweise werden Sie die Standardregeln benutzen, die in der der Includedatei 'components/codetools/fpcsrcrules.inc' festgelegt sind. Sie können die Regeln testen mit dem Befehlszeilenwerkzeug: components/codetools/examples/testfpcsrcunitrules.lpi.

Gebrauch von testfpcsrcunitrules

Gebrauch: lazarus/components/codetools/examples/testfpcsrcunitrules -h

  -c <compiler file name>, --compiler=<compiler file name>
         Vorgabe ist es, die Umgebungsvariable PP zu benutzen.
         Wenn nicht angegeben, suche nach fpc

  -T <target OS>, --targetos=<target OS>
         Vorgabe ist es, die Umgebungsvariable FPCTARGET zu benutzen.
         Wenn nicht angegeben, nimm die Vorgabe des Kompilers.

  -P <target CPU>, --targetcpu=<target CPU>
         Vorgabe ist es, die Umgebungsvariable FPCTARGETCPU zu benutzen.
         Wenn nicht angegeben, nimm die Vorgabe des Kompilers.

  -F <FPC source directory>, --fpcsrcdir=<FPC source directory>
         Vorgabe ist es, die Umgebungsvariable FPCDIR zu benutzen.
         Es gibt keine andere Vorgabe.

  -u <unit name>, --checkunit=<unit name>
         Schreibe einen detailierten Bericht über diese Unit.

Beispiel für testfpcsrcunitrules

Öffnen Sie die Datei 'testfpcsrcunitrules.lpi' in der IDE und kompilieren Sie sie. Starten Sie dann das Werkzeug in einer Befehlszeile/Konsole:

./testfpcsrcunitrules -F ~/fpc/sources/2.5.1/fpc/

Dadurch erfahren Sie, welcher Kompiler benutzt wird, welcher Kompiler ausgeführt wird, welche Konfigurationsdateinen getestet und analysiert wurden, Sie werden gewarnt über doppelte Units im FPC-Suchpfad und doppelte Quelltextdateien für die selbe Unit.

Doppelte Quelltextdateien

Angenommen, Sie finden heraus, dass die CodeTools für das Ziel 'wince/arm' den falschen Quelltext der Unit mmsystem öffnen. Starten Sie das Werkzeug mit dem Parameter '-u':

./testfpcsrcunitrules -F ~/fpc/2.5.1/fpc/ -T wince -P arm -u mmsystem

Dadurch erhalten Sie einen detaillierten Bericht darüber, wo diese Unit gefunden wurde und welches Score jede Quelltextdatei erzielt hat. Zum Beispiel:

Unit report for mmsystem
  WARNING: mmsystem is not in PPU search path
GatherUnitsInFPCSources UnitName=mmsystem File=packages/winunits-base/src/mmsystem.pp Score=11
GatherUnitsInFPCSources UnitName=mmsystem File=packages/winceunits/src/mmsystem.pp Score=11 => duplicate

Dies bedeutet, dass es zwei Quelltextdateien mit dem selben Score gibt, sodass die CodeTools die erste genommen haben. Die letzte im Verzeichnis 'winceunits' ist für das Ziel 'wince' und die erste ist für 'win32' und 'win64'.

Öffnen Sie jetzt die Regeldatei 'fpcsrcrules.inc'.

Regeln arbeiten so: <Delphi> Score:=10; Targets:='wince'; Add('packages/winceunits'); </Delphi>

Das Add fügt eine Regel hinzu für alle Dateien, die mit 'packages/winceunits' beginnen. Zum Score all dieser Dateien wird 10 addiert. Targets ist eine durch Kommas getrennte Liste von Zielbetriebssystemen und/oder Zielprozessoren. Beispielsweise: Targets='wince,linux,i386' bedeutet: wende diese Regel auf das ZielOS 'wince' oder 'linux' und auf die ZielCPU i386 an.

Wie die CodeTools Quelltexte untersuchen, abhängig von einem Kompiler

Ein Kompiler ist dazu optimiert den Code linear zu durchlaufen und die benötigten Units und Includedateien dann zu laden, wenn er einen uses-Abschnitt oder eine Direktive findet. Die CodeTools sind dazu optimiert, nur Teile des Codes zu untersuchen. Beispielsweise braucht der Sprung von einer Methodendeklaration zum Methodenrumpf nur die Unit und ihre Includedateien. Wenn die CodeTools eine Deklaration suchen, dann suchen sie rückwärts. Das bedeutet, sie starten die Suche in den lokalen Variablen und dann oberhalb in der Implementation. Wenn sie einen uses-Abschnitt vorfinden, suchen sie den Bezeichner im Abschnitt 'interface' der Units. Wird der Bezeichner gefunden, halten sie an. Das Ergebnis und einige Zwischenschritte werden gecached. Weil oftmals nur einige interface-Abschnitte durchsucht werden müssen, wird ein einzelner Bezeichner rasch gefunden.

Die CodeTools durchsuchen einen Quelltext nicht in einem Durchgang wie ein Kompiler, sondern in mehreren Durchgängen, abhängig von den Erfordernissen der aktuellen Funktion:

  • Zuerst wird ein Quelltext in einen TCodeBuffer geladen. Die IDE benutzt diesen Schritt, um die Kodierung in UTF8 zu ändern. Die Dateien werden im Speicher gehalten und nur nachgeladen, wenn sich das Änderungsdatum ändert oder eine Datei manuell umgeändert wird. Es gibt einige Tools und Funktionen die direkt mit dem Puffer arbeiten.
  • Die nächste Ebene ist das Untersuchen einer Unit oder Includedatei. Eine Unit muss vom Anfang an durchlaufen werden, deshalb versuchen die CodeTools die Hauptdatei herauszufinden, die erste Datei einer Unit. Dabei suchen sie in der ersten Zeile nach einer Direktive, wie {%MainUnit ../lclintf.pp}. Falls diese nicht existiert, wird der 'includelink cache' durchsucht. Die IDE speichert diesen Cache auf dem Laufwerk, so lernt die IDE mit der Zeit dazu.
  • Nachdem die Hauptdatei gefunden wurde, scannt TLinkScanner den Quelltext. Er verarbeitet Kompilerdirektiven, wie Include-Direktiven und 'if-else'-Direktiven. Dem Scanner kann ein Bereich übergeben werden, beispielsweise um nur das Interface einer Unit zu durchsuchen. Der Scanner erzeugt den clean source. Der 'clean source' besteht aus allen Includedateien und befreit von sämtlichen übersprungenen Codestellen aus den 'else'-Teilen. Er erzeugt auch eine Liste von links, die den 'clean source' und die realen Quelltextdatein verbinden. Der 'clean source' ist jetzt Pascal. Beachten Sie: es gibt auch Tools, um eine einzelne Quelle nach allen Direktiven zu durchsuchen und einen Direktivenbaum zu erstellen.
  • Nach dem Erzeugen des 'clean source' untersucht ein TCodeTool diesen und erstellt einen Baum von 'TCodeTreeNode'-Elementen. Auch hier ist eine Bereichsangabe möglich. Dieser Parser überspringt einige Teile, zum Beispiel 'class members', 'begin..end'-Blocks und Parameterlisten. Viele Tools brauchen diese nicht. Diese Unterknoten werden nur bei Bedarf erzeugt. Ein TCodeTreeNode hat einen Bereich StartPos..EndPos, welche 'clean positions' sind, also Positionen im 'clean source'. Es gibt nur Knoten für die wichtigen Teile. Knoten für jedes Detail zu erstellen würde mehr Speicher benötigen als der Quelltext selbst und ist auch selten erforderlich. Zahlreiche Funktionen sind dazu da, die Details herauszufinden. Ob beispielsweise eine Funktion der Aufrufskonvention 'cdecl' entspricht.
  • Wird ein Bezeichner gesucht, dann speichert die Suche die gefundenen Basistypen und erzeugt Caches für alle Bezeichner aus dem Interface-Abschnitt.

Jede Ebene hat ihre eigenen Caches, die vor dem Aufruf einer Funktion geprüft und aktualisiert werden müssen. Viele High-level-Funktionen, auf die mittels 'CodeToolBoss' zugegriffen werden kann, machen das automatisch. Für andere liegt die Verantwortung beim aufrufenden Prozess.

Ein Beispiel dafür:

unti1.pas:

<Delphi> unit Unit1; {$I settings.inc} interface uses

 {$IFDEF Flag}
 unix,
 {$ELSE}
 windows,
 {$ENDIF}
 Classes;

</Delphi>

settings.inc:

<Delphi> {%MainUnit unit1.pas} {$DEFINE Flag} </Delphi>

clean source:

<Delphi> unit Unit1; {$I settings.inc}{%MainUnit unit1.pas} {$DEFINE Flag} interface uses

 {$IFDEF Flag}
 unix,
 {$ELSE}{$ENDIF}
 Classes;

</Delphi>


Tipp: Um eine Unit einfach zu durchlaufen und die Knoten zu erstellen, verwenden Sie CodeToolBoss.Explore.

Clean Position und Kursorposition

Es gibt verschiedene Methoden, um in den CodeTools eine Position zu definieren.

Die absolute Position bezieht sich auf den Quelltext als Zeichenkette und beginnt bei 1. Beispielsweise hält ein TCodeBuffer den Dateiinhalt als Zeichenkette in der Eigenschaft 'Source'. Die Positionen der Einfügemarke oder des Kursors sind als X,Y gegeben, wobei Y die Zeilennummer ist (bei 1 beginnend) und X die Spaltennummer (bei 1 beginnend). Ein TCodeBuffer stellt zum Umwandeln die Funktionen LineColToPosition' und AbsoluteToLineCol zur Verfügung. Wenn Sie mit mehreren Quelltextdateinen arbeiten, wie einer Unit, die aus verschiedenen Includedateien bestehen kann, dann bezieht sich 'Clean Position' auf die absolute Position im bereinigten Code Src. Src ist eine Zeichenkette und Clean Positions beginnt bei 1. Die Kursorpositionen werden angegeben als TCodeXYPosition (Code,X,Y). Ein TCodeTool stellt zum Umwandeln die Funktionen CaretToCleanPos, CleanPosToCaret zur Verfügung.

Inserting, deleting, replacing - the TSourceChangeCache

When making changes to the source code of a unit (or its include files) you should use the CodetoolBoss.SourceChangeCache, because:

  • Simple usage. Connect, Replace, Replace, ... Apply. See below.
  • You can use cleanpos as given by the node tree OR you can use direct position in a file.
  • You can use Replace to insert, delete, which automatically calls events, so connected editors are notified of changes.
  • It can automatically insert needed spaces, line breaks or empty lines in front or behind each Replace. For example you define that there should be an empty line in front. The SourceChangeCache checks what is inserted and how much space there is already and will insert needed space.
  • It checks if the replaced/deleted span is writable.
  • You can do multiple Replaces and you control when they are applied. Keep in mind that inserting code means that the parsed tree becomes invalid and needs rebuilding.
  • Multiple replaces are checked for intersection. For example an insert in the middle of deleted code gives an error.
  • Mutiple insertions at the same place are added FIFO - first at the top.
  • You can combine several functions altering code to one bigger function. See below.

Usage

The SourceChangeCache works on a unit, so you need to get a TCodeTool and scan a unit/include file. For example:

<Delphi>

 // Step 1: load the file and parse it
 Code:=CodeToolBoss.LoadFile(Filename,false,false);
 if Code=nil then
   raise Exception.Create('loading failed '+Filename);
 if not CodeToolBoss.Explore(Code,Tool,false) then
   ...;// parse error ...
 // Step 2: connect the SourceChangeCache
 CodeToolBoss.SourceChangeCache.MainScanner:=Tool.Scanner;
 // Step 3: use Replace to insert and/or delete code
 // The first two parameters are the needed spaces in front and behind the insertion
 // The FromPos,ToPos defines the deleted/replaced range in CleanPos positions.
 // The NewCode is the string of new code. Use  for a delete.
 if not CodeToolBoss.SourceChangeCache.Replace(gtNone,gtNone,FromPos,ToPos,NewCode) then
   exit; // e.g. source read only or a former Replace has deleted the place
 ...do some more Replace...
 // Step 4: Apply the changes
 if not CodeToolBoss.SourceChangeCache.Apply then
   exit; // apply was aborted

</Delphi>

BeginUpdate/EndUpdate

BeginUpdate/EndUpdate delays the Apply. This is useful when combining several code changing functions. For example:

You want to scan the unit, add a unit to the interface uses section, and remove the unit from the implementation uses section. The two functions AddUnitToMainUsesSection and RemoveUnitFromUsesSection use Apply, altering the source, so the second function would rescan the unit a second time. But since the two functions are independent of each other (they change different parts of the source) you can combine them and do it with one scan:

<Delphi>

 // Step 1: parse unit and connect SourceChangeCache
 if not CodeToolBoss.Explore(Code,Tool,false) then
   ...;// parse error ...
 CodeToolBoss.SourceChangeCache.MainScanner:=Tool.Scanner;
 // Step 2: delay Apply
 CodeToolBoss.SourceChangeCache.BeginUpdate;
 // Step 3: add unit to interface section
 // AddUnitToMainUsesSection would apply and change the code
 // Because of the BeginUpdate the change is not yet done, but stored in the SourceChangeCache
 if not Tool.AddUnitToMainUsesSection('Classes',,CodeToolBoss.SourceChangeCache) then exit;
 // Step 4: remove unit from implementation section
 // Without the BeginUpdate the RemoveUnitFromUsesSection would rescan the unit
 if Tool.FindImplementationUsesSection<>nil then
   if not Tool.RemoveUnitFromUsesSection(Tool.FindImplementationUsesSection,'Classes',CodeToolBoss.SourceChangeCache) then exit;
 // Step 5: apply all changes
 if not CodeToolBoss.SourceChangeCache.EndUpdate then
   exit; // apply was aborted

</Delphi>

BeginUpdate/EndUpdate work with a counter, so if you call BeginUpdate twice you need to call EndUpdate twice. This means you can put the above example in a function and combine that with another function.

Saving changes to disk

The above changes are made to the code buffers and the buffers are marked modified. To save the changes to disk, you have to call Save for each modified buffer.

  • The buffers that will be modified in the next Apply/EndUpdate are in SourceChangeCache.BuffersToModify and BuffersToModifyCount.
  • The events SourceChangeCache.OnBeforeApplyChanges/OnAfterApplyChanges are used by the CodeToolBoss, which connects it to its own OnBeforeApplyChanges/OnAfterApplyChanges. The Lazarus IDE sets these events and automatically opens modified files in the source editor, so all changes go into the undo list of synedit.