Programowanie w języku Delphi » Delphi 2005. Kompendium programisty

Rozdział 6. Praca z kodem źródłowym

Pomimo iż wspominałem i opisałem już częściowo IDE oraz język Delphi, a także platformę .NET, nie mówiłem jeszcze o pracy z kodem źródłowym. Delphi udostępnia wiele narzędzi przydatnych w trakcie pisania kodu i omówię je w tym rozdziale.

W rozdziale 7. zajmiemy się programowaniem obiektowym, lecz już teraz chciałbym nakreślić tematykę klas oraz obiektów. Zajmiemy się także analizą kodu źródłowego oraz popracujemy z formularzami Delphi.

Spis treści

          1 Na czym polega programowanie obiektowe?
          2 Biblioteki wizualne
          3 Podstawowy kod formularza
          4 Formularz w VCL.NET
               4.1 Sekcja uses
               4.2 Klasa
               4.3 Zmienna wskazująca na klasę
               4.4 Plik *.nfm
               4.5 Generowanie kodu
               4.6 Nazwy komponentów
          5 Programowanie zdarzeniowe
               5.1 Generowanie zdarzeń
                    5.1.1 Przykładowy program
                         5.1.1.1 Rozwiązanie
                         5.1.1.2 Zmiany kosmetyczne
               5.2 Lista zdarzeń w inspektorze obiektów
               5.3 Jedno zdarzenie dla kilku obiektów
                    5.3.1 Generowanie pozostałych zdarzeń
          6 Edytor kodu
               6.1 Ukrywanie kodu
               6.2 Makra
               6.3 Code Insight
               6.4 Code Completion
               6.5 Help Insight
               6.6 Wyróżnianie błędów
               6.7 Menu podręczne edytora kodu
               6.8 Opcje edytora kodu
                    6.8.1 Editor Options
                    6.8.2 Source Options
                    6.8.3 Color
                    6.8.4 Display
                    6.8.5 Key Mappings
                    6.8.6 Code Insight
               6.9 Skrawki kodu
                    6.9.1 Dodawanie pozycji do okna Code Snippets
          7 To-Do List
               7.1 Generowanie komentarza TODO
               7.2 Menu podręczne okna
          8 Szablony kodu
          9 Refaktoryzacja
               9.1 Deklaracja zmiennych
               9.2 Deklaracja pól
               9.3 Wyodrębnianie metod
               9.4 Wyodrębnianie łańcucha znaków
               9.5 Zmiana nazwy
               9.6 Szukanie modułu
          10 Synchroniczna edycja
          11 Wyszukiwanie odwołań
          12 Menedżer projektu
               12.1 Model View
               12.2 Data Explorer
          13 Menedżer historii
          14 Test
          15 FAQ
          16 Podsumowanie


W tym rozdziale:

  • zdefiniuję pojęcie programowania obiektowego,
  • powiem, jak generować zdarzenia dla komponentów,
  • przedstawię opcje edytora kodu,
  • opowiem, czym jest Code Completion oraz Code Insight,
  • omówię wykorzystywanie mechanizmu refaktoryzacji.

Na czym polega programowanie obiektowe?


Programy rozrastają się coraz bardziej i bardziej. Tak samo jak kiedyś nie wystarczała idea programowania proceduralnego, teraz nie wystarcza już programowanie strukturalne.

Koncepcja programowania obiektowego pojawiła się już w latach 60. za sprawą języka Simula 67, zaprojektowanego przez naukowców z Oslo w celu przeprowadzania symulacji zachowania się statków. Jednakże idea programowania obiektowego swoją popularyzację zawdzięcza językowi SmallTalk. Połowa lat 80. to czas, kiedy programowanie obiektowe stało się dominującą techniką — głównie za sprawą C++. Wtedy to też w wielu innych językach pojawiła się możliwość tworzenia obiektów.

Można powiedzieć, że klasa jest rodzajem zbioru, pewnym elementem programu, który wykonuje jakieś. Klasa zawiera metody (procedury i funkcje), współdziałające ze sobą w celu wykonania jakiegoś zadania. Programowanie obiektowe przyczyniło się do tego, że takie moduły jak klasy mogą być wykorzystywane w wielu innych projektach — ułatwia to jeszcze bardziej zarządzanie i konserwację kodu.

Załóżmy, że napisano klasę do obsługi poczty (wysyłanie i odbieranie). Klasa może zawierać procedury Connect (połącz), SendMail (wyślij e-mail), Disconnect (rozłącz). Z kolei procedura Connect może wywoływać inną, np. Error (która też jest procedurą znajdującą się w klasie), wyświetlającą komunikat o błędzie w razie niepowodzenia i zapisującą odpowiednią informację w dzienniku programu (czyli, inaczej mówiąc, w logach — plikach z rozszerzeniem *.log). Teraz taką klasę można wykorzystać w wielu swoich aplikacjach — wystarczy skopiować fragment kodu i już gotowa jest obsługa błędów, łączenie itp. Taką klasę można udostępnić innym użytkownikom lub swoim współpracownikom. Inny użytkownik nie musi wiedzieć, jak działa klasa — dla niego jest ważne jej działanie (np. wysyłanie e-maili). Użytkownik musi jedynie wiedzieć, że istnieje metoda Connect, która połączy go z danym serwerem oraz musi mieć świadomość obecności kilku innych procedur. To wszystko — nie interesuje go obsługa błędów — nie musi nawet zdawać sobie sprawy z jej istnienia.

Można by oczywiście utworzyć nowy moduł, a w nim umieścić także procedury Connect, SendMail oraz Disconnect, Error i resztę potrzebnego kodu. Jednak w takim przypadku metody i zmienne (zmienne także mogą być elementami danej klasy) nie oddziałują na siebie w takim stopniu. Przykładowo, użytkownik korzystający z takiego kodu będzie miał dostęp do tych zmiennych, do których nie powinien. Będzie mógł też wywołać swobodnie procedurę Error — a nie powinien, bo może to spowodować niepożądane skutki. Dzięki klasom można sprawić, iż taka procedura Error nie będzie dostępna poza klasą. Jej elementy (zmienne) też będą nie do odczytania przez przyszłego użytkownika.

Tworzenie własnych klas pokażę w rozdziale 7.

Biblioteki wizualne


W rzeczywistości takie biblioteki jak VCL czy VCL.NET (ang. Visual Component Library) stanowią szereg modułów i klas współpracujących ze sobą. Dzięki idei programowania obiektowego to wszystko działa bardzo sprawnie i jest nad wyraz czytelne. Dzieje się tak głównie dzięki mechanizmowi dziedziczenia, która pozwala na rozszerzenie funkcjonalności klas. Klasa potomna może dziedziczyć po klasie bazowej, przejmując od niej całą funkcjonalność, ale jednocześnie można rozszerzyć ją o nowe funkcje. Tak właśnie zbudowana jest biblioteka VCL — w oparciu o dziedziczenie szeregu klas.

Podstawowy kod formularza


W poprzednich rozdziałach zajmowaliśmy się głównie Windows Forms, ale od strony wizualnej, czyli nasza praca polegała głównie na wykorzystywaniu projektanta formularzy. Nie wgłębialiśmy się w szczegóły takie jak kod źródłowy formularza. Po otwarciu nowego projektu WinForms na pierwszym planie pojawi się czysty formularz. Za pomocą klawisza F12 przejdźmy do edytora kodu. Podstawowy kod modułu źródłowego został przedstawiony na listingu 6.1.

Listing 6.1. Podstawowy kod modułu WinForms
unit WinForm2;
 
interface
 
uses
  System.Drawing, System.Collections, System.ComponentModel,
  System.Windows.Forms, System.Data;
 
type
  TWinForm2 = class(System.Windows.Forms.Form)
  {$REGION 'Designer Managed Code'}
  strict private
    /// <summary>
    /// Required designer variable.
    /// </summary>
    Components: System.ComponentModel.Container;
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    procedure InitializeComponent;
  {$ENDREGION}
  strict protected
    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    procedure Dispose(Disposing: Boolean); override;
  private
    { Private Declarations }
  public
    constructor Create;
  end;
 
  [assembly: RuntimeRequiredAttribute(TypeOf(TWinForm2))]
 
implementation
 
{$REGION 'Windows Form Designer generated code'}
/// <summary>
/// Required method for Designer support -- do not modify
/// the contents of this method with the code editor.
/// </summary>
procedure TWinForm2.InitializeComponent;
begin
  Self.Components := System.ComponentModel.Container.Create;
  Self.Size := System.Drawing.Size.Create(300, 300);
  Self.Text := 'WinForm2';
end;
{$ENDREGION}
 
procedure TWinForm2.Dispose(Disposing: Boolean);
begin
  if Disposing then
  begin
    if Components <> nil then
      Components.Dispose();
  end;
  inherited Dispose(Disposing);
end;
 
constructor TWinForm2.Create;
begin
  inherited Create;
  //
  // Required for Windows Form Designer support
  //
  InitializeComponent;
  //
  // TODO: Add any constructor code after InitializeComponent call
  //
end;
 
end.


Należy już na samym początku zaznaczyć, że składnia modułu WinForms znacząco różni się od standardowego VCL.NET (listing 6.2).

Listing 6.2. Kod formularza w VCL.NET
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;
 
type
  TForm1 = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.nfm}
 
end.


Na pierwszy rzut oka widać wyraźną różnicę pomiędzy kodem formularza WinForms a VCL.NET. Przede wszystkim ten drugi jest prostszy i krótszy.

Na razie omówię podstawowe części kodu formularza w VCL.NET, gdyż jest on prostszy i może ułatwić Czytelnikowi zrozumienie znaczenia poszczególnych elementów.

Po utworzeniu nowego projektu VCL (nie VCL.NET) łatwo zauważyć, że kod formularza jest prawie identyczny, tak więc informacje o VCL.NET zawarte w tym rozdziale dotyczą także biblioteki VCL.

Formularz w VCL.NET


Moduły i ich budowa zostały szczegółowo omówione w rozdziale 3. poświęconym elementom języka Delphi. To trzeba zapamiętać! Każdemu formularzowi odpowiada jeden moduł z odpowiednim kodem, lecz to twierdzenie nie jest prawdziwe w drugą stronę — moduł niekoniecznie musi być formularzem.

Podczas projektowania wizualnego Delphi samodzielnie edytuje kod źródłowy, dodając lub zmieniając odpowiednie elementy — dzięki temu projektowanie staje się szybsze, a programista nie musi martwić się o szczegóły związane z dodaniem jakiegoś potrzebnego elementu.

Kod źródłowy formularza (modułu) składa się także ze standardowych sekcji i słów kluczowych typu: unit, interface, uses, implementation.

Sekcja uses


Podczas tworzenia nowego projektu Delphi dodaje do sekcji uses standardowe moduły, konieczne do prawidłowego działania aplikacji. W tych modułach znajdują się deklaracje odpowiednich klas, dzięki czemu projekt może zostać skompilowany. Przykładowo, nadajmy sekcji uses następujący wygląd:

uses
  Windows;


Po próbie kompilacji Delphi już na początku wyświetli komunikat o błędzie: [Error] Unit1.pas(9): Undeclared identifier: 'TForm'. Informacja ta mówi o tym, że nazwa TForm nie jest rozpoznawalna dla kompilatora. Stało się tak dlatego, że z sekcji uses usunięto moduł Forms, który zawierał deklarację typu TForm.

Klasa


Poniższy fragment w kodzie źródłowym jest deklaracją klasy, o której już tyle mówiliśmy:

type
  TForm1 = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;


W tym momencie został zadeklarowany nowy typ — TForm1, który jest klasą wywodzącą się z kolei z klasy bazowej TForm. Zostało tutaj użyte tzw. dziedziczenie. Termin ten zostanie omówiony w rozdziale 7.

W VCL.NET i VCL nazwy wszystkich klas zaczynają się od litery T.

Zmienna wskazująca na klasę


Poniżej deklaracji klasy, jeszcze w sekcji interface, jest zadeklarowana standardowo zmienna, która wskazuje na nasz typ — TForm1:

var
  Form1: TForm1;


Plik *.nfm


Komponenty umieszczane na formularzu mogą mieć różne właściwości. Te dane muszą być gdzieś przechowywane. W VCL.NET miejscem, gdzie są zapisywane te informacje, jest plik *.nfm (w VCL ten plik nosi rozszerzenie *.dfm), którego zawartość została przedstawiona na listingu 6.3.

Listing 6.3. Zawartość pliku *.nfm
object Form1: TForm1
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  Height = 240
  Width = 347
  Left = 13
  Top = 13
  PixelsPerInch = 96
  TextHeight = 13
end


Praktycznie nigdy nie zachodzi potrzeba, aby ręcznie edytować ten plik. To zadanie należy już do Delphi, które dokonuje zapisu i odczytu danych właśnie z tego pliku. Pliki *.nfm zawierają informacje na temat formularza, jego właściwości, komponentów oraz właściwości komponentów.

W pliku źródłowym formularza znajduje się dyrektywa nakazująca kompilatorowi dołączyć do projektu plik *.nfm:

{$R *.nfm}


Jest to specjalna instrukcja — na pierwszy rzut oka wyglądająca jak komentarz, jednak jest informacją dla kompilatora o włączeniu do projektu wszystkich plików z rozszerzeniem *.nfm.

Generowanie kodu


Teraz możemy powrócić do projektanta formularzy. Odszukajmy na palecie narzędzi komponent DBGrid i umieśćmy go na formularzu. Następnie z powrotem należy otworzyć edytor kodu — łatwo zauważyć, że Delphi w sekcji uses dodało nowe moduły: Borland.Vcl.Grids oraz Borland.Vcl.DBGrids, które są wymagane do prawidłowego skompilowania aplikacji. W klasie Form1 znalazł się również nowy wpis:

DBGrid1: TDBGrid;


Dzięki temu oszczędzamy na czasie, nie musząc wpisywać ręcznie tego kodu.

Powyższa linia w klasie Form1 identyfikuje komponent typu TDBGrid o nazwie DBGrid1. Delphi automatycznie nadaje własne nazwy tworzonym komponentem, przypisując im kolejne numery: DBGrid1, DBGrid2 itd. Jeżeli na formularzu umieścić kilka komponentów typu TButton, to Delphi nazwie je odpowiednio: Button1, Button2 itd.

Jeżeli w wyniku opisanych powyżej operacji, Delphi nie dołączy do listy uses wspomnianych modułów, można spróbować skompilować projekt (Ctrl+F9) — to powinno pomóc.

Nazwy komponentów


Nazwa komponentu jest symboliczna. Z punktu widzenia kompilatora nie jest ważne, czy komponent nazywa się Button1 czy ToJestMójFajnyButtonikNumer1. Ważne jest jedynie to, aby nazwa była unikalna, a więc nie mogą istnieć dwa komponenty o tej samej nazwie. W nazwie komponentu nie mogą być użyte polskie znaki. Nazwa nie może się także zaczynać od cyfry.

Nazwę komponentu można zmienić, korzystając z inspektora obiektu. W tym celu trzeba znaleźć właściwość Name, której można nadać nową wartość, będącą nazwą komponentu. Po dokonaniu modyfikacji Delphi uaktualni również wszelkie wpisy w kodzie źródłowym związane z tą nazwą.

Nigdy nie należy pozostawiać domyślnych nazw komponentów, jakie nadaje Delphi (tzn. Button1, Button2 itd.). W późniejszym czasie łatwo jest się pogubić w takim nazewnictwie. Lepiej nazywać komponenty opisowo — to znaczy tak, aby opisywały rolę, jaką pełnią. Jeżeli, przykładowo, dany komponent wyświetla informacje o autorze programu, można go nazwać btnAbout. W przypadku przycisku, który ma zamykać program — niech będzie to nazwa btnClose.

Programowanie zdarzeniowe


Programy napisane w Delphi są programami zdarzeniowymi, które wykonują pewne czynności pod wpływem działania użytkownika. Co to oznacza? Kiedy program jest uruchamiany, następuje jego załadowanie do pamięci, rezerwowanie pamięci itp. Następnie taki program czeka na zadania. Projektując nasze aplikacje wizualne, programujemy pewne zdarzenia, na jakie ma reagować program — np. kliknięcie przyciskiem myszy w obszarze komponentu. Jeżeli aplikacja odbierze takie zdarzenie (użytkownik kliknie w obszarze komponentu), jest podejmowane pewne działanie. To samo tyczy się innych przypadków — poruszanie myszą, wprowadzanie tekstu z klawiatury itp.

Generowanie zdarzeń


W kodzie źródłowym modułu nie można w dowolnym miejscu umieszczać instrukcji wykonujących pewne czynności (pomijamy tu sekcję initialization oraz finalization) — wszystko musi być zawarte w procedurach czy funkcjach.

  1. Utwórz nowy projekt VCL.NET.
  2. Umieść na formularzu komponent TButton.
  3. Kliknij dwukrotnie w przycisk (komponent TButton).

W tym momencie Delphi powinno wygenerować w kodzie źródłowym zdarzenie i otworzyć edytor kodu, natomiast kursor ustawić w ciele procedury:

procedure TForm1.Button1Click(Sender: TObject);
begin
 
end;


Właśnie we wnętrzu tej procedury należy umieścić kod, który będzie wykonywany po naciśnięciu przycisku w trakcie działania programu. Zaprogramujmy naszą aplikację, tak aby w wyniku naciśnięcia przycisku zmieniała się wartość pewnej właściwości.

Przykładowy program


Jak dotąd pokazywałem, w jaki sposób zmieniać właściwości komponentu jedynie z poziomu inspektora obiektów. Należałoby również wiedzieć, że jest to możliwe także z poziomu kodu źródłowego za pomocą operatora odwołania (.).

Nasz przykładowy program będzie zmieniał pozycję przycisku na formularzu. Nowa pozycja będzie losowana i ustawiana za każdym razem, gdy użytkownik kliknie przycisk.

Każda kontrolka wizualna w VCL.NET posiada właściwości: Width (szerokość), Height (wysokość), Left (pozioma pozycja na formularzu), Top (pionowa pozycja na formularzu). Tak więc nadanie nowej wartości dla właściwości z poziomu kodu wygląda następująco:

Button1.Width := 100; // szerokość komponentu


Do konkretnych właściwości odwołujemy się za pomocą operatora odwołania (kropka), a wartości nadajemy tak jak zwykłym zmiennym.

Rozwiązanie

Przy każdorazowym naciśnięciu przycisku program powinien losować nową wartość z określonego zakresu i ustawiać komponent na tej wylosowanej wartości. Pytanie brzmi: jaki to będzie zakres? Odpowiedź: szerokość i wysokość formularza!

Listing 6.4 przedstawia kod całego modułu programu.

Listing 6.4. Kod modułu
unit Unit1;
 
interface
 
uses
  Windows, Messages, Variants, SysUtils, Graphics, Forms,
  Dialogs, System.ComponentModel, Borland.Vcl.Controls, 
  Borland.Vcl.StdCtrls;
 
type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.nfm}
 
procedure TForm1.Button1Click(Sender: TObject);
var
  FormWidth, FormHeight : Integer;
begin
{ przypisz wartości opisujące szerokość i wysokość formularza }
  FormWidth := Form1.Width - 100;
  FormHeight := Form1.Height - 50;
 
  Randomize; // losowanie
 
{ nadanie nowych wartości właściwościom Left i Top }
  Button1.Left := Random(FormWidth);
  Button1.Top := Random(FormHeight);
end;
 
end.


Szerokość i wysokość formularza odczytujemy z właściwości Width oraz Height, a następnie przypisujemy te wartości do zmiennych zadeklarowanych wcześniej: @@FormWidth@@ oraz @@FormHeight@@:

FormWidth := Form1.Width - 100;
FormHeight := Form1.Height - 50;


Warto zwrócić uwagę, że dodatkowo odjęliśmy od tych wartości liczby, odpowiednio, 100 i 50, tak aby zachować pewien margines podczas losowania liczb.

Następnie wylosowane wartości posłużą jako nowe współrzędne położenia komponentu TButton:

Button1.Left := Random(FormWidth);
Button1.Top := Random(FormHeight);


Wiadomo już, dlaczego odjęliśmy 100 i 50 od wartości zmiennej @@FormWidth@@ i @@FormHeight@@? Zakładając, że szerokość formularza wynosi 500 — program będzie losował jedynie z zakresu od 0 do 400, tak aby nie wylosował liczby 500 — gdyby tak się stało, to komponent nie zmieściłby się całkowicie w oknie (jego krawędź wystawałaby poza obszar formularza). Wszystko dlatego, że właściwość @@Left@@ określa pozycję lewej krawędzi komponentu względem formularza.

Zmiany kosmetyczne

  1. W inspektorze obiektów zmień właściwość @@Width@@ oraz @@Height@@ formularza na 500.
  2. Zmień właściwość @@Caption@@ przycisku (Button1) na Kliknij mnie!.

Działanie programu zostało przedstawione na rysunku 6.1.
 

Rysunek 6.1. Program w trakcie działania

Lista zdarzeń w inspektorze obiektów


Wspominałem wcześniej o zakładce Events inspektora obiektów. Po wykonaniu wcześniejszego ćwiczenia na liście Events przy pozycji @@OnClick@@ znajdzie się wpis: Button1Click (rysunek 6.2).
 

Rysunek 6.2. Inspektor obiektów z wygenerowanym zdarzeniem OnClick

Po zaznaczeniu zdarzenia @@OnClick@@, zostanie ono podświetlone na szaro. Po przeniesieniu wskaźnika myszy nad tekst powinien on zmienić kształt. Podwójne kliknięcie myszą w tym miejscu spowoduje otwarcie edytora kodu w miejscu deklaracji zdarzenia @@OnClick@@. Tak samo jest z generowaniem innych zdarzeń — np. @@OnKeyDown@@.

Jedno zdarzenie dla kilku obiektów


Czasami może zajść potrzeba przypisania tego samego zdarzenia @@OnClick@@ (lub jakiegokolwiek innego) do kilku przycisków lub innych komponentów.

W takim przypadku po zaznaczeniu wybranej pozycji, np. @@OnClick@@, po prawej stronie pojawia się strzałka, po naciśnięciu której rozwija się lista ze zdarzeniami dostępnymi w programie. Można dla swojego komponentu wybrać dowolne zdarzenie.

Generowanie pozostałych zdarzeń


Projektant może korzystać ze wszystkich zdarzeń, jakie w obrębie danego komponentu oferuje Delphi. Przykładowo, zdarzenie @@OnMouseMove@@ umożliwia oprogramowanie akcji przesuwania wskaźnika myszy nad obiektem. Innymi słowy, można zapewnić odpowiednią reakcję aplikacji na taką sytuację.

W jaki sposób odbywa się generowanie zdarzeń z poziomu inspektora obiektów? Należy zaznaczyć wybrane zdarzenie, niech to będzie @@OnMouseMove@@, klikając je. Po prawej stronie pojawi się lista rozwijalna, która najpierw jest pusta. Jeśli wskaźnik myszy znajdzie się nad białym polem, powinien zmienić swój kształt. W tym momencie wystarczy kliknąć dwukrotnie — spowoduje to wygenerowanie zdarzenia @@OnMouseMove@@.

procedure TMainForm.btnMainMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
begin
 
end;


W niniejszej książce dla określenia nazw komponentów będę posługiwał się prawidłowym określeniem z literą T na początku — np. TButton czy TLabel. Są to poprawne nazwy dla komponentów VCL, chociaż wiele osób nadaje komponentom nazwy pozbawione tej litery — Button, Label.

Jeżeli wygenerowane zdarzenie pozostanie puste, tj. nie będzie zawierało żadnego kodu, zostanie usunięte podczas kompilacji lub zapisu projektu.

Edytor kodu


Przed przystąpieniem do dalszej pracy, wypadałoby poznać główne narzędzie pracy, opisywane w tym rozdziale — edytor kodu (rysunek 6.3).
 

Rysunek 6.3. Edytor kodu

Edytor kodu jest zwykłym edytorem tekstu. Jest podzielony na zakładki, co daje możliwość jednoczesnej edycji wielu plików. W przypadku, gdy mamy do czynienia z kodem źródłowym formularza, dodatkowo okno jest podzielone na dole na dwie zakładki — Code (pisanie kodu) oraz Design (projektowanie wizualne).

W Delphi 2005 dodatkowo można korzystać z funkcji historii (zakładka History), ale o tym opowiem w dalszej części rozdziału.

Ukrywanie kodu


Edytor Delphi posiada możliwość ukrywania fragmentu kodu. W takim przypadku z lewej stronie edytora zostaną wyświetlone małe ikony, których kliknięcie spowoduje rozwinięcie ukrytego fragmentu. Jest to dość ciekawe rozwiązanie — umożliwia zwiększenie przejrzystości w kodzie i schowanie fragmentu, który w danym momencie nie interesuje programisty.

Wszystko to odbywa się za sprawą dyrektywy {$REGION}, którą należy wstawić przed ukrywanym blokiem. Wygląda to następująco:

{$REGION 'Nazwa regionu'}
// kod, który chcemy ukryć
{$ENDREGION}


Po wpisaniu takich instrukcji będzie możliwe ukrywanie danych zapisanych pomiędzy tymi dyrektywami.

Opcja ukrywania części kodu pojawiła się po raz pierwszy w Delphi 8.

Makra


Mówiąc ogólnie, makra (również wprowadzone jako nowość w Delphi 8) umożliwiają „nagrywanie” pewnych operacji dokonanych w kodzie źródłowym, a następnie ich „odtwarzanie”. Wszystkie trzy przyciski z tym związane znajdują się w lewym dolnym rogu edytora kodu.

Przeprowadźmy pewien eksperyment w celu potwierdzenia moich słów: należy kliknąć przycisk Record Macro (środkowy przycisk) i wpisać w edytorze kodu tekst:

ShowMessage('Nagrywamy makro...');


Po wpisaniu trzeba kliknąć przycisk Stop Recording Macro. Od tej pory pierwszy przycisk — Playback Macro powinien się uaktywnić. Po jego kliknięciu w edytorze kodu ponownie zostanie wstawiona nagrana instrukcja.

Code Insight


Code Insight należy do najważniejszych i zarazem najlepszych narzędzi w Delphi. Trudno jest zapamiętać parametry wszystkich funkcji udostępnionych przez Delphi, ba — śmiem twierdzić, że jest to niemożliwe. Code Insight podczas pisania wyświetla deklaracje danej procedury, dzięki czemu wiadomo, ile dana procedura ma mieć parametrów i jakiego mają być typu.
Podczas pisania kodu wystarczy, że po nazwie procedury bądź funkcji postawimy znak nawiasu otwierającego, który rozpoczyna wprowadzanie parametrów do procedury. Po chwili powinno wyświetlić się małe okienko z listą parametrów (rysunek 6.4).
 

Rysunek 6.4. Code Insight w działaniu

Pogrubiony parametr w Code Insight oznacza aktualnie wstawiany parametr. Wiadomo, że parametry muszą być oddzielone przecinkami — na tej podstawie Code Insight jest w stanie sprawdzić, który parametr jest wpisywany w danym momencie.

Skrótem klawiaturowym, który powoduje wyświetlenie podpowiedzi, jest Ctrl+Shift+spacja.

Code Completion


Inną usługą technologii Code Insight jest Code Completion. Usługa ta umożliwia podgląd wszystkich procedur, funkcji czy właściwości możliwych do zastosowania w danym momencie. Skrótem klawiaturowym Code Completion jest Ctrl+spacja. Samo okno jest pokazane na rysunku 6.5.
 

Rysunek 6.5. Code Completion w działaniu

W trakcie pisania kodu po postawieniu operatora kropki (.) także zostanie wyświetlona lista wszystkich metod danego obiektu (klasy).

Lista Code Completion jest dopasowywana do tekstu, który aktualnie jest wpisywany. Oznacza to, że gdy wstawiamy np. literę B, wówczas na liście Code Completion pojawią się jedynie metody o nazwach zaczynających się na literę B.

Nawigacja w obrębie listy Code Completion jest możliwa za pomocą klawiszy strzałek na klawiaturze. Naciśnięcie przycisku Enter spowoduje wstawienie do kodu zaznaczonej pozycji.

Począwszy od Delphi 7 w wersjach Professional oraz Enterprise istnieje możliwość wykorzystania Code Completion także w plikach HTML. Poleceniem File/Open można otworzyć jakiś plik HTML. Wówczas Code Completion (Ctrl+spacja) będzie zawierał listę możliwych do zastosowania znaczników HTML.

Help Insight


Nowością w Delphi 2005 jest okno Help Insight, które daje użytkownikowi jeszcze więcej informacji na temat danego symbolu, czy to nazwy klasy, czy też procedury. Jeśli przesunąć wskaźnik myszy nad jakiś symbol — niech to będzie słówko TButton w kodzie źródłowym programu — zostanie wyświetlone małe okienko zawierające informacje o module, w którym została zadeklarowana klasa TButton. Po kliknięciu nazwy modułu, Delphi wyświetli bardziej szczegółowe informacje na temat tego komponentu.

Okno Help Insight może być używane także w połączeniu z Code Completion, co daje nam jeszcze więcej informacji na temat metody czy właściwości danego obiektu (np. TButton), z których możemy skorzystać (rysunek 6.6).
 

Rysunek 6.6. Funkcja Help Insight połaczona z Code Completion

Wyróżnianie błędów


Kolejną nowością w Delphi 2005 jest wyróżnianie błędów w kodzie źródłowym jeszcze przed procesem kompilacji (Error Insight). Daje to możliwość naprawienia pomyłki. Można sprawdzić działanie tej funkcji, wystarczy spojrzeć na następując kod:

procedure TForm2.Button1Click(Sender: TObject);
begin
   Mojafunkcja;
end;


Nie zostanie on prawidłowo skomplikowany, jeśli wcześniej nigdzie nie zadeklarowano procedury czy funkcji o nazwie Mojafunkcja. Można wkleić ten blok kodu do swojego projektu. Fraza Mojafunkcja powinna zostać podkreślona charakterystyczną, czerwoną linią.

Dodatkowo, w oknie struktury (ang. Structure) powinno pojawić się ostrzeżenie opisujące dany błąd. Podwójne kliknięcie opisu błędu spowoduje przejście kursora edytora kodu do linii, w której znajduje się błąd.

Menu podręczne edytora kodu


Jak każdy edytor tekstu, również edytor kodu w Delphi oferuje podstawowe funkcje w menu podręcznym, takie jak wycinanie, kopiowanie czy wklejanie ze schowka.

Opcja Close Page (skrót Ctrl+F4) umożliwia zamknięcie aktywnego okna. Przedtem Delphi wyświetli zapytanie, czy należy zapisać zmiany.

Opcja Show Designer powoduje wyświetlenie formularza, nad którym pracujemy. Odpowiednikiem tej opcji jest skrót klawiaturowy F12, powodujący uruchomienie projektanta formularzy z poziomu edytora kodu.

Pozycja Open File At Cursor umożliwia otwarcie danego pliku źródłowego na kolejnej zakładce edytora kodu. Wystarczy kliknąć prawym przyciskiem myszy np. którąś pozycję z sekcji uses i wybrać z menu Open File At Cursor. W edytorze kodu powinna pojawić się nowa zakładka z kodem źródłowym danego modułu.

Pozycja File Format daje możliwość ustawienia formatu dla pliku — od standardowego ANSI do formatu binarnego. W Delphi 2005 możemy dodatkowo wybrać kodowanie znaków dla tworzonego kodu źródłowego (UTF-8 lub ANSI).

Topic Search powoduje szukanie w systemie pomocy hasła związanego z frazą, na której znajduje się kursor tekstowy. Czyli jeżeli kursor znajduje się na słowie kluczowym procedure, to opcja ta (skrót F1) spowoduje znalezienie tematów ze słowem procedure.

Pozycja Complete Class At Cursor związana jest z uzupełnianiem definicji procedury. Jest to swego rodzaju ułatwienie dla programisty — wystarczy wpisać deklarację procedury w sekcji interface i nacisnąć Ctrl+Shift+C, aby uzupełnić definicję owej procedury w sekcji implementation.

Add To-Do Item jest opcją związaną z wyświetleniem okienka To-Do i wstawieniem odpowiedniej frazy w miejscu, gdzie znajduje się kursor. Opowiem o tym parę akapitów dalej.

Edytor Delphi umożliwia także ustawianie tzw. bookmarks (zakładek) w miejscu, gdzie znajduje się kursor. Do ustawiania zakładki służy pozycja Toggle Bookmarks, a do przechodzenia do zakładki — Goto Bookmarks. Niestety, Delphi umożliwia ustawienie zaledwie 9 zakładek.

W Delphi 2005 zakładki pozostają zachowane również po zamknięciu projektu. Aby usunąć zakładki, można wybrać z menu kontekstowego polecenie Clear Bookmarks.

Pozycja w menu nazwana Debug jest związana z debugerem Delphi i na razie nie będziemy się nią zajmować, gdyż temat ten omówię szczegółowo w rozdziale 9.

Istnieje możliwość ustawienia „flagi” w edytorze kodu, dzięki której edytowany tekst staje się tekstem tylko do odczytu (ang. read only). Wszystko to za sprawa pozycji w menu — Read Only. Opcja ta przydaje się w przypadkach, gdy edytowanie danego dokumentu przez kogoś innego jest niepożądane.

Kolejna pozycja, Message View, stanowi opcję związaną z wyświetlaniem dodatkowego okna w edytorze kodu — okna komunikatów.

Wreszcie dwie nowe pozycje u dołu menu, wprowadzone w Delphi 8, są związane właśnie z ukrywaniem części kodu modułu — np. regionów, typów, metod itp. Odkrywanie całości zapewnia opcja Unfold/All.

Nowością w Delphi 2005 jest pozycja Toggle Comment, która umożliwia skomentowanie linii kodu, w której aktualnie jest ustawiony kursor. Opcja stosuje komentarz jednoliniowy (//). Opcja posiada także przeciwne działanie — usuwa komentarz z linii, w której znajduje się kursor. Skrót klawiaturowy tej opcji to Ctrl+/.

Kolejną, nową opcją w Delphi 2005 jest Refactoring oraz Find — omówię je w dalszej części rozdziału.

Opcje edytora kodu


Do okna opcji edytora kodu można przejść wprost z menu podręcznego, wybierając pozycję Properties (rysunek 6.7).
 

Rysunek 6.7. Okno opcji Delphi

W rzeczywistości w tym oknie można ustawiać wszystkie opcje związane z Delphi — my jednak będziemy się na razie zajmować jedynie opcjami edytora (Editor Options), które są podzielone dodatkowo na kilka innych kategorii.

Editor Options


Opcje znajdujące się w głównej kategorii zostały opisane w tabeli 6.1.

Tabela 6.1. Opcje z kategorii Editor Options
OpcjaOpis
Insert ModeDomyślnie jest zaznaczona. Powoduje, iż tekst wstawiany do edytora kodu nie będzie zastępował już tam istniejącego. Opcję można włączyć lub wyłączyć klawiszem Insert.
Group UndoGrupowe cofanie za pomocą menu Edit/Undo. Przykładowo, cofnięcie wpisanego tekstu wymaże cały tekst za jednym razem, inaczej cofanie zmian odbywałoby się znak po znaku.
Double Click LinePodwójne kliknięcie zaznacza całą linię tekstu.
Force cut and copy enabledUaktywnia opcje kopiowania i wycinania, nawet jeżeli tekst nie jest zaznaczony.
Auto-complete text to findWłącza autouzupełnianie w oknie szukania tekstu.
Create backup filesTworzy kopie zapasowe plików.
Undo After SavePozwala na cofanie dokonanych operacji nawet po zapisie pliku.
Persistent BlockPo zaznaczeniu fragmentu tekstu nie usuwa zaznaczenia nawet po przesunięciu kursora klawiszami strzałek na klawiaturze.
Overwrite BlocksJeżeli zaznaczony jest jakiś fragment tekstu, po rozpoczęciu wprowadzania znaków z klawiatury zostanie on usunięty (w przypadku zaznaczenia tej opcji).
Find text at CursorJeżeli opcja jest zaznaczona, domyślnym tekstem wyszukiwanym (Ctrl+F) będzie tekst znajdujący się za kursorem.


Na samym dole zakładki znajdują się dodatkowo trzy listy rozwijalne: Editor SpeedSetting, Undo limit oraz File backup limit. Pierwsza z nich zawiera gotowe szablony ustawienia kategorii Editor Options. W zależności od wybranej pozycji, ustawienia opcji będą odpowiednio zmieniane.

Druga lista definiuję liczbę operacji możliwych do cofnięcia w edytorze. Standardowo jest to 32767.

Trzecia pozycja, która pojawiła się po raz pierwszy w Delphi 2005, określa liczbę generowanych kopii zapasowych plików (domyślnie 10). Nowa wersja oferuje bowiem zapisywanie całej historii projektu (zajmiemy się tym później).

Source Options


Jak sama nazwa wskazuje, kategoria Source Options ma związek z bezpośrednią edycją kodu źródłowego (rysunek 6.8).
 

Rysunek 6.8. Kategoria Source Options

Głównym komponentem tego okna jest lista rozwijalna położona na samej górze — Source file type. Na owej liście są zadeklarowane różne typy kodu źródłowego — np. Pascal, C#, XML itp. Zmiana pozycji z tej listy powoduje równocześnie zmianę opcji tej zakładki.

Edytor Delphi umożliwia edycję kodu innych języków programowania oraz różne kolorowanie składni zawartości plików.

Istnieje możliwość dodawania własnego szablonu (New) i ustawiania własnych opcji lub usunięcia dotychczasowego szablonu (Delete).

Lista rozwijalna Extensions definiuje rozszerzenia, które identyfikują obecne ustawienia. Na przykład, jeżeli użytkownik otworzy w edytorze plik z rozszerzeniem *.pas, to będzie to oznaczało, że edytor ma wczytać ustawienia Pascala.

Lista rozwijalna Syntax Higlighter określa ustawienia kolorowania składni dla różnych plików.

Lista Block indent pozwala na określenie liczby spacji, jakie będą stanowiły wcięcia w kodzie. Na pewno podczas lektury tej książki Czytelnik zauważył, że stosuję wcięcia w kodzie o wielkości dwóch spacji, dzięki czemu mój kod jest bardziej czytelny.

Tab Stops oznacza liczbę znaków, o które będzie przechodził kursor tekstowy po naciśnięciu klawisza Tab.

Pozostałe opcje zostały omówione w tabeli 6.2.
Tabela 6.2. Opcje kategorii Source Options
OpcjaOpis
Auto indent modePo naciśnięciu klawisza Enter automatycznie jest wykonywane wcięcie, co spowoduje ustawienie kursora w takiej samej pozycji, jak w poprzedniej linii.
Use tab characterJeżeli opcja jest włączona, edytor faktycznie po naciśnięciu Tab umieszcza znak tabulatora. W przeciwnym przypadku zamiast znaku Tab wstawiana jest odpowiednia liczba spacji.
Smart tabPrzemieszcza się do pierwszego znaku w linii (znak nie może być spacją 1) .
Cursor through tabsUmożliwia przechodzenie klawiszami strzałek w miejsce wstawienia znaku Tab.
Optimal fillOpcja związana z Auto indent mode; w przypadku przejścia do nowej linii w miarę możliwości wcięcia (spacje) zamienia na znak tabulacji.
Backspace unindentsCofniecie (Backspace) spowoduje wyrównanie kursora w stosunku do tekstu z wiersza powyżej.
Show tab characterWyróżnia znaki tabulatora.
Show space characterWyróżnia znaki spacji.
Use Syntax HiglighterWłączenie/wyłączenie kolorowania składni.
Show line breaksWłączenie tej opcji powoduje wyróżnianie miejsca zakończenia linii (ostatni znak w danej linii).
Highlight current lineWłączenie tej opcji powoduje, iż linia, w której aktualnie znajduje się kursor będzie specjalnie podświetlona.


Color


Kategoria Color (rysunek 6.9) ma związek jedynie z ustawieniami kolorowania składni.
 

Rysunek 6.9. Kategoria Color

Na samej górze, na liście rozwijalnej (Color SpeedSettings), można od razu wybrać gotowe schematy kolorowania składni.

Druga lista rozwijalna — Element — służy do ustawiania kolorów konkretnego elementu języka — np. słów kluczowych, komentarzy itp.

Kolejne opcje są związane jedynie ze szczegółowymi ustawieniami czcionki: Bold (pogrubienie), Italic (kursywa), Underline (podkreślenie), Foreground (użyj standardowego ustawienia koloru), Background (użyj standardowego ustawienia koloru tła), Foreground Color (kolor tekstu), Background Color (kolor tekstu dla tła).

Poniżej znajdują się dodatkowo zakładki — C#, HTML/XML, C/C++ i Delphi, co daje kolejne możliwości w ustawieniach kolorowania składni dla różnych języków programowania.

Display


Kategoria Display (rysunek 6.10) zawiera wszelkie opcje związane z wyświetlaniem tekstu (rozmiar czcionki itp.) oraz z ustawieniami samego edytora kodu.
 

Rysunek 6.10. Kategoria Display

Myślę, że nie trzeba objaśniać pozycji znajdujących się na samym dole (Editor font oraz Size), które zwyczajnie określają nazwę oraz rozmiar czcionki używanej przez edytor. Opis pozostałych opcji znajduje się w tabeli 6.3.

Tabela 6.3. Opcje kategorii Display
OpcjaOpis
BRIEF cursor shapesZmiana standardowego kursora w edytorze kodu.
Show close button on tabsNa zakładkach jest wyświetlany przycisk umożliwiający ich zamknięcie (jeżeli opcja jest włączona).
Zoom to full screenPowiększanie okna edytora kodu.
Sort popup pages menuSortuje okna w menu podręcznym edytora kodu (po kliknięciu listy zakładek, rozwinie się menu z pozycją Pages).
Visible right marginPokaż prawy margines (w formie linii).
Visible gutterPokaż lewą belkę w lewej części edytora kodu.
Show line numbersPokaż numer linii na belce z lewej strony.
Right marginPozycja prawego marginesu.
Gutter widthSzerokość belki po lewej stronie.


Key Mappings


W praktyce opcje znajdujące się na tej zakładce są rzadko używane — pewnie Czytelnik nie będzie miał potrzeby ingerowania w te ustawienie.

Na tej zakładce istnieje możliwość wprowadzania ustawień klawiatury — np. obsługi różnych skrótów klawiaturowych — my pozostawimy ustawienia standardowe.

Code Insight


Nieco wcześniej mówiłem o Code Insight. Kategoria (rysunek 6.11) o tej samej nazwie, dostępna w oknie Options, umożliwia określanie szczegółowych ustawień związanych z tą techniką.
 

Rysunek 6.11. Kategoria Code Insight

Większość opcji (w ramce Symbols Colors) jest związana z kolorystyką poszczególnych elementów Code Insight.

Najlepszym rozwiązaniem jest pozostawienie wszystkich opcji włączonych (ustawienia domyślne), gdyż dzięki temu Code Insight najlepiej pełni swoją rolę — podpowiadając programiście tak często, jak tylko jest to możliwe.

Suwak Delay umożliwia ustawianie czasu, po jakim powinien pojawić się Code Insight.

Skrawki kodu


Wspominałem wcześniej o palecie narzędzi. W wersji Delphi 2005 znalazła ona jeszcze jedno zastosowanie, pomocne przy edycji kodu. Umożliwia wstawianie do kodu tzw. skrawków kodu (ang. code snippets) poprzez jedno naciśnięcie odpowiedniego przycisku (rysunek 6.12).
 

Rysunek 6.12. Paleta narzędzi z Code Snippets

Po kliknięciu którejś z tych pozycji w miejscu umieszczenia kursora w edytorze kodu zostanie wstawiony odpowiedni zapis — np. ciało pętli for.

Dodawanie pozycji do okna Code Snippets


Opcja Code snippets jest wygodna, lecz byłaby bardziej funkcjonalna, gdyby można było w łatwy sposób dodawać do niej własne skrawki kodu. Czynność ta nie jest jednak zbyt intuicyjna. Aby dodać skrawek kodu do palety narzędzi, należy w edytorze kodu zaznaczyć wybrany fragment kodu, a następnie nacisnąć i przytrzymać klawisz Alt. Przytrzymując klawisz Alt, metodą przeciągnij i upuść, należy przeciągnąć zaznaczony fragment kodu nad paletę narzędzi.

To-Do List


Niezwykle przydatnym i pomysłowym narzędziem okazała się wprowadzona w Delphi 7 lista To-Do (w polskim tłumaczeniu — do zrobienia). Opcja ta szczególnie przydaje się w trakcie pracy zespołowej nad projektem.

W wielu projektach zespołowych wręcz obowiązkiem stało się dołączenie do projektu pliku TODO, który miał zawierać wpisane po kolei uwagi dotyczące kolejnej wersji programu, czyli — inaczej mówiąc — spisu tego, co w programie należy jeszcze zrobić.

W Delphi zaimplementowano mechanizm dodawania odpowiednich fraz w treści kodu źródłowego jako komentarza, np.:

procedure TForm1.Button2Click(Sender: TObject);
begin
  { TODO 4 -oJanek : Musisz wpisać tekst zdarzenia! }
end;


Z punktu widzenia kompilatora jest to zwykły komentarz, lecz w gruncie rzeczy stanowi on informację dla czytającego kod źródłowy, że w tym miejscu należy coś jeszcze zmienić.

Tego typu komentarze są także wyświetlane w okienku To-Do List, wywoływanym z menu View/To Do List (rysunek 6.13).
 

Rysunek 6.13. To-Do List

W oknie w postaci wierszy są przedstawione kolejne napotkane w kodzie komentarze TODO — w ten sposób jest tworzona lista, tak jak pokazano na rysunku 6.13. Po kliknięciu którejś z pozycji program przechodzi do żądanego fragmentu kodu, tego, w którym znalazł się komentarz TODO.

Generowanie komentarza TODO


Odpowiedni komentarz TODO można wygenerować, korzystając z opcji menu podręcznego edytora kodu — Add To-Do Item (rysunek 6.14).
 

Rysunek 6.14. Okno Add To-Do Item

Główną kontrolką okna jest Text. Tu wpisuje się pewne wskazówki lub inny tekst, który zostanie wstawiony — np. Popraw działanie pętli!.

Okno Priority służy do określenia priorytetu danej pozycji. Wiadomo, że im wyższy priorytet, tym ważniejsze jest wykonanie tego zadania. Delphi umożliwia nadawanie priorytetów w postaci liczb z zakresu od 0 do 5. Priorytet 0 powinny mieć czynności kosmetyczne — zupełnie nieistotne i niemające większego wpływu na działanie aplikacji czy bezpieczeństwo.

Kontrolka Owner określa, do kogo jest skierowana wskazówka. Zawsze należy nadawać wartość tej kontrolce. Jeżeli zadanie nie jest skierowane do nikogo konkretnego, można w tym miejscu wstawić po prostu All lub Wszyscy. Jeżeli nad projektem pracują dwie osoby — także należy wstawiać imię tego drugiego programisty, aby się nie pogubić (z biegiem czasu można zapomnieć, kto jest autorem danego wpisu i uznać go za uwagę skierowaną do siebie).

Lista rozwijalna Category umożliwia określenie kategorii danego wpisu — np. można wpisać w tym oknie: Interfejs lub Engine.

Menu podręczne okna


Zasadniczo okno View => To-Do List służy do wyświetlania pozycji TODO i nawigowania między nimi, ale posiada także menu podręczne z ciekawymi opcjami, o których także należy wspomnieć.

Pierwsza pozycja z menu (Open) powoduje znalezienie w edytorze kodu miejsca występowania zaznaczonej pozycji w kodzie. Trzy kolejne opcje także nie powinny sprawić żadnego problemu: Add (dodaj nową), Edit (edytuj zaznaczoną), Delete (usuń zaznaczoną).

Opcje z menu umożliwiają także posortowanie (Sort) lub przefiltrowanie (Filter) wybranych elementów w oknie.
Ciekawą opcją jest natomiast kopiowanie zawartości okna w postaci czystego tekstu do schowka (Copy As => Text) lub w postaci gotowej tabeli HTML (Copy => HTML Table). Istnieje dodatkowo możliwość ustawienia właściwości tabeli, jaka będzie generowana przez Delphi. Wszystko za sprawą pozycji Table Properties (rysunek 6.15).
 

Rysunek 6.15. Okno Table Properties

W oknie właściwości tabeli można zmienić wszystkie elementy HTML generowanej tabeli — począwszy od zmiany tła poprzez wyrównanie aż po ustawienia krawędzi.

Szablony kodu


Kategoria Source Options ustawień edytora kodu, zawiera przycisk Edit Code Templates (z ang. edytuj szablony kodu). Okno zaprezentowane na rysunku 6.16 umożliwia tworzenie szablonów kodu — czyli gotowych elementów języka, które można później bardzo łatwo wykorzystać podczas pisania programu.
 

Rysunek 6.16. Okno Code Templates

Szablony można modyfikować za pomocą trzech przycisków: Add (dodaj), Edit (edytuj) i Delete (usuń). Gotowe szablony wywołuje się podczas edycji kodu, korzystając ze skrótu klawiszowego Ctrl+J. Szablony kodu zostaną wtedy wyświetlone w okienku takim samym jak Code Completion.

Istnieje też inny, szybszy sposób. Otóż podczas dodawania nowego szablonu należy określić skrót danego szablonu (ang. shortcut). Przykładowo, skrótem dla bloku begin..end jest słowo begin. Wystarczy więc, że w edytorze kodu wpiszemy begin i naciśniemy Ctrl+J, a Delphi zamieni wprowadzone słowo na szablon o tym skrócie.

Szablony się przydają — jest to dobre narzędzie. Aby jednak realnie przyspieszyć wpisywanie kodu, należy nauczyć się skrótów prowadzących do danego szablonu.

Refaktoryzacja


Nowe narzędzie w Delphi 2005, jakim jest refaktoryzacja, umożliwia zmianę struktury kodu źródłowego, co umożliwia szybkie zmiany w kodzie i jego konserwację.

W skład refaktoryzacji wchodzi kilka funkcji, które znajdują się w menu Refactor. Są to m.in. funkcje zmiany nazwy danej zmiennej, deklarowanie pól oraz zmiennych, a także wyodrębnianie metod. Przyjrzyjmy się pokrótce każdej z opcji.

Deklaracja zmiennych


Jak już wiadomo, Delphi wymaga, aby zmienne przed użyciem zostały zadeklarowane w bloku var. Czasem może się jednak zdarzyć, że projektant koncentruje się na kodzie i jego strukturze, nie przejmując się deklaracją zmiennych użytych w funkcji. Oczywiście takie zmienne mimo wszystko należy zadeklarować, lecz teraz można zrobić to szybciej, używając opcji Refactor => Declare Variable.

Spójrzmy na poniższy fragment kodu:

procedure TForm2.Button1Click(Sender: TObject);
begin
  S := 'To jest tekst';
 
  for i := 1 to 100 do
     Button1.Text := S + ': ' + IntToStr(i);
end;


Oczywiście, powyższy fragment nie ma konkretnego działania, jedynie w pętli przypisuje wartość do właściwości @@Text@@. Warto zwrócić uwagę, że zmienne @@S@@ oraz @@I@@ nie zostały zadeklarowane. Teraz należy umieścić kursor tekstowy przed zmienną @@S@@, kliknąć prawym przyciskiem myszy i z menu podręcznego wybrać Refactor => Declare Variable. W wyniku takiej operacji powinno pojawić się okno, podobne do tego z rysunku 6.17.
 

Rysunek 6.17. Okno deklaracji zmiennej

Przed zadeklarowaniem zmiennej, Delphi wyświetli zapytanie o typ zmiennej oraz ewentualną wartość domyślną. Przez zaznaczenie pozycji Set Value możemy nadać wartość domyślną dla zmiennej, a pozycja Array pozwala na określenie, czy dana zmienna będzie tablicą. Po naciśnięciu przycisku OK zmienna zostanie zadeklarowana. Tak samo można postąpić ze zmienną @@I@@.

Warto zwrócić uwagę, że Delphi potrafi określić typ zmiennej @@S@@ na podstawie danych przypisanych w ciele procedury zdarzeniowej.

Deklaracja pól


Funkcja Declare Field, podobnie jak Declare Variable, umożliwia zadeklarowanie pola klasy. Odbywa się to niemal identycznie jak deklaracja zmiennej. O polach powiem w kolejnym rozdziale.

Wyodrębnianie metod


Często używany fragment kodu można po prostu przenieść do osobnej procedury i kiedy to jest wymagane — wywoływać jedynie ową procedurę. Funkcja refaktoryzacji — Extract Method umożliwia wyodrębnianie zaznaczonego fragmentu kodu. Posłużmy się przykładem, który zaprezentowałem wcześniej. Należy zaznaczyć fragment kodu znajdujący się pomiędzy słowami begin i end. Następnie z menu podręcznego edytora kodu wybiera się Refactoring => Extract Method. W efekcie zostanie wyświetlone okno, w którym trzeba wpisać nazwę tworzonej metody (rysunek 6.18).
 

Rysunek 6.18. Wyodrębnianie metody

Po naciśnięciu przycisku OK, utworzona zostanie nowa metoda, a obecnie zaznaczony kod zostanie zastąpiony przez nazwę naszej metody, czyli np. ExtractedMethod.

Wyodrębnianie łańcucha znaków


Ponownie przywołamy poprzedni przykład, aby zaprezentować działanie kolejnej z funkcji — wyodrębniania łańcuchów zasobów. Łańcuchy zasobów przypominają zwykłe stałe, z tą różnią, że są deklarowane z użyciem słowa kluczowego resourcestring:

resourcestring
  Str1 = 'Łańcuch 1';


W odróżnieniu od stałych, łańcuchy zasobów mogą być tylko i wyłącznie ciągami znakowymi typu string. Z zasobów łańcuchowych można korzystać tak samo jak ze stałych — są one wkompilowane w plik wykonywalny .exe do tzw. zasobów programu. Zasobami programu mogą być na przykład dodatkowe obrazki, pliki muzyczne, które w prosty sposób da się włączyć do programu.

Kliknijmy prawym przyciskiem myszy w obrębie jakiegokolwiek łańcucha, a następnie z menu podręcznego wybierzmy Refactoring => Extract Resource String. Aby wyodrębnić łańcuch zasobów, należy podać jego nazwę (rysunek 6.19).
 

Rysunek 6.19. Wyodrębnianie łańcucha znaków

Spowoduje to umieszczenie w sekcji implementation następującego kodu:

resourcestring
  StrToJestTekst = 'To jest tekst';


Zmiana nazwy


Kolejną opcją refaktoryzacji jest zmiana nazwy, czy to metody, czy zmiennej. Jest to bardzo przydatna opcja w momencie, gdy chcemy zmienić nazwę np. zmiennej. W takim przypadku należy zmienić odwołania do owej zmiennej w obrębie całego programu.

Aby zmienić nazwę metody (np. ExtractedMethod), należy kliknąć ją prawym przyciskiem myszy i z menu kontekstowego wybrać Refactoring => Rename. W tym miejscu trzeba wprowadzić nową nazwę dla metody. Po naciśnięciu OK zostaną odszukane wszelkie odwołania do tej nazwy w programie, a Delphi wyświetli podsumowanie (rysunek 6.20).
 

Rysunek 6.20. Odwołania wyszukane przy użyciu refaktoryzacji

Aby dopełnić procesu zamiany, trzeba nacisnąć pierwszy przycisk Refactor. Później można także cofnąć operację, stosując przycisk Undo (drugi z lewej).

Szukanie modułu


Ostatnia opcja z menu Refactor umożliwia dodanie modułu do listy uses (Find Unit). Po wybraniu tej opcji, w okienku zostanie wyświetlona lista modułów, które można dołączyć do listy uses. Okno Find Unit pozwala także na określenie, w jakiej części ma zostać dołączony dany moduł (interface, implementation).

Synchroniczna edycja


Kolejną nowością w Delphi 2005 jest synchroniczna edycja symboli, która jest podobna do opcji zmiany nazwy w refaktoryzacji. Opcja synchronicznej edycji umożliwia masowe zmiany powtarzających się symboli w zaznaczonym fragmencie kodu. Spójrzmy na poniższy, przykładowy fragment:

procedure TForm2.Button1Click(Sender: TObject);
begin
  ExtractedMethod2;
end;
 
procedure TForm2.ExtractedMethod2;
var
  i: Integer;
  S: string;
begin
  S := StrToJestTekst;
  for i := 1 to 100 do
    Button1.Text := S + ': ' + IntToStr(i);
end;


Teraz można go zaznaczyć, a następnie odszukać charakterystyczny przycisk umieszczony na lewej belce edytora kodu. Kliknięcie go spowoduje zaznaczenie powtarzających się symboli w edytorze kodu (rysunek 6.21).
 

Rysunek 6.21. Zaznaczenie powtarzających się symboli

Teraz, zmieniając nazwę zmiennej @@S@@, można zaobserwować, że jej nazwa zostaje zamieniona także w pozostałych miejscach.

Synchroniczna edycja jest wygodną opcją, jeżeli do edycji pozostaje niewielki fragment kodu. W przypadku konieczności zmiany nazwy symbolu w kilku modułach, należy zastosować refaktoryzację.

Wyszukiwanie odwołań


Wyszukiwanie odwołań jest mechanizmem pozwalającym na wyszukanie wszelkich odwołań do danej zmiennej czy metody w obrębie całego programu czy pojedynczego modułu. W menu Search znajduje się kilka pozycji:

  • Find References — wyszukuje odwołania do danej zmiennej czy innego symbolu w obrębie całego programu.
  • Find Local References — wyszukuje odwołania do danego symbolu w obrębie pojedynczego modułu.
  • Find Declaration Symbol — wyszukuje miejsce deklaracji danego symbolu.

Opcje wyszukiwania odwołań przydają się w przypadku skomplikowanego kodu źródłowego, gdy chcemy szybko znaleźć np. miejsce deklaracji danej zmiennej. Rezultaty szukania są prezentowane w oknie Find References (rysunek 6.22).
 

Rysunek 6.22. Rezultat wyszukiwania odwołań

Rezultaty wyszukiwań prezentowane są w formie gałęzi.

Okno Find References można wyświetlić, wybierając z menu View pozycję Find References Results.

Menedżer projektu


Menedżer projektu (rysunek 6.23) służy — jak sama nazwa wskazuje — do zarządzania projektem, a raczej projektami. W Delphi istnieje bowiem możliwość pracowania nad kilkoma projektami jednocześnie.
 

Rysunek 6.23. Okno menedżera projektu

Centralna lista gałęzi zawiera nazwy projektów, nad którymi aktualnie pracujemy i umożliwia wybranie tego aktualnego. Jest tak dlatego, iż domyślnie polecenie Compile (F9) powoduje kompilację jednego aktywnego projektu. Tak więc trzeba wskazać, który jest aktywny.

Istnieje jednak pozycja Compile All w menu Project. Polecenie to umożliwia skompilowanie jednocześnie wszystkich projektów, nad którymi pracujemy.

Dany projekt aktywuje się za pomocą przycisku Activate.

Kolejne dwa przyciski, New oraz Remove, służą do tworzenia nowych oraz do usuwania zaznaczonych projektów. Po wybraniu przycisku New zostanie otwarte repozytorium. Remove — jak łatwo się domyślić — usuwa dany projekt z menedżera projektów (zamyka go, ale nie usuwa fizycznie z dysku).

W przypadku przedstawionym na rysunku 6.23 gałąź Project3.exe oznacza projekt, nad którym aktualnie pracuje programista. Wszelkie odgałęzienia oznaczają pliki wchodzące w skład tego projektu. Dodatkowo, w kategorii References znajduje się lista odwołań, czyli podzespołów .NET wykorzystywanych w tym projekcie. O odwołaniach odpowiedniego projektu informuje dyrektywa {%DelphiDotNetAssemblyCompiler} w pliku *.dpr projektu, np.:

{%DelphiDotNetAssemblyCompiler '$(SystemRoot)\microsoft.net\framework\v1.1.4322\System.Windows.Forms.dll'}


Model View


Zakładka Model View daje hierarchiczny obraz plików, komponentów oraz funkcji projektu. Opcje tego okna (prawy przycisk myszy) zapewniają możliwość edycji diagramów UML, a następnie eksportu takiego diagramu do pliku graficznego.

Data Explorer


Zakładka Data Explorer jest związana bezpośrednio z bazami danych w Delphi oraz edycją połączeń z systemami bazodanowymi. Tematem baz danych będziemy zajmować się w II części książki.

Menedżer historii


Jedną z nowości w Delphi 2005 jest menedżer historii. W trakcie pracy nad projektem, u dołu oprócz zakładki Design (projektowanie wizualne), Code (edytor kodu), powinna znaleźć się zakładka History. Zakładka ta prezentuje historię postępu prac nad danym projektem (rysunek 6.24).
 

Rysunek 6.24. Menedżer historii projektu

Każdy zapis projektu wiąże się z utworzeniem kopii w ukrytym katalogu __history.

Na samej górze okna znajduje się lista rozwijalna, wymieniająca wszystkie pliki zawarte w projekcie. Rozwinięcie listy i wybranie którejś pozycji spowoduje wyświetlenie informacji o historii danego pliku.

Jak widać, na rysunku 6.24 znalazły się dwie wersje pliku MainFrm.pas. Jedna jest opatrzona etykietą Local file (plik lokalny), a druga — Local Backup (lokalna kopia). Zaznaczenie którejś z pozycji spowoduje zaprezentowanie podglądu pliku.

Dodatkowo okno historii jest podzielone na kolejne trzy zakładki:

  • Contents — zakładka prezentuje zawartość wybranego pliku,
  • Info — zakładka prezentuje informacje oraz komentarz do danego pliku,
  • Diff — zakładka daje możliwość wyświetlenia różnić pomiędzy poszczególnymi kopiami.

Szczególnie ciekawa jest opcja prezentowania różnic pomiędzy kopiami plików (zakładka Diff).

W każdym momencie można przywrócić zapisaną kopię pliku, która znajduje się w historii. Przykładowo, jeżeli zmiany wprowadzone w ciągu kilku ostatnich godzin nie mają sensu, z powodzeniem można przywrócić poprzednie wersje pliku. Wystarczy zaznaczyć kopię, która ma zostać przywrócona. Następnie należy kliknąć zaznaczaną pozycję prawym przyciskiem myszy i wybrać opcję Revert.

Delphi przechowuje w katalogu __history 10 ostatnich kopii plików. Jest to domyślne ustawienie, które można zmienić w opcjach Delphi (Tools => Options), w kategorii Editor Options. Tam znajduje się pole File backup limit.

Test


1) W jakim pliku są przechowywane informacje odnośnie formularza VCL.NET?
a)        *.dfm,
b)        *.nfm,
c)        *.pas.
2) Jak wyłączyć kolorowanie składni w Delphi?
a)        Korzystając z pozycji Use syntax highlight na zakładce Editor Options,
b)        Korzystając z pozycji Use syntax highlight w menu podręcznym edytora,
c)        Korzystając z pozycji Use syntax highlight na zakładce Source Options.
3) Jaki skrót odpowiada za wyświetlenie listy Code Completion?
a)        Ctrl+Shift+spacja,
b)        Ctrl+spacja,
c)        Nie ma takiego skrótu.
4) Gdzie zmienić ustawienia liczby zapisywanych kopii plików źródłowych:
a)        Pozycja File backup limit w kategorii Editor Options,
b)        Pozycja File backup limit w kategorii Editor Options/History,
c)        Nie ma takiej opcji.

FAQ


Czy istnieje gotowy komponent dający takie możliwości edycji jak edytor kodu Delphi?

Tak, jest taki komponent. Trzeba go jednak pobrać z internetu i zainstalować (Component => Install Component). Po instalacji na palecie narzędzi pojawi się nowa zakładka z tym komponentem. Owa kontrolka, o której mowa, nazywa się SynEdit.

Spacja powoduje usuwanie tekstu — a nie przesuwanie. Co zrobić?

Powodem może być zła konfiguracja. Aby to szybko zmienić, należy użyć klawisza Insert. Dolny pasek w edytorze kodu sygnalizuje dany stan — albo Insert (przesuwanie tekstu w miarę wpisywania nowych znaków), albo Overwrite (zmiana istniejącego tekstu w miarę wpisywania nowego).

Podsumowanie


Po lekturze tego rozdziału Czytelnik powinien zyskać o wiele lepsze rozeznanie odnośnie do edycji kodu źródłowego w Delphi. Zaprezentowałem opcje edytora kodu oraz technologie, które z pewnością przydadzą się w trakcie pisania kodu (np. Code Insight). W tym momencie nie powinniśmy mieć już problemów z generowaniem zdarzeń czy pracą z komponentami. Można także używać refaktoryzacji do lepszego zarządzania swoim projektem.

[1] Opcje Use tab character oraz Smart tab wykluczają się wzajemnie — obie nie mogą być zaznaczone jednocześnie.




© Helion 2005. Autor: Adam Boduch. Zabrania się rozpowszechniania tego tekstu bez zgody autora.

1 komentarz

Iv3x 2011-02-27 12:52

Imponujące