Everything in the program should be perfect: algorithms and source text alignment.
Ace Breakpoint
At first it may seem that the formatting of the source text was invented by bores, but in fact, text aligned according to certain rules is easier to perceive, which means less time is spent studying it. And time, as you know, is the most valuable resource.
Since there are many paid and free formatting utilities already written, I decided not to reinvent the wheel, but to choose a ready-made one.
After a few experiments, I managed to find the best option: the ptop.exe utility, which is included with FreePascal.
After Us – Through The PTOP*
*In Russian, this is a play on words: “потоп” (flood) – PTOP
In order to get to this utility, you need to download the installation file suitable for your operating system from the developers’ website. If you do not plan to use Free Pascal, then during installation select only the install utilities option.
After the installation is complete, move the ptop.exe file closer to your project (for myself, I decided that all external utilities will be stored in the extras\ folder, in a subfolder with the name of the utility).
To generate a configuration file, run the utility on the command line with the -g option, specifying the name of the configuration file to be generated.
ptop.exe -g ptop.cfg
Code language: CSS (css)
You don’t have to create it, but I personally didn’t like that ptop.exe does capitalization (changes the first character to capitalization) of all keywords, and to undo this, you need to go through the configuration file, deleting the word capital in action descriptions for all keywords.
To format the source file and overwrite the original file, you need to run the following command:
ptop.exe -g ptop.cfg source.pas source.pas
Code language: CSS (css)
Formatting module
Although you only need to write one small script for formatting, I decided to separate it into a separate module, referring it to the tools (Script\Tools\Formetter\Formattrer.pas folder).
const
FORMATTER_EXE = 'Extras\FreeFormat\ptop.exe';
FORMATTER_CONFIG = 'Extras\FreeFormat\ptop.cfg';
FORMATTER_PARAM = '-c %0.s %1.s %2.s '; // numbers do not play a role, the parameters are substituted in turn
function Formatter_FormatText( AText:string ):string;
// formatting Delphi source text with an external utility
var
tmpDataFile: string;
tmpConfigFile: string;
tmpFormatter: string;
tmpMemo: TMemo;
tmpParam: string;
begin
// the utility and the configuration file are in a special folder inside our project
tmpFormatter := ExtractFilePath( Application.ExeName ) + FORMATTER_EXE;
tmpConfigFile := ExtractFilePath( Application.ExeName ) + FORMATTER_CONFIG;
// if something is not found, then raise an exception
if not FileExists(tmpFormatter) then
RaiseException('Format utility not found: '+tmpFormatter)
else
if (FORMATTER_CONFIG <> '') and not FileExists(tmpConfigFile) then
RaiseException('Configuration file not found: '+tmpConfigFile)
else
begin
// create a temporary file
tmpDataFile := GetTempFileName+'.pas';
// use memo to create a file
tmpMemo := TMemo.Create(MainForm);
tmpMemo.Parent := MainForm; // this property must be specified for saving to disk to work
tmpMemo.Visible := False;
try
tmpMemo.Lines.Text := AText;
tmpMemo.Lines.SaveToFile(tmpDataFile); // save text to temporary file
// do the conversion
tmpParam := Format(FORMATTER_PARAM,[tmpConfigFile,tmpDataFile,tmpDataFile]);
OpenFile(tmpParam,tmpFormatter);
case WaitExternalExe( tmpFormatter ) of
-1: RaiseException('The formatting utility did not start: '+tmpFormatter);
-2: RaiseException('Formatting utility stuck: '+tmpFormatter)
end;
tmpMemo.Lines.Clear; // loading the result
tmpMemo.Lines.LoadFromFile(tmpDataFile);
Result := tmpMemo.Lines.Text;
finally
tmpMemo.Free;
end;
end;
end;
Code language: Delphi (delphi)
The Formatter_FormatText() function calls an external utility to perform text formatting. Setting up the utility comes down to setting three constants: two of them store the relative path to the utility itself and the configuration file, and the third contains a line describing the parameters of the utility call, which is designed to use the standard Format() command. This command substitutes values of various types into a template string. In our case, three string parameters are substituted: configuration file, source file, and result file.
Special attention should be paid to the script, which can be considered universal. This is the function WaitExternalExe() – waiting for the completion of an external program. The principle of its operation is that the program with a certain header is expected to start for a specified time, and then the script waits for its completion.
function WaitExternalExe( ACaption:string; AStartCount: integer = 10; AFinishCount:integer = 0; ACountDelay:integer = 10 ):integer;
// waiting for the external program to finish
// search by window title is used
//
// ACaption - window caption
// AStartCount - the number of cycles of waiting for the program to start
// AFinishCount - number of waiting cycles for program completion; 0 - infinite waiting loop;
// ACountDelay - delay inside the waiting cycle, ms
var
h: integer; // window handle
tmpCounter: integer;
begin
tmpCounter := AStartCount;
// waiting for opening
// looking for a window
while (h = 0) and (tmpCounter > 0) do
begin
Sleep(ACountDelay);
h := FindWindow('', ACaption);
Dec(tmpCounter);
end;
if h = 0 then
Result := -1 // the program didn't start
else
begin
tmpCounter := AFinishCount;
while (h <> 0) and (tmpCounter >= 0) do
begin
Sleep(ACountDelay);
h := FindWindow('', ACaption);
if AFinishCount > 0 then
Dec(tmpCounter);
end;
if h = 0 then
Result := 0 // program completed
else
Result := -2; // the program is not responding
end;
end;
Code language: Delphi (delphi)
If successful, the function returns 0, other values mean errors. These errors should be dealt with in the place where this function is called from. In our case, in Formatter_FormatText(). But since this function is also a library function, I do not use the ShowMessage() functions and the like in it to display errors, but throw an exception, transferring responsibility for error handling to higher-level scripts. In our case, the call to the application layer formatting function looks like this:
procedure efmExample_btnFormatCode_OnClick(Sender: TObject; var Cancel: boolean);
// code formatting
begin
try
// call the formatting function
efmExample.memSnippet.Text := Formatter_FormatText( efmExample.memSnippet.Text );
except
// in case of an error, Formatter_FormatText() throws exceptions,
// which can be caught and display the text of the exception
ShowMessage( ExceptionMessage );
end;
end;
Code language: Delphi (delphi)
Result
Now you don’t have to bother manually aligning examples taken from your sources or the forum.
Open the example for editing and click the “Format code” button (1), and then “Save” (2).
Now the source looks much neater:
Links
- 1.3 beta developer guide
- Project source files (available to library subscribers).