Difference between revisions of "Codetools/ru"

From Lazarus wiki
Jump to navigationJump to search
m (Fixed syntax highlighting; deleted category included in page template)
 
(16 intermediate revisions by 2 users not shown)
Line 38: Line 38:
 
Вот пример того, как расширить глобальный путь включения и запросить путь включения каталога:
 
Вот пример того, как расширить глобальный путь включения и запросить путь включения каталога:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
uses
 
uses
 
   Classes, SysUtils, CodeToolManager, DefineTemplates, FileProcs;
 
   Classes, SysUtils, CodeToolManager, DefineTemplates, FileProcs;
Line 70: Line 70:
 
Кроме того, есть несколько предопределенных макросов, которые начинаются с хеша <tt>(#)</tt>. См. модуль <tt>definetemplates</tt> для [получения] полного списка. Вот несколько важных [из них]:
 
Кроме того, есть несколько предопределенных макросов, которые начинаются с хеша <tt>(#)</tt>. См. модуль <tt>definetemplates</tt> для [получения] полного списка. Вот несколько важных [из них]:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
   ExternalMacroStart = '#';
 
   ExternalMacroStart = '#';
 
   // Стандартные макросы
 
   // Стандартные макросы
Line 86: Line 86:
 
Для удобства чтения вы можете использовать константу '''IncludePathMacro''' вместо '' '$('+IncludePathMacroName+')' '':
 
Для удобства чтения вы можете использовать константу '''IncludePathMacro''' вместо '' '$('+IncludePathMacroName+')' '':
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
   DefinePathMacro          = '$('+DefinePathMacroName+')'; // путь к определенному шаблону
 
   DefinePathMacro          = '$('+DefinePathMacroName+')'; // путь к определенному шаблону
 
   UnitPathMacro            = '$('+UnitPathMacroName+')';
 
   UnitPathMacro            = '$('+UnitPathMacroName+')';
Line 131: Line 131:
 
Когда codetools ищет исходник FPC-процессора, он использует набор правил. Вы можете написать свои собственные правила, но обычно вы будете использовать стандартные правила, которые определены в include-файле components/codetools/fpcsrcrules.inc. Вы можете проверить правила с помощью утилиты командной строки: components/codetools/examples/testfpcsrcunitrules.lpi.
 
Когда codetools ищет исходник FPC-процессора, он использует набор правил. Вы можете написать свои собственные правила, но обычно вы будете использовать стандартные правила, которые определены в include-файле components/codetools/fpcsrcrules.inc. Вы можете проверить правила с помощью утилиты командной строки: components/codetools/examples/testfpcsrcunitrules.lpi.
  
==Usage of testfpcsrcunitrules==
+
==Использование testfpcsrcunitrules==
  
 
<pre>
 
<pre>
Line 137: Line 137:
  
 
   -c <compiler file name>, --compiler=<compiler file name>
 
   -c <compiler file name>, --compiler=<compiler file name>
         Default is to use environment variable PP.
+
         По умолчанию используется переменная окружения PP.
         If this is not set, search for fpc
+
         Если это не установлено, ищите fpc
  
 
   -T <target OS>, --targetos=<target OS>
 
   -T <target OS>, --targetos=<target OS>
         Default is to use environment variable FPCTARGET.
+
         По умолчанию используется переменная окружения FPCTARGET.
         If this is not set, use the default of the compiler.
+
         Если это не установлено, используйте компилятор по умолчанию.
  
 
   -P <target CPU>, --targetcpu=<target CPU>
 
   -P <target CPU>, --targetcpu=<target CPU>
         Default is to use environment variable FPCTARGETCPU.
+
         По умолчанию используется переменная окружения FPCTARGETCPU.
        If this is not set, use the default of the compiler.
+
         Если это не установлено, используйте компилятор по умолчанию.
  
 
   -F <FPC source directory>, --fpcsrcdir=<FPC source directory>
 
   -F <FPC source directory>, --fpcsrcdir=<FPC source directory>
         Default is to use environment variable FPCDIR.
+
         По умолчанию используется переменная окружения FPCDIR.
        There is no default.
+
         Значения по умолчанию нет.
  
 
   -u <unit name>, --checkunit=<unit name>
 
   -u <unit name>, --checkunit=<unit name>
         Write a detailed report about this unit.
+
         Напишите подробный отчет об этом модуле.
 
</pre>
 
</pre>
  
==Example for testfpcsrcunitrules==
+
==Пример для testfpcsrcunitrules==
  
Open the testfpcsrcunitrules.lpi in the IDE and compile it. Then run the utility in a terminal/console:
+
Откройте testfpcsrcunitrules.lpi в IDE и скомпилируйте его. Затем запустите утилиту в терминале/консоли:
 
  ./testfpcsrcunitrules -F ~/fpc/sources/2.5.1/fpc/
 
  ./testfpcsrcunitrules -F ~/fpc/sources/2.5.1/fpc/
  
This will tell you which compiler is used, which compiler executes, which config files were tested and parsed, and it warns you about duplicate units in the FPC search path and about duplicated unit source files.
+
Это скажет вам, какой компилятор используется, какой компилятор выполняется, какие файлы конфигурации были протестированы и проанализированы, и предупредит вас о дублирующих модулях в пути поиска FPC и о дублировании исходных файлов модулей.
'''Note:''' This example caches results in the file ''codetools.config''. You should delete ''codetools.config'' when you update the compiler or your fpc.cfg file.
 
  
==Duplicate source files==
+
{{Note| В этом примере результаты кэшируются в файле ''codetools.config''. Вы должны удалить ''codetools.config'' при обновлении компилятора или файла fpc.cfg.}}
  
You find out that the codetools opens for target wince/arm the wrong source of the unit ''mmsystem''. Run the tool with the -u parameter:
+
==Дубликаты исходных файлов==
  
./testfpcsrcunitrules -F ~/fpc/2.5.1/fpc/ -T wince -P arm -u mmsystem
+
[Допустим,] вы обнаруживаете, что codetools открывает для целевого wince/arm неправильный исходник модуля ''mmsystem''. Запустите инструмент с параметром -u:
  
This will give you a detailed report where this unit was found and what score each source file got. For example:
+
<syntaxhighlight lang=pascal>./testfpcsrcunitrules -F ~/fpc/2.5.1/fpc/ -T wince -P arm -u mmsystem</syntaxhighlight>
  
Unit report for mmsystem
+
Это даст вам подробный отчет о том, где был найден этот модуль и какую оценку получил каждый исходный файл. Например:
  WARNING: mmsystem is not in PPU search path
+
 
 +
Unit report for mmsystem
 +
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/winunits-base/src/mmsystem.pp Score=11
 
  GatherUnitsInFPCSources UnitName=mmsystem File=packages/winceunits/src/mmsystem.pp Score=11 => duplicate
 
  GatherUnitsInFPCSources UnitName=mmsystem File=packages/winceunits/src/mmsystem.pp Score=11 => duplicate
  
This means there are two source files with the same score, so the codetools took the first. The last one in winceunits is for target wince and the first one is for win32 and win64.  
+
Это означает, что есть два исходных файла с одинаковым счетом, поэтому инструменты кодинга взяли первый. Последний в модулях  wince предназначен для целевой [платформы] wince, а первый - для win32 и win64.
  
Now open the rules file fpcsrcrules.inc.
+
Теперь откройте файл правил fpcsrcrules.inc.
  
Rules work like this:
+
Правила работают так:
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
Score:=10;
 
Score:=10;
 
Targets:='wince';
 
Targets:='wince';
Line 188: Line 189:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The '''Add''' adds a rule for all files beginning with 'packages/winceunits' that adds a score of 10 to all these files. The '''Targets''' is a comma separated list of target operating systems and/or target processors. For example Targets='wince,linux,i386' means: apply this rules to TargetOS wince or linux and to all TargetCPU i386.
+
'''Add''' добавляет правило для всех файлов, начинающихся с 'packages/winceunits', которое добавляет 10 баллов ко всем этим файлам. '''Targets''' - это список целевых операционных систем и/или целевых процессоров через запятую. Например, Targets='wince,linux,i386' означает: применить эти правила к TargetOS wince или linux и ко всем TargetCPU i386.
  
=How the codetools parses sources differently from the compiler=
+
=Как codetools анализирует источники по-другому, нежели компилятор=
  
A compiler is optimized to parse code linearly and to load needed units and include files as soon as it parses a uses section or a directive.  
+
Компилятор оптимизирован для линейного анализа кода и загрузки необходимых модулей и включаемых файлов, как только он анализирует секцию uses или директиву.  
The codetools are optimized to parse only certain code sections. For example jumping from the method declaration to the method body only needs the unit and its include files. When a codetool searches a declaration it searches backwards. That means it starts searching in the local variables, then upwards from the implementation. When it finds a uses section it searches the identifiers in the interface section of the units. When the identifier is found it stops. The result and some intervening steps are cached. Because it often only needs to parse a few interface sections it finds an individual identifier very quickly.
+
Codetools оптимизирован для анализа только определенных разделов кода. Например, для перехода от объявления метода к телу метода нужен только модуль и его включаемые файлы. Когда codetool ищет объявление, он ищет в обратном направлении. Это означает, что он начинает поиск в локальных переменных, а затем вверх от [секции] implementation. Когда он находит секцию uses, он ищет идентификаторы в секции interface модулей. Когда идентификатор найден, он останавливается. Результат и некоторые промежуточные шаги кэшируются. Поскольку часто требуется проанализировать только несколько разделов интерфейса, он очень быстро находит индивидуальный идентификатор.
  
The codetools do not parse a source in a single step (as the compiler does) but in several steps, which depend on what the current codetool needs:
+
Codetools анализируют источник не за один шаг (как это делает компилятор), а за несколько шагов, которые зависят от того, что нужно текущему codetool:
*First a source file is loaded in a TCodeBuffer. The IDE uses this step to change the encoding to UTF8. The files are kept in memory and only reloaded if the modification date changes or if a file is manually reverted. There are several tools and functions which work directly on the buffer.
+
*Сначала исходный файл загружается в TCodeBuffer. В среде IDE этот шаг используется для изменения кодировки на UTF8. Файлы хранятся в памяти и перезагружаются только в случае изменения даты изменения или возврата файла вручную. Есть несколько инструментов и функций, которые работают непосредственно с буфером.
*The next step is to parse the unit (or include file). A unit must be parsed from the beginning, so the codetools tries to find the main file, the first file of a unit. It does that by looking for a directive in the first line like {%MainUnit ../lclintf.pp}. If that does not exist, it searches in the includelink cache. The IDE saves this cache to disk, so the IDE learns over time.
+
*Следующим шагом является анализ модуля (или include-файла). Модуль должен анализироваться с самого начала, поэтому codetools пытается найти основной файл, первый файл модуля. Это достигается путем поиска директивы в первой строке, например {% MainUnit ../lclintf.pp}. Если этого не существует, он выполняет поиск в кэше includelink. Среда IDE сохраняет этот кэш на диск, поэтому среда IDE обучается с течением времени.
*After finding the main file TLinkScanner parses the source. It handles compiler directives, such as include directives and conditional directives. The scanner can be given a range, so it might, for instance, parse only a unit's interface. The scanner creates the '''clean source'''. The clean source is put together from all include files, having been stripped of code in the ''else'' part of conditional directives, which is skipped. It also creates a list of '''links''' which map the clean source to the real source files. The clean source is now Pascal containing no ''else'' code. Note: there are also tools designed to scan a single source for all directives. These tools create a tree of directives.
+
*После нахождения основного файла TLinkScanner анализирует источник. Он обрабатывает директивы компилятора, такие как директивы include и директивы условной компиляции. Сканеру может быть задан диапазон, например, он может анализировать только интерфейс модулей. Сканер создает ''чистый исходник''. Чистый исходный код составляется из всех включаемых файлов, очищенных от директив условной компиляции ''else'', которые пропускаются. Он также создает список '''ссылок''', которые отображают чистый исходный код в реальные исходные файлы. Чистым исходным кодом теперь является Pascal, не содержащий кода ''else''. Примечание: есть также инструменты, предназначенные для сканирования единого исходного кода для всех директив. Эти инструменты создают дерево директив.
*After creating the clean source a TCodeTool parses it and creates a tree of TCodeTreeNode. It can also be given a range. This parser skips a few parts, for example class members, begin..end blocks and parameter lists. Many tools don't need them. These sub nodes are created on demand. A TCodeTreeNode has a range StartPos..EndPos which are clean positions, that means positions in the clean source. There are only nodes for the important parts. Creating nodes for every detail would need more memory than the source itself and is seldom needed. There are plenty of functions to find out the details. For example if a function has the 'cdecl' calling convention.
+
*После создания очищенного исходника TCodeTool анализирует его и создает дерево TCodeTreeNode. Он также может указать диапазон. Этот анализатор пропускает несколько частей, например члены класса, блоки <tt>begin..end</tt> и списки параметров. Многим инструментам они не нужны. Эти подузлы создаются по запросу. У TCodeTreeNode есть диапазон <tt>StartPos..EndPos</tt>, который является чистой позицией, то есть позицией в очищенном исходнике. Есть только узлы для важных частей. Создание узлов для каждой детали требует больше памяти, чем сам исходник, и [на практике] редко востребовано. Существует множество функций, чтобы узнать детали. Например, если функция имеет соглашение о вызовах 'cdecl'.
*When searching for an identifier the search stores the base types it finds and creates caches for all identifiers in the interface section.
+
*При поиске идентификатора поиск сохраняет базовые типы, которые он находит, и создает кэши для всех идентификаторов в разделе interface.
  
Every level has its own caches, which need to be checked and updated before calling a function. Many high level functions accessible via the CodeToolBoss do that automatically. For others it is the responsibility of the caller.
+
Каждый уровень имеет свои собственные кэши, которые необходимо проверять и обновлять перед вызовом функции. Многие функции высокого уровня, доступные через CodeToolBoss, делают это автоматически. Для других ответственность за это лежит на [том, кто их] вызывает.
  
Example for:
+
Пример для:
  
 
unit1.pas:
 
unit1.pas:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
unit Unit1;
 
unit Unit1;
 
{$I settings.inc}
 
{$I settings.inc}
Line 223: Line 224:
 
settings.inc:
 
settings.inc:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
{%MainUnit unit1.pas}
 
{%MainUnit unit1.pas}
 
{$DEFINE Flag}
 
{$DEFINE Flag}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
clean source:
+
очищенный исходник:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
unit Unit1;
 
unit Unit1;
 
{$I settings.inc}{%MainUnit unit1.pas}
 
{$I settings.inc}{%MainUnit unit1.pas}
Line 242: Line 243:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 +
Подсказка: чтобы было легко разобрать модуль и построить узлы, используйте '''CodeToolBoss.Explore'''.
  
Hint: To easily parse a unit and build the nodes, use '''CodeToolBoss.Explore'''.
+
=CleanPos и CursorPos=
 
 
=CleanPos and CursorPos=
 
  
There are several methods to define a position in the codetools.
+
Есть несколько методов для определения позиции в codetools.
  
The codetools Absolute position is related to the source as a continuous string starting at character 1. For example a TCodeBuffer holds the file contents as a single string in its Source property. Caret or cursor positions are given as X,Y where X is the columne number and Y the line number. Each value (X and Y) starts at 1. A TCodeBuffer provides the member functions '''LineColToPosition''' and '''AbsoluteToLineCol'' to convert between (X,Y) values and the Absolute codetools position.
+
Абсолютная позиция codetools связана с исходником как непрерывная строка, начинающаяся с символа 1. Например, TCodeBuffer хранит содержимое файла как одну строку в своем свойстве Source. Позиции каретки или курсора задаются как X,Y, где X - номер столбца, а Y - номер строки. Каждое значение (X и Y) начинается с 1. TCodeBuffer предоставляет функции-члены '''LineColToPosition''' и '''AbsoluteToLineCol''' для преобразования между значениями (X, Y) и абсолютной позицией codetools.
When working with multiple source files (such as a unit which may contain several include files), the clean position relates to the absolute position in the stripped code '''Src'''. Here Src is a string whose clean positions start at 1. Cursor positions are specified as TCodeXYPosition (Code,X,Y). A TCodeTool provides the functions '''CaretToCleanPos''' and '''CleanPosToCaret''' to convert between them.
+
При работе с несколькими исходными файлами (например, модулем, который может содержать несколько включаемых файлов), чистая позиция относится к абсолютной позиции в разобранном коде '''Src'''. Здесь Src - строка, чья чистая позиция начинаются с 1. Позиция курсора указывается как TCodeXYPosition (Code,X,Y). TCodeTool предоставляет функции '''CaretToCleanPos''' и '''CleanPosToCaret''' для преобразования между ними.
  
=Inserting, deleting, replacing - the TSourceChangeCache=
+
=Вставка, удаление, замена - TSourceChangeCache=
  
When making changes to the source code of a unit (or its include files) you should use the CodetoolBoss.SourceChangeCache instead of altering the source directly.
+
При внесении изменений в исходный код модуля (или его включаемых файлов) вы должны использовать [функцию] <tt>CodetoolBoss.SourceChangeCache</tt> вместо непосредственного изменения исходника.
  
* Simple usage: Connect, Replace, Replace, ... Apply. See below.
+
* Простое использование: Connect, Replace, Replace, ... Apply. См. ниже.
* You can use cleanpos as given by the node tree OR you can use direct position in a file.
+
* Вы можете использовать cleanpos, как указано в дереве узлов, ИЛИ вы можете использовать точное положение в файле.
* You can use Replace to insert and delete, which automatically calls events, so connected editors are notified of changes.
+
* Вы можете использовать Replace для вставки и удаления, что автоматически вызывает события, поэтому подключенные редакторы уведомляются об изменениях.
* It can automatically insert needed spaces, line breaks or empty lines in front or behind each Replace. For example you define that there should be an empty line in front. The SourceChangeCache checks what is inserted and how much space there is already and will insert needed space.
+
* Он может автоматически вставлять необходимые пробелы, разрывы строк или пустые строки перед или после каждой замены. Например, вы определяете, что впереди должна быть пустая строка. SourceChangeCache проверяет, что вставлено и сколько места уже есть, и вставит необходимое пространство.
* It checks if the replaced/deleted span is writable.
+
* Он проверяет, доступен ли для записи замененный/удаленный диапазон.
* You can do multiple Replaces and you control when they are applied. Keep in mind that inserting code means that the parsed tree becomes invalid and needs rebuilding.
+
* Вы можете делать множественные замены и контролировать [процесс], когда они применяются. Имейте в виду, что вставка кода означает, что проанализированное дерево становится недействительным и нуждается в перестройке.
* Multiple replaces are checked for intersection. For example an insert in the middle of deleted code gives an error.
+
* Множественные замены проверяются на пересечение. Например, вставка в середине удаленного кода выдает ошибку.
* Mutiple insertions at the same place are added FIFO - first at the top.
+
* Несколько вставок в одном месте добавляются [по принципу] FIFO - сначала сверху.
* You can combine several functions altering code to one bigger function. See below.
+
* Вы можете объединить несколько функций, изменяя код в одну большую функцию. См. ниже.
  
==Usage==
+
==Использование==
  
The SourceChangeCache works on a unit, so you need to get a TCodeTool and scan a unit/include file. For example:
+
SourceChangeCache работает с модулем, поэтому вам нужно получить TCodeTool и отсканировать модуль/include-файл. Например:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
   // Step 1: load the file and parse it
+
   // Шаг 1: загружаем файл и парсим его
 
   Code:=CodeToolBoss.LoadFile(Filename,false,false);
 
   Code:=CodeToolBoss.LoadFile(Filename,false,false);
 
   if Code=nil then
 
   if Code=nil then
Line 278: Line 278:
 
     ...;// parse error ...
 
     ...;// parse error ...
  
   // Step 2: connect the SourceChangeCache
+
   // Шаг 2: подключаем SourceChangeCache
 
   CodeToolBoss.SourceChangeCache.MainScanner:=Tool.Scanner;
 
   CodeToolBoss.SourceChangeCache.MainScanner:=Tool.Scanner;
  
   // Step 3: use Replace to insert and/or delete code
+
   // Шаг 3: используем Replace для вставки и/или удаления кода
   // The first two parameters are the needed spaces in front and behind the insertion
+
   // Первые два параметра - это необходимые пробелы спереди и позади вставки
   // The FromPos,ToPos defines the deleted/replaced range in CleanPos positions.
+
   // FromPos, ToPos определяют удаляемый/заменяемый диапазон в позиции CleanPos.
   // The NewCode is the string of new code. Use '' for a delete.
+
   // NewCode - это строка нового кода. Используем '' для удаления.
 
   if not CodeToolBoss.SourceChangeCache.Replace(gtNone,gtNone,FromPos,ToPos,NewCode) then
 
   if not CodeToolBoss.SourceChangeCache.Replace(gtNone,gtNone,FromPos,ToPos,NewCode) then
     exit; // e.g. source read only or a former Replace has deleted the place
+
     exit; // например, исходник [оказался] только для чтения или шаблон Replace удалил место
   ...do some more Replace...
+
   ...еще немного применяем Replace...
  
   // Step 4: Apply the changes
+
   // Шаг 4: Применяем изменения
 
   if not CodeToolBoss.SourceChangeCache.Apply then
 
   if not CodeToolBoss.SourceChangeCache.Apply then
     exit; // apply was aborted
+
     exit; // применение было прервано
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
==BeginUpdate/EndUpdate==
 
==BeginUpdate/EndUpdate==
  
BeginUpdate/EndUpdate delays the Apply. This is useful when combining several code changing functions. For example:
+
BeginUpdate/EndUpdate задерживают [немедленное] применение [изменений]. Это полезно при объединении нескольких функций изменения кода. Например:
  
You want to scan the unit, add a unit to the interface uses section, and remove the unit from the implementation uses section. The two functions AddUnitToMainUsesSection and RemoveUnitFromUsesSection use Apply, altering the source, so the second function would rescan the unit a second time. But since the two functions are independent of each other (they change different parts of the source) you can combine them and do it with one scan:
+
Вы хотите отсканировать модуль, добавить модуль в секцию uses раздела interface, удалив [его] из секции uses раздела implementation. Две функции AddUnitToMainUsesSection и RemoveUnitFromUsesSection используют [метод] Apply, изменяя исходник, поэтому вторая функция будет повторно сканировать модуль во второй раз. Но так как эти две функции не зависят друг от друга (они изменяют разные части источника), вы можете объединить их и сделать это за одно сканирование:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
   // Step 1: parse unit and connect SourceChangeCache
+
   // Шаг 1: парсим модуль  и подключаем SourceChangeCache
 
   if not CodeToolBoss.Explore(Code,Tool,false) then
 
   if not CodeToolBoss.Explore(Code,Tool,false) then
     ...;// parse error ...
+
     ...;// ошибка парсинга ...
 
   CodeToolBoss.SourceChangeCache.MainScanner:=Tool.Scanner;
 
   CodeToolBoss.SourceChangeCache.MainScanner:=Tool.Scanner;
  
   // Step 2: delay Apply
+
   // Шаг 2: задерживаем [применение метода] Apply
 
   CodeToolBoss.SourceChangeCache.BeginUpdate;
 
   CodeToolBoss.SourceChangeCache.BeginUpdate;
  
   // Step 3: add unit to interface section
+
   // Шаг 3: добавляем модуль в секцию uses раздела interface  
   // AddUnitToMainUsesSection would apply and change the code
+
   // AddUnitToMainUsesSection будет применяться и изменять код
   // Because of the BeginUpdate the change is not yet done, but stored in the SourceChangeCache
+
   // Из-за BeginUpdate изменение еще не сделано, но сохранено в SourceChangeCache
 
   if not Tool.AddUnitToMainUsesSection('Classes','',CodeToolBoss.SourceChangeCache) then exit;
 
   if not Tool.AddUnitToMainUsesSection('Classes','',CodeToolBoss.SourceChangeCache) then exit;
  
   // Step 4: remove unit from implementation section
+
   // Шаг 4: удаляем модуль из секции uses раздела implementation  
   // Without the BeginUpdate the RemoveUnitFromUsesSection would rescan the unit
+
   // Без BeginUpdate RemoveUnitFromUsesSection будет повторно сканировать модуль
 
   if Tool.FindImplementationUsesSection<>nil then
 
   if Tool.FindImplementationUsesSection<>nil then
 
     if not Tool.RemoveUnitFromUsesSection(Tool.FindImplementationUsesSection,'Classes',CodeToolBoss.SourceChangeCache) then exit;
 
     if not Tool.RemoveUnitFromUsesSection(Tool.FindImplementationUsesSection,'Classes',CodeToolBoss.SourceChangeCache) then exit;
  
   // Step 5: apply all changes
+
   // Шаг 5: применяем все изменения
 
   if not CodeToolBoss.SourceChangeCache.EndUpdate then
 
   if not CodeToolBoss.SourceChangeCache.EndUpdate then
     exit; // apply was aborted
+
     exit; // применение было прервано
 
</syntaxhighlight>
 
</syntaxhighlight>
  
BeginUpdate/EndUpdate work with a counter, so if you call BeginUpdate twice you need to call EndUpdate twice. This means you can put the above example in a function and combine that with another function.
+
BeginUpdate/EndUpdate работают со счетчиком, поэтому, если вы вызываете BeginUpdate дважды, вам нужно дважды вызывать EndUpdate. Это означает, что вы можете поместить приведенный выше пример в функцию и объединить ее с другой функцией.
  
==Saving changes to disk==
+
==Сохранение изменений на диск==
  
The above changes are made to the code buffers and the buffers are marked modified. To save the changes to disk, you have to call Save for each modified buffer.  
+
Вышеуказанные изменения вносятся в буферы кода, и буферы помечаются как измененные. Чтобы сохранить изменения на диске, вы должны вызвать Save для каждого измененного буфера.
*The buffers that will be modified in the next Apply/EndUpdate are in SourceChangeCache.BuffersToModify and BuffersToModifyCount.
+
*Буферы, которые будут изменены в следующем Apply/EndUpdate, находятся в <tt>SourceChangeCache.BuffersToModify</tt> и <tt>BuffersToModifyCount</tt>.
*The events SourceChangeCache.OnBeforeApplyChanges/OnAfterApplyChanges are used by the CodeToolBoss, which connects it to its own OnBeforeApplyChanges/OnAfterApplyChanges. The Lazarus IDE sets these events and automatically opens modified files in the source editor, so all changes go into the undo list of synedit.
+
*События <tt>SourceChangeCache.OnBeforeApplyChanges/OnAfterApplyChanges</tt> используются CodeToolBoss, который подключает их к собственным <tt>OnBeforeApplyChanges/OnAfterApplyChanges</tt>. Среда Lazarus IDE устанавливает эти события и автоматически открывает измененные файлы в редакторе исходного кода, поэтому все изменения попадают в список отмены synedit.
  
=Hints/Tips/Guide Lines=
+
=Подсказки/Советы/Руководства=
  
*BuildTree checks the current state and will only parse if needed. If a former call has parsed the interface and you need the interface again, BuildTree will not do anything. If you need the full unit, only the implementation will be parsed. Nodes are only freed if some files have changed on disk or some settings have changed (initial macros). BuildTree does not check all files on every call. Instead it uses the directory cache of the codetools. So, if nothing has changed it will return very quickly. You should call it before any search operation.
+
*BuildTree проверяет текущее состояние и будет анализировать [код] только при необходимости. Если предыдущий вызов проанализировал интерфейс, и вам снова нужен интерфейс, BuildTree ничего не сделает. Если вам нужен весь модуль, будет проанализирован только раздел implementation. Узлы освобождаются только в том случае, если некоторые файлы изменились на диске или изменились некоторые настройки (исходные макросы). BuildTree не проверяет все файлы при каждом вызове. Вместо этого он использует каталог кеша codetools. Так что, если ничего не изменилось, он вернется очень быстро. Вы должны вызвать его перед любой операцией поиска.
*Do '''not''' call BuildTree '''after''' an operation. That is a waste of CPU.
+
*'''Не''' вызывайте BuildTree '''после''' операции [проверки]. Это пустая трата [ресурсов] процессора.
*BuildTree raises an exception when there are missing include files or syntax errors. You should enclose your code into
+
*BuildTree вызывает исключение, когда отсутствуют include-файлы или [имеются] синтаксические ошибки. Вы должны приложить к своему коду [следующую конструкцию]
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
try
 
try
 
   Tool.BuildTree(lsrEnd);
 
   Tool.BuildTree(lsrEnd);
   ... search ... replace  
+
   ... поиск ... замена  
 
except
 
except
 
   on E: Exception do
 
   on E: Exception do
Line 347: Line 347:
 
</syntaxhighlight>
 
</syntaxhighlight>
 
*After calling SourceChangeCache.Replace (multiple times) the sources (TCodeBuffers) have not changed immediately. You must call SourceChangeCache.Apply to change the sources. This will not save the changes to file.
 
*After calling SourceChangeCache.Replace (multiple times) the sources (TCodeBuffers) have not changed immediately. You must call SourceChangeCache.Apply to change the sources. This will not save the changes to file.
 +
*После вызова <tt>SourceChangeCache.Replace</tt> (несколько раз) исходники (TCodeBuffers) не изменяются сразу. Вы должны вызвать <tt>SourceChangeCache.Apply</tt>, чтобы изменить исходники. [Но] это не сохраняет изменения в файл.
  
=Links=
+
=Ссылки=
 
 
*[[Lazarus IDE Tools]] - A tutorial about the built-in tools of the standard IDE
 
*[[Cody]] - IDE package adding advanced code tools to the IDE
 
*[[Extending the IDE]] - How to write your own codetools plugins for the IDE.
 
  
[[Category: Components]]
+
*[[Lazarus_IDE_Tools/ru|Lazarus IDE Tools]] - Учебник по встроенным инструментам стандартной IDE
 +
*[[Cody]] - Пакет IDE, добавляющий продвинутые инструменты кода в IDE
 +
*[[Extending_the_IDE/ru|Extending the IDE]] - Как написать свои собственные плагины codetools для IDE.

Latest revision as of 06:45, 11 February 2020

Deutsch (de) English (en) français (fr) русский (ru)

Что такое codetools?

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

svn:

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

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

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

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

  • FPCDIR: путь к исходникам FPC, по умолчанию это ~/freepascal/fpc.
  • PP: pпуть к исполняемому файлу компилятора (/usr/bin/fpc or /usr/bin/ppc386 or C:\lazarus\ppc386.exe). Codetools нужно спросить компилятор о настройках. По умолчанию поиск 'fpc' осуществляется через переменную PATH.
  • FPCTARGETOS: скажите codetools сканировать другую операционную систему (кросс-компиляция). Например: linux, freebsd, darwin, win32, win64, wince
  • FPCTARGETCPU: при сканировании для другого процессора. Например: i386, powerpc, x86_64, arm, sparc
  • LAZARUSDIR: путь к исходникам Lazarus. Требуется только если вы хотите их сканировать.

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

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

 <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" (шаблоны определений) для генерации путей поиска и макросов. Шаблон определений является деревом правил.

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

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 для [получения] полного списка. Вот несколько важных [из них]:

  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+')' :

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

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

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

  • da_Block - используется для группировки шаблонов. Когда блок выполняется, выполняются и все дочерние элементы.
  • da_Directory - используйте это, чтобы определить все правила каталога. Если это корневой каталог, задайте для параметра Value полный развернутый путь к каталогу (используйте функцию CleanAndExpandDirectory). Если это подкаталог (родительский шаблон - это каталог), то значение - это подкаталог. Может содержать макросы. Дочерние объекты выполняются только в том случае, если каталог соответствует.
  • da_Define - устанавливает значение (Value) макроса (Variable) текущего каталога. Значение может содержать макросы. Обратите внимание, что когда значение макроса установлено в пустую строку , оно все еще определено, это означает, что {$IFDEF variable} все равно будет true. Дочерние элементы не выполняются.
  • da_Undefine - очищает макрос (Variable) текущего каталога. {$IFDEF macro} является равным false . Дочерние элементы не выполняются.
  • da_DefineRecurse - как и da_Define, но для текущего каталога и подкаталогов.
  • da_UndefineRecurse - как и da_Undefine, но для текущего каталога и подкаталогов.
  • da_UndefineAll - очищает все значения макросов.
  • da_IfDef - если макрос Variable определен, тогда выполняются дочерние элементы и пропускаются следующие da_Else и da_ElseIf.
  • da_IfNDef - если макрос Variable не определен, тогда выполняются дочерние элементы и пропускаются следующие da_Else и da_ElseIf.
  • da_If,daElseIf - если выполнение логического выражения Value приводит к true, тогда выполняются дочерние элементы и пропускаются следующие da_Else и da_ElseIf. Значение может содержать макросы.
  • da_Else - когда этот шаблон будет выполнен, выполняются все дочерние элементы.

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

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

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

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

  • Как написать пакет IDE. Когда вы устанавливаете этот пакет, он регистрирует элемент Quick Fix в IDE.
  • Как написать элемент Quick Fix для сообщения компилятора: 'Parameter "Sender" not used' (Параметр "Sender" не используется)
  • Как использовать codetools для:
    • [синтаксического] разбора модуля
    • преобразования данных Filename, Line и Column в исходную позицию codetools
    • поиска узла codetools в позиции курсора
    • поиска узла процедуры и узла begin..end
    • создания прекрасной позиции вставки для оператора в начале блока begin..end
    • получения информации об отступах строки, чтобы новая строка работала также в подпроцедуре
    • вставки кода, используя codetools

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

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

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

Usage: lazarus/components/codetools/examples/testfpcsrcunitrules -h

  -c <compiler file name>, --compiler=<compiler file name>
         По умолчанию используется переменная окружения PP.
         Если это не установлено, ищите fpc

  -T <target OS>, --targetos=<target OS>
         По умолчанию используется переменная окружения FPCTARGET.
         Если это не установлено, используйте компилятор по умолчанию.

  -P <target CPU>, --targetcpu=<target CPU>
         По умолчанию используется переменная окружения FPCTARGETCPU.
         Если это не установлено, используйте компилятор по умолчанию.

  -F <FPC source directory>, --fpcsrcdir=<FPC source directory>
         По умолчанию используется переменная окружения FPCDIR.
         Значения по умолчанию нет.

  -u <unit name>, --checkunit=<unit name>
         Напишите подробный отчет об этом модуле.

Пример для testfpcsrcunitrules

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

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

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

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

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

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

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

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

Unit report for mmsystem

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.

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

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:

  • Сначала исходный файл загружается в TCodeBuffer. В среде IDE этот шаг используется для изменения кодировки на UTF8. Файлы хранятся в памяти и перезагружаются только в случае изменения даты изменения или возврата файла вручную. Есть несколько инструментов и функций, которые работают непосредственно с буфером.
  • Следующим шагом является анализ модуля (или include-файла). Модуль должен анализироваться с самого начала, поэтому codetools пытается найти основной файл, первый файл модуля. Это достигается путем поиска директивы в первой строке, например {% MainUnit ../lclintf.pp}. Если этого не существует, он выполняет поиск в кэше includelink. Среда IDE сохраняет этот кэш на диск, поэтому среда IDE обучается с течением времени.
  • После нахождения основного файла TLinkScanner анализирует источник. Он обрабатывает директивы компилятора, такие как директивы include и директивы условной компиляции. Сканеру может быть задан диапазон, например, он может анализировать только интерфейс модулей. Сканер создает чистый исходник. Чистый исходный код составляется из всех включаемых файлов, очищенных от директив условной компиляции else, которые пропускаются. Он также создает список ссылок, которые отображают чистый исходный код в реальные исходные файлы. Чистым исходным кодом теперь является Pascal, не содержащий кода else. Примечание: есть также инструменты, предназначенные для сканирования единого исходного кода для всех директив. Эти инструменты создают дерево директив.
  • После создания очищенного исходника TCodeTool анализирует его и создает дерево TCodeTreeNode. Он также может указать диапазон. Этот анализатор пропускает несколько частей, например члены класса, блоки begin..end и списки параметров. Многим инструментам они не нужны. Эти подузлы создаются по запросу. У TCodeTreeNode есть диапазон StartPos..EndPos, который является чистой позицией, то есть позицией в очищенном исходнике. Есть только узлы для важных частей. Создание узлов для каждой детали требует больше памяти, чем сам исходник, и [на практике] редко востребовано. Существует множество функций, чтобы узнать детали. Например, если функция имеет соглашение о вызовах 'cdecl'.
  • При поиске идентификатора поиск сохраняет базовые типы, которые он находит, и создает кэши для всех идентификаторов в разделе interface.

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

Пример для:

unit1.pas:

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

settings.inc:

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

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

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 вместо непосредственного изменения исходника.

  • Простое использование: Connect, Replace, Replace, ... Apply. См. ниже.
  • Вы можете использовать cleanpos, как указано в дереве узлов, ИЛИ вы можете использовать точное положение в файле.
  • Вы можете использовать Replace для вставки и удаления, что автоматически вызывает события, поэтому подключенные редакторы уведомляются об изменениях.
  • Он может автоматически вставлять необходимые пробелы, разрывы строк или пустые строки перед или после каждой замены. Например, вы определяете, что впереди должна быть пустая строка. SourceChangeCache проверяет, что вставлено и сколько места уже есть, и вставит необходимое пространство.
  • Он проверяет, доступен ли для записи замененный/удаленный диапазон.
  • Вы можете делать множественные замены и контролировать [процесс], когда они применяются. Имейте в виду, что вставка кода означает, что проанализированное дерево становится недействительным и нуждается в перестройке.
  • Множественные замены проверяются на пересечение. Например, вставка в середине удаленного кода выдает ошибку.
  • Несколько вставок в одном месте добавляются [по принципу] FIFO - сначала сверху.
  • Вы можете объединить несколько функций, изменяя код в одну большую функцию. См. ниже.

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

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

  // Шаг 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, изменяя исходник, поэтому вторая функция будет повторно сканировать модуль во второй раз. Но так как эти две функции не зависят друг от друга (они изменяют разные части источника), вы можете объединить их и сделать это за одно сканирование:

  // Шаг 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 для каждого измененного буфера.

  • Буферы, которые будут изменены в следующем Apply/EndUpdate, находятся в SourceChangeCache.BuffersToModify и BuffersToModifyCount.
  • События SourceChangeCache.OnBeforeApplyChanges/OnAfterApplyChanges используются CodeToolBoss, который подключает их к собственным OnBeforeApplyChanges/OnAfterApplyChanges. Среда Lazarus IDE устанавливает эти события и автоматически открывает измененные файлы в редакторе исходного кода, поэтому все изменения попадают в список отмены synedit.

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

  • BuildTree проверяет текущее состояние и будет анализировать [код] только при необходимости. Если предыдущий вызов проанализировал интерфейс, и вам снова нужен интерфейс, BuildTree ничего не сделает. Если вам нужен весь модуль, будет проанализирован только раздел implementation. Узлы освобождаются только в том случае, если некоторые файлы изменились на диске или изменились некоторые настройки (исходные макросы). BuildTree не проверяет все файлы при каждом вызове. Вместо этого он использует каталог кеша codetools. Так что, если ничего не изменилось, он вернется очень быстро. Вы должны вызвать его перед любой операцией поиска.
  • Не вызывайте BuildTree после операции [проверки]. Это пустая трата [ресурсов] процессора.
  • BuildTree вызывает исключение, когда отсутствуют include-файлы или [имеются] синтаксические ошибки. Вы должны приложить к своему коду [следующую конструкцию]
try
  Tool.BuildTree(lsrEnd);
  ... поиск ... замена  
except
  on E: Exception do
    CodeToolBoss.HandleException(E);
end;
  • After calling SourceChangeCache.Replace (multiple times) the sources (TCodeBuffers) have not changed immediately. You must call SourceChangeCache.Apply to change the sources. This will not save the changes to file.
  • После вызова SourceChangeCache.Replace (несколько раз) исходники (TCodeBuffers) не изменяются сразу. Вы должны вызвать SourceChangeCache.Apply, чтобы изменить исходники. [Но] это не сохраняет изменения в файл.

Ссылки

  • Lazarus IDE Tools - Учебник по встроенным инструментам стандартной IDE
  • Cody - Пакет IDE, добавляющий продвинутые инструменты кода в IDE
  • Extending the IDE - Как написать свои собственные плагины codetools для IDE.