Sign in Join
Wiki copytemporary

Codetools/ru

Deutsch · English · Français · Русский

Что такое codetools?

Codetools - это пакет Lazarus, предоставляющий инструменты для анализа, изучения, редактирования и рефакторинга исходных кодов Pascal. Codetools был упакован в своем собственном модуле и лицензирован под лицензией GPL. Многие примеры, показывающие, как использовать codetools в ваших собственных программах, можно найти в components/codetools/examples.

svn:

Использование codetools без IDE

Вы можете использовать codetools без IDE. Это может быть использовано для тестирования нового инструмента. Простой пример

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

Чтобы проверить декларацию find, codetools необходимо проанализировать исходники. Особенно исходники RTL и FCL. В примерах используются следующие переменные среды:

FPC - очень сложный проект с множеством путей поиска, включающих файлы и макросы. Codetools должен знать все эти пути и макросы для анализа этих джунглей. Чтобы легко все это настроить, codetools содержит предопределенные шаблоны для исходных каталогов FPC, Lazarus, Delphi и Kylix.

Смотрите пример объявления поиска

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

Поскольку исходники FPC содержат несколько версий некоторых модулей, и исходники FPC часто меняются, codetools не используют фиксированную таблицу путей, а скорее сканируют всю структуру каталогов FPC при первом использовании, применяя набор правил, определяющих, что это - правильный исходник для текущих TargetOS и TargetCPU. Это сканирование может занять некоторое время в зависимости от скорости вашего диска. Все примеры сохраняют результат в codetools.config, так что при следующем запуске Lazarus это возможно длительное сканирование будет пропущено.

Когда бы ни изменялись исходники FPC или переименовывалось устройство, просто удалите файл codetools.config. IDE Lazarus имеет свой собственный файл конфигурации и выполняет повторное сканирование всякий раз, когда изменяется исполняемый файл компилятора (или когда пользователь вызывает 'Tools > Rescan FPC source directory').

Задание путей поиска и макросов

Codetools использует [конструкцию] "define templates" (шаблоны определений) для генерации путей поиска и макросов. Шаблон определений является деревом правил.

Вот пример того, как расширить глобальный путь включения и запросить путь включения каталога:

pascal
uses
  Classes, SysUtils, CodeToolManager, DefineTemplates, FileProcs;
  
var
  Directory: String;
  IncPathTemplate: TDefineTemplate;
begin
  Directory:=ExpandFileNameUTF8(GetCurrentDirUTF8);

  // добавление подшаблона для расширения включающего пути поиска #IncPath.
  IncPathTemplate:=TDefineTemplate.Create(
    'Add myincludes to the IncPath',  // имя шаблона, полезно для его поиска в дальнейшем (необязательно)
    'Add /tmp/myincludes to the include search path', // описание (необязательно)
    IncludePathMacroName,  // имя переменной: #IncPath
    '$('+IncludePathMacroName+');/tmp/myincludes' // новое значение: $(#IncPath);/tmp/myincludes
    ,da_DefineRecurse
    );
  // добавляем шаблон включающего пути в дерево
  CodeToolBoss.DefineTree.Add(IncPathTemplate);

  writeln('Directory="',Directory,'"',
    ' IncPath="',CodeToolBoss.GetIncludePathForDirectory(Directory),'"');
end.

Шаблон определений макроса

Макросы, заданные [при помощи] шаблона определений, используются синтаксическим анализатором Pascal. В Codetools есть функции, позволяющие запрашивать у Free Pascal Compiler свои макросы и преобразовывать их для шаблонов определений. Это делается [с помощью] CodeToolBoss.Init(Options);, который вы можете найти во многих примерах.

Кроме того, есть несколько предопределенных макросов, которые начинаются с хеша (#). См. модуль definetemplates для [получения] полного списка. Вот несколько важных [из них]:

pascal
  ExternalMacroStart = '#';
  // Стандартные макросы
  DefinePathMacroName = ExternalMacroStart+'DefinePath'; // текущий каталог
  UnitPathMacroName = ExternalMacroStart+'UnitPath'; // путь поиска объекта, разделенный точкой с запятой (такой же, как для FPC)
  IncludePathMacroName = ExternalMacroStart+'IncPath'; // путь поиска включаемого файла, разделенный точкой с запятой (такой же, как для FPC)
  SrcPathMacroName = ExternalMacroStart+'SrcPath'; // путь поиска исходника модуля, разделенный точкой с запятой (не указывается для FPC)

Например, IncludePathMacroName - это #IncPath и используется для определения пути поиска включаемого файла. Имейте в виду, что значения макросов зависят от каталога.

Значения шаблонов определений могут содержать макросы. Макрос должен быть заключен в [символ] доллара и скобки: $(macro).

В приведенном выше примере значение было '$('+IncludePathMacroName+');/tmp/myinclude', которое эквивалентно '$(#IncPath);/tmp/myinclude', и которое выполняется для старого включаемого пути поиска + ;/tmp/myinclude, что означает добавление пути. Для удобства чтения вы можете использовать константу IncludePathMacro вместо '$('+IncludePathMacroName+')':

pascal
  DefinePathMacro          = '$('+DefinePathMacroName+')'; // путь к определенному шаблону
  UnitPathMacro            = '$('+UnitPathMacroName+')';
  IncludePathMacro         = '$('+IncludePathMacroName+')';
  SrcPathMacro             = '$('+SrcPathMacroName+')';

Шаблон определений действий

Шаблон определений имеет имя, необязательное описание, переменную, значение и действие. Имя и описание не являются обязательными. Значение переменной и значение [шаблона определений] зависит от действия. Вы можете добавить шаблоны определения как дочерние элементы шаблонов определения - создавая дерево шаблонов определения. Вы можете увидеть много примеров для определения шаблонов в диалоговом окне Lazarus'а Tools / CodeTools Defines Editor.

Как расширить включаемый путь каталога

См. пример lazarus/components/codetools/examples/setincludepath.lpr. Он демонстрирует использование относительных путей, абсолютных путей, как использовать DefinePathMacro и разницу между da_Define и da_DefineRecurse.

Использование codetools в IDE с IDEIntf

См. пакет <lazarusdir>/examples/idequickfix/quickfixexample.lpk. Он демонстрирует:

Правила Codetools для исходников FPC

Когда codetools ищет исходник FPC-процессора, он использует набор правил. Вы можете написать свои собственные правила, но обычно вы будете использовать стандартные правила, которые определены в include-файле components/codetools/fpcsrcrules.inc. Вы можете проверить правила с помощью утилиты командной строки: components/codetools/examples/testfpcsrcunitrules.lpi.

Использование testfpcsrcunitrules

pascal
Usage: lazarus/components/codetools/examples/testfpcsrcunitrules -h
pascal
-c <compiler file name>, --compiler=<compiler file name>
       ???? ?????????????????? ???????????????????????? ???????????????????? ?????????????????? PP.
       ???????? ?????? ???? ??????????????????????, ?????????? fpc
pascal
-T <target OS>, --targetos=<target OS>
       ???? ?????????????????? ???????????????????????? ???????????????????? ?????????????????? FPCTARGET.
       ???????? ?????? ???? ??????????????????????, ?????????????????????? ???????????????????? ???? ??????????????????.
pascal
-P <target CPU>, --targetcpu=<target CPU>
       ???? ?????????????????? ???????????????????????? ???????????????????? ?????????????????? FPCTARGETCPU.
pascal
?????????????????????????? ?????? ???? ??????????????????????, ?????????????????????? ???????????????????? ???? ??????????????????.
pascal
-F <FPC source directory>, --fpcsrcdir=<FPC source directory>
       ???? ?????????????????? ???????????????????????? ???????????????????? ?????????????????? FPCDIR.
pascal
?????????????????????????????????? ???? ?????????????????? ??????.
pascal
-u <unit name>, --checkunit=<unit name>
       ???????????????? ?????????????????? ?????????? ???? ???????? ????????????.

Пример для testfpcsrcunitrules

Откройте testfpcsrcunitrules.lpi в IDE и скомпилируйте его. Затем запустите утилиту в терминале/консоли:

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

Это скажет вам, какой компилятор используется, какой компилятор выполняется, какие файлы конфигурации были протестированы и проанализированы, и предупредит вас о дублирующих модулях в пути поиска FPC и о дублировании исходных файлов модулей.

Note: В этом примере результаты кэшируются в файле codetools.config. Вы должны удалить codetools.config при обновлении компилятора или файла fpc.cfg.

Дубликаты исходных файлов

[Допустим,] вы обнаруживаете, что codetools открывает для целевого wince/arm неправильный исходник модуля mmsystem. Запустите инструмент с параметром -u:

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

Это даст вам подробный отчет о том, где был найден этот модуль и какую оценку получил каждый исходный файл. Например:

Unit report for mmsystem

text
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

Это означает, что есть два исходных файла с одинаковым счетом, поэтому инструменты кодинга взяли первый. Последний в модулях wince предназначен для целевой [платформы] wince, а первый - для win32 и win64.

Теперь откройте файл правил fpcsrcrules.inc.

Правила работают так:

pascal
Score:=10;
Targets:='wince';
Add('packages/winceunits');

Add добавляет правило для всех файлов, начинающихся с 'packages/winceunits', которое добавляет 10 баллов ко всем этим файлам. Targets - это список целевых операционных систем и/или целевых процессоров через запятую. Например, Targets='wince,linux,i386' означает: применить эти правила к TargetOS wince или linux и ко всем TargetCPU i386.

Как codetools анализирует источники по-другому, нежели компилятор

Компилятор оптимизирован для линейного анализа кода и загрузки необходимых модулей и включаемых файлов, как только он анализирует секцию uses или директиву. Codetools оптимизирован для анализа только определенных разделов кода. Например, для перехода от объявления метода к телу метода нужен только модуль и его включаемые файлы. Когда codetool ищет объявление, он ищет в обратном направлении. Это означает, что он начинает поиск в локальных переменных, а затем вверх от [секции] implementation. Когда он находит секцию uses, он ищет идентификаторы в секции interface модулей. Когда идентификатор найден, он останавливается. Результат и некоторые промежуточные шаги кэшируются. Поскольку часто требуется проанализировать только несколько разделов интерфейса, он очень быстро находит индивидуальный идентификатор.

Codetools анализируют источник не за один шаг (как это делает компилятор), а за несколько шагов, которые зависят от того, что нужно текущему codetool:

Каждый уровень имеет свои собственные кэши, которые необходимо проверять и обновлять перед вызовом функции. Многие функции высокого уровня, доступные через CodeToolBoss, делают это автоматически. Для других ответственность за это лежит на [том, кто их] вызывает.

Пример для:

unit1.pas:

pascal
unit Unit1;
{$I settings.inc}
interface
uses
  {$IFDEF Flag}
  unix,
  {$ELSE}
  windows,
  {$ENDIF}
  Classes;

settings.inc:

pascal
{%MainUnit unit1.pas}
{$DEFINE Flag}

очищенный исходник:

pascal
unit Unit1;
{$I settings.inc}{%MainUnit unit1.pas}
{$DEFINE Flag}
interface
uses
  {$IFDEF Flag}
  unix,
  {$ELSE}{$ENDIF}
  Classes;

Подсказка: чтобы было легко разобрать модуль и построить узлы, используйте CodeToolBoss.Explore.

CleanPos и CursorPos

Есть несколько методов для определения позиции в codetools.

Абсолютная позиция codetools связана с исходником как непрерывная строка, начинающаяся с символа 1. Например, TCodeBuffer хранит содержимое файла как одну строку в своем свойстве Source. Позиции каретки или курсора задаются как X,Y, где X - номер столбца, а Y - номер строки. Каждое значение (X и Y) начинается с 1. TCodeBuffer предоставляет функции-члены LineColToPosition и AbsoluteToLineCol для преобразования между значениями (X, Y) и абсолютной позицией codetools. При работе с несколькими исходными файлами (например, модулем, который может содержать несколько включаемых файлов), чистая позиция относится к абсолютной позиции в разобранном коде Src. Здесь Src - строка, чья чистая позиция начинаются с 1. Позиция курсора указывается как TCodeXYPosition (Code,X,Y). TCodeTool предоставляет функции CaretToCleanPos и CleanPosToCaret для преобразования между ними.

Вставка, удаление, замена - TSourceChangeCache

При внесении изменений в исходный код модуля (или его включаемых файлов) вы должны использовать [функцию] CodetoolBoss.SourceChangeCache вместо непосредственного изменения исходника.

Использование

SourceChangeCache работает с модулем, поэтому вам нужно получить TCodeTool и отсканировать модуль/include-файл. Например:

pascal
  // Шаг 1: загружаем файл и парсим его
  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 ...

  // Шаг 2: подключаем SourceChangeCache
  CodeToolBoss.SourceChangeCache.MainScanner:=Tool.Scanner;

  // Шаг 3: используем Replace для вставки и/или удаления кода
  // Первые два параметра - это необходимые пробелы спереди и позади вставки
  // FromPos, ToPos определяют удаляемый/заменяемый диапазон в позиции CleanPos.
  // NewCode - это строка нового кода. Используем '' для удаления.
  if not CodeToolBoss.SourceChangeCache.Replace(gtNone,gtNone,FromPos,ToPos,NewCode) then
    exit; // например, исходник [оказался] только для чтения или шаблон Replace удалил место
  ...?????? ?????????????? ?????????????????? Replace...

  // Шаг 4: Применяем изменения
  if not CodeToolBoss.SourceChangeCache.Apply then
    exit; // применение было прервано

BeginUpdate/EndUpdate

BeginUpdate/EndUpdate задерживают [немедленное] применение [изменений]. Это полезно при объединении нескольких функций изменения кода. Например:

Вы хотите отсканировать модуль, добавить модуль в секцию uses раздела interface, удалив [его] из секции uses раздела implementation. Две функции AddUnitToMainUsesSection и RemoveUnitFromUsesSection используют [метод] Apply, изменяя исходник, поэтому вторая функция будет повторно сканировать модуль во второй раз. Но так как эти две функции не зависят друг от друга (они изменяют разные части источника), вы можете объединить их и сделать это за одно сканирование:

pascal
  // Шаг 1: парсим модуль  и подключаем SourceChangeCache
  if not CodeToolBoss.Explore(Code,Tool,false) then
    ...;// ошибка парсинга ...
  CodeToolBoss.SourceChangeCache.MainScanner:=Tool.Scanner;

  // Шаг 2: задерживаем [применение метода] Apply
  CodeToolBoss.SourceChangeCache.BeginUpdate;

  // Шаг 3: добавляем модуль в секцию uses раздела interface 
  // AddUnitToMainUsesSection будет применяться и изменять код
  // Из-за BeginUpdate изменение еще не сделано, но сохранено в SourceChangeCache
  if not Tool.AddUnitToMainUsesSection('Classes','',CodeToolBoss.SourceChangeCache) then exit;

  // Шаг 4: удаляем модуль из секции uses раздела implementation 
  // Без BeginUpdate RemoveUnitFromUsesSection будет повторно сканировать модуль
  if Tool.FindImplementationUsesSection<>nil then
    if not Tool.RemoveUnitFromUsesSection(Tool.FindImplementationUsesSection,'Classes',CodeToolBoss.SourceChangeCache) then exit;

  // Шаг 5: применяем все изменения
  if not CodeToolBoss.SourceChangeCache.EndUpdate then
    exit; // применение было прервано

BeginUpdate/EndUpdate работают со счетчиком, поэтому, если вы вызываете BeginUpdate дважды, вам нужно дважды вызывать EndUpdate. Это означает, что вы можете поместить приведенный выше пример в функцию и объединить ее с другой функцией.

Сохранение изменений на диск

Вышеуказанные изменения вносятся в буферы кода, и буферы помечаются как измененные. Чтобы сохранить изменения на диске, вы должны вызвать Save для каждого измененного буфера.

Подсказки/Советы/Руководства

pascal
try
  Tool.BuildTree(lsrEnd);
  ... ?????????? ... ????????????  
except
  on E: Exception do
    CodeToolBoss.HandleException(E);
end;

Ссылки


From the Free Pascal wiki, page "Codetools/ru", as it stood on 2020-02-11 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 3, last changed by WikiArchive .

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.