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.cfgCode 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.pasCode 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.

Before formating

Open the example for editing and click the “Format code” button (1), and then “Save” (2).

Now the source looks much neater:

After formating

Links

Leave a Reply

Your email address will not be published. Required fields are marked *