В программе всё должно быть прекрасно: и алгоритмы, и выравнивание исходного текста.

Эйс Брейкпоинт

Сначала может показаться, что форматирование исходного текста придумали зануды, но на самом деле выровненный по определенным правилам текст легче воспринимается, а значит – на его изучение тратится меньше времени. А время, как вы знаете, – самый ценный ресурс.

Так как уже написано множество платных и бесплатных утилит форматирования, я решил не изобретать велосипед, а выбрать готовый.

После нескольких экспериментов мне удалось найти оптимальный вариант: утилита ptop.exe, которая входит в состав FreePascal.

После нас хоть PTOP

Для того чтобы добраться до этой утилиты, необходимо на сайте разработчиков скачать файл установки, подходящий для вашей операционной системы. Если вы не планируете использовать Free Pascal, то при инсталляции выберите только опцию установки утилит.

После завершения установки перенесите файл ptop.exe поближе к вашему проекту (для себя я решил, что все внешние утилиты будут храниться в папке extras\, в подпапке с названием утилиты).

Чтобы сгенерировать файл конфигурации, запустите утилиту в командной строке с параметром -g, указав имя создаваемого файла конфигурации.

ptop.exe -g ptop.cfgCode language: CSS (css)

Можно его и не создавать, но лично мне не понравилось, что ptop.exe делает капитализацию (меняет первый символ на заглавный) всех ключевых слов, и чтобы это отменить, надо прошерстить файл конфигурации, удалив слово capital в описаниях действий для всех ключевых слов.

Для форматирования файла-исходника c перезаписью оригинального файла потребуется выполнить такую вот команду:

ptop.exe -g ptop.cfg source.pas source.pasCode language: CSS (css)

Модуль форматирования

Хотя для форматирования понадобится написать всего один небольшой скрипт, я решил выделить его в отдельный модуль, отнеся его к инструментам (папка Script\Tools\Formetter\Formattrer.pas).

const
  FORMATTER_EXE = 'Extras\FreeFormat\ptop.exe';
  FORMATTER_CONFIG = 'Extras\FreeFormat\ptop.cfg';
  FORMATTER_PARAM = '-c %0.s %1.s %2.s '; // цифры роли не играю, параметры по очереди подставляются

function Formatter_FormatText( AText:string ):string;
// форматирование исходного текста Delphi внешней утилитой
var
  tmpDataFile: string;
  tmpConfigFile: string;
  tmpFormatter: string;
  tmpMemo: TMemo;
  tmpParam: string;
begin
  // утилита и файл конфигурации лежат в специальной папке внутри нашего проекта
  tmpFormatter := ExtractFilePath( Application.ExeName ) + FORMATTER_EXE;
  tmpConfigFile := ExtractFilePath( Application.ExeName ) + FORMATTER_CONFIG;
  // если чего-то не нашли, то возбуждаем исключение
  if not FileExists(tmpFormatter) then
    RaiseException('Утилита форматирования не найдена: '+tmpFormatter)
  else
  if (FORMATTER_CONFIG <> '') and  not FileExists(tmpConfigFile) then
    RaiseException('Файл конфигурации не найден: '+tmpConfigFile)
  else
  begin
    // создаем временный файл
    tmpDataFile := GetTempFileName+'.pas';
    // используем Memo для создания файла
    tmpMemo := TMemo.Create(MainForm);
    tmpMemo.Parent := MainForm; // нужно, чтобы работало сохранение на диск
    tmpMemo.Visible := False;
    try
      tmpMemo.Lines.Text := AText;
      tmpMemo.Lines.SaveToFile(tmpDataFile); // сохраняем текст во временный файл
      // делаем конвертацию
      tmpParam := Format(FORMATTER_PARAM,[tmpConfigFile,tmpDataFile,tmpDataFile]);
      OpenFile(tmpParam,tmpFormatter);
      case WaitExternalExe( tmpFormatter ) of
      -1: RaiseException('Утилита форматирования не запустилась: '+tmpFormatter);
      -2: RaiseException('Утилита форматирования зависла: '+tmpFormatter)
      end;
      tmpMemo.Lines.Clear; // загружаем результат
      tmpMemo.Lines.LoadFromFile(tmpDataFile);
      Result := tmpMemo.Lines.Text;
    finally
      tmpMemo.Free;
    end;
  end;
end;
Code language: Delphi (delphi)

Функция Formatter_FormatText() вызывает внешнюю утилиту для выполнения форматирования текста. Настройка утилиты сводится к настройке трёх констант: в двух из них хранится относительный путь к самой утилите и файлу конфигурации, а в третьей находится строка с описанием параметров вызова утилиты, которая рассчитана на использование стандартной команды Format(). Эта команда производит подстановку значений различных типов в строку шаблона. В нашем случае подставляются три строковых параметра: файл конфигурации, исходный файл и файл результата.

Отдельное внимание стоит уделить скрипту, который можно считать универсальным. Это функция WaitExternalExe() – ожидание завершения внешней программы. Принцип её работы заключается в том, что в течение заданного времени ожидается запуск программы с определенным заголовком, а затем скрипт ждёт её завершение.

function WaitExternalExe( ACaption:string; AStartCount: integer = 10; AFinishCount:integer = 0; ACountDelay:integer = 10 ):integer;
// ожидание завершения работы внешней программы
// используется поиск по заголовку окна
//
// ACaption - заголовок окна
// AStartCount - число циклов ожидания запуска программы
// AFinishCount - число циклов ожидания завершения программы; 0 - бесконечный цикла ожидания;
// ACountDelay - задержка внутри цикла ожидания, мС
var
  h: integer; // дескриптор окна
  tmpCounter: integer;
begin
  tmpCounter := AStartCount;
  // ожидание открытия
  // ищем окно
  while (h = 0) and (tmpCounter > 0) do
  begin
    Sleep(ACountDelay);
    h := FindWindow('', ACaption);
    Dec(tmpCounter);
  end;
  if h = 0 then
    Result := -1 // программа не запустилась
  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 // программа отработала
    else
      Result := -2; // программа зависла
  end;
end;Code language: Delphi (delphi)

В случае успеха функция возвращает 0, другие значения означают ошибки. Эти ошибки должны разбираться в том месте, откуда вызывается данная функция. В нашем случае – в Formatter_FormatText(). Но так как эта функция также является библиотечной, то для отображения ошибок я не использую в ней функции ShowMessage() и ей подобные, а вызываю исключение, перенося ответственность за обработку ошибок на скрипты более высокого уровня. В нашем случае вызов функции форматирования прикладного уровня выглядит так:

procedure efmExample_btnFormatCode_OnClick (Sender: TObject; var Cancel: boolean);
// форматирование кода
begin
  try
    // вызываем функцию форматирования
    efmExample.memSnippet.Text := Formatter_FormatText( efmExample.memSnippet.Text );
  except
    // в случае ошибки Formatter_FormatText() создаёт исключения,
    // которые можно перехватить и отобразить текст исключения
    ShowMessage( ExceptionMessage );
  end;
end;Code language: Delphi (delphi)

Результат

Теперь можно не утруждать себя ручным выравниванием примеров, взятых из своих исходников или форума.

До форматирования

Открываем пример на редактирование и нажимаем кнопку “Форматировать код” (1), а затем “Сохранить” (2).

Теперь исходник выглядит гораздо аккуратней:

После форматирования

Ссылки

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *