Инструменты Lazarus IDE. Часть I. Codetools - инструменты редактора исходного кода среды Lazarus. |
25.04.2005 Сергей Смирнов |
За основу статьи взят документ из Lazarus-ccr
IDE Lazarus использует библиотеку парсинга и редактирования паскалевого кода именуемую "codetools". Этот инструментарий обеспечивает такие возможности, как поиск объявлений, завершение кода, выделение, перемещение, вставку и украшение кода на паскале. Эти функции позволят Вам сэкономить кучу времени и повысить продуктивность работы. Они полностью настраиваемые и могут быть сконфигурированы для выполнения специальными сочетаниями клавиш (см. Editor Options).
Codetools работают напрямую с исходным кодом и понимают не только FPC, но также Delphi и Kylix. Это даёт Вам уникальную возможность одновременно работать и с исходниками FPC, и Delphi.
Стандартные сочетания клавиш:Переход между декларацией и реализацией метода | Ctrl+Shift+Up |
Применение шаблонов кода | Ctrl+J |
Завершение кода | Ctrl+Shift+C |
Завершение идентификатора | Ctrl+Space |
Для перехода между телом процедуры или функции и её декларацией используется Ctrl+Shift+Up. Например:
interface procedure DoSomething; // декларация процедуры implementation procedure DoSomething; // тело процедуры begin end;
Если курсор находится внутри процедуры и Вы нажимаете Ctrl+Shift+Up, то он перескакивает на её определение. Если снова нажать Ctrl+Shift+Up, то курсор снова окажется в теле процедуры после "begin". Если, например, Вы находитесь в процессе переделывания кода и определение метода перестало соответствовать его реализации, то при использовании данного средства IDE попытается самостоятельно найти наиболее подходящий вариант.
Include-файлы вставляются компилятором в исходный код с помощью директивы {$I ИмяФайла} или {$INCLUDE ИмяФайла}. Lazarus и FPC очень интенсивно используют эту технику чтобы не загромождать коды и избавиться от неудобочитаемых конструкций {$IFDEF}, обеспечивающих кроссплатформенность.
В отличие от Delphi, Lazarus с успехом поддерживает включаемые файлы. Например, Вы можете перепрыгнуть с объявления метода к его реализации, содержащейся в Include-файле, или когда завершение кода добавляет тело метода после другого тела метода оба они сохраняются в одном файле. Таким путём Вы можете создавать во включаемых файлах реализации целых классов подобно тому как реализованы почти все компоненты LCL.
Однако, здесь кроется одна ловушка: если Вы впервые открыли Include-файл и попытались перескочить на объявление метода из его реализации, то IDE не сможет этого сделать, так как неизвестно какой модуль включает этот файл. Вам придётся сначала самостоятельно открыть этот модуль. Однако IDE запоминает директивы включения для открываемых модулей, поэтому во второй раз всё сработает.
Разумеется, эта технология не безупречна. Некоторые Include-файлы используются дважды и более и тут поведение IDE будет зависеть от последовательности действий.
Шаблоны кода превращают идентификатор в некоторый текст или даже целый фрагмент кода. По-умолчанию шаблоны кода вызываются сочетанием клавиш Ctrl+J. Вы набираете некоторый идентификатор, нажимаете Ctrl+J и введенный идентификатор заменяется текстом, определённым для данного идентификатора. Определения шаблонов кода доступны через Environment -> Editor Options -> CodeTools.
Пример: наберите "classf", оставьте курсор справа от буквы "f" и нажмите Ctrl+J. Идентификатор "classf" будет заменён следующим фрагментом кода:
T = class(T) private public constructor Create; destructor Destroy; override; end;Шаблон можно выбрать из списка, если поместить курсор на пустом месте и нажать Ctrl+J. Список шаблонов всплывёт в этом месте и Вы сможете выбрать нужный шаблон с помощью клавиш перемещения курсора или просто набирая первые буквы шаблона. Нажатие Return вставит выбранный, а Escape закроет список.
Завершение кода (Code Completion) можно вызвать с помощью меню Edit -> Complete Code или с помощью стандартного сочетания клавиш Ctrl+Shift+C.
В Delphi под завершением кода подразумевается появление списка идентификаторов в текущем положении курсора (Ctrl+Space). В Lazarus это называется завершением идентификатора (Identifier completion).
Завершение кода включает целый ряд функций. Например:
Завершение класса - наиболее сильная черта средств завершения кода. Вы пишете класс, добавляете методы и свойства, а завершение кода добавляет всё остальное: заготовки реализации методов, методы и переменные для свойств. Вот к примеру. Создадим класс (не забывайте пользоваться шаблонами кода):
TExample = class(TObject) public constructor Create; destructor Destroy; override; end;Установим курсор в любом месте определения класса и нажмём Ctrl+Shift+C. Это добавит заготовки методов и установит курсор внутри первого из созданных методов:
{ TExample } constructor TExample.Create; begin | end; destructor TExample.Destroy; begin inherited Destroy; end;Учтите, что "|" - это просто изображение курсора. Перескочить на объявление метода можно с помощью уже неоднократно упомянутой комбинации клавиш Ctrl+Shift+Up. Вы также наверное заметили, что в деструктор был добавлен вызов "inherited Destroy". Это случается, когда в объявлении класса присутствует ключевое слово "override".
Теперь добавим метод DoSomething:
TExample = class(TObject) public constructor Create; procedure DoSomething(i: integer); destructor Destroy; override; end;И нажмём Ctrl+Shift+C. Среда разработки добавит:
procedure TExample.DoSomething(i: integer); begin | end;Как Вы увидите, реализация метода будет добавлена точно между Create и Destroy, как и было указано в определении класса. Такова политика вставки. Вы можете определить её в меню Environment > Codetools Options -> Code Creation.
Добавим к определению класса свойство AnInteger:
TExample = class(TObject) public constructor Create; procedure DoSomething(i: integer); destructor Destroy; override; property AnInteger: Integer; end;Нажмём Ctrl+Shift+C и получим следующее:
procedure TExample.SetAnInteger(const AValue: integer); begin |if FAnInteger=AValue then exit; FAnInteger:=AValue; end;Система завершения кода любезно создала для нас метод для записи свойства. Перепрыгнем на определение метода с помощью Ctrl+Shift+Up и увидим, какие нововведения появились в нашем классе:
TExample = class(TObject) private FAnInteger: integer; procedure SetAnInteger(const AValue: integer); public constructor Create; procedure DoSomething(i: integer); destructor Destroy; override; property AnInteger: integer read FAnInteger write SetAnInteger; end;Наспех сделанное определение свойства было расширено не только разделами Read и Write, но и используемыми там методом и переменной. В соответствии с принципом инкапсуляции класс получил новую секцию "private" с переменной "FAnInteger" и методом "SetAnInteger". Добавление префиксов "F" к переменной и "Set" к методу является правилом, принятым в Delphi, однако если Вас это не устраивает, всегда можно сделать по-другому с помощью меню Environment > Codetools Options -> Code Creation.
Создание свойства только для чтения:
property PropName: PropType read;будет расширено вот так:
property PropName: PropType read FPropName;Создание свойства только для записи:
property PropName: PropType write;будет превращено в:
property PropName: PropType write SetPropName;Создание свойства только для чтения с помощью метода:
property PropName: PropType read GetPropName;будет сохранено, но будет добавлена функция GetPropName:
function GetpropName: PropType;Создание свойства с модификатором "stored":
property PropName: PropType stored;будет преобразовано в
property PropName: PropType read FPropName write SetPropName stored PropNameIsStored;Так как "stored" используется в потоке, разделы Read и Write будут также автоматически добавлены. Замечание: Завершение идентификатора также распознаёт недоделанные свойства и строит определённые предположения относительно имён. Например:
property PropName: PropType read |;Если расположить курсор на один пробел правее ключевого слова (перед точкой с запятой), то нажатие Ctrl+Space для завершения идентификатора вызовет список, в котором будут присутствовать переменная "FPropName" и процедура "SetPropName".
Завершение предварительных объявлений процедур "Forward Procedure Completion" также является частью системы завершения кода и добавляет пропущенные реализации процедур. Оно срабатывает, когда курсор находится на объявлении процедуры. Например, давайте добавим новую процедуру в интерфейсную секцию:
procedure DoSomething;Поместим на неё курсор и нажмём Ctrl+Shift+C для завершения кода. Это вызовет создание в секции реализации следующего кода:
procedure DoSomething; begin | end;Созданные таким способом процедуры будут добавлены перед методами класса. Если в интерфейсной части уже присутствовали другие процедуры, Lazarus постарается сохранить их порядок. Например:
procedure Proc1; procedure Proc2; // new proc procedure Proc3;Если процедуры Proc1 и Proc3 уже реализованы, то код процедуры Proc2 будет вставлено межу ними. Такое поведение обусловлено настройками, доступ к которым можно получить с помощью меню Environment > Codetools Options -> Code Creation. Если имеется не одна, а несколько нереализованных процедур, то система завершения кода сработает сразу для всех. Вы можете спросить, почему было придумано отдельное название "Завершение Forward-процедур"? Потому, что это работает не только для процедур, объявленных в интерфейсной части модуля, но и для процедур с модификатором "forward".
Завершение назначения процедур обработки событий также является частью системы завершения кода и работает для единичной операции присвоения обработчика события. Запускается всё той же комбинацией Ctrl+Shift+C когда курсор расположен правее оператора присваивания события. К примеру, в обработчике события FormCreate добавим строку "OnPaint:=":
procedure TForm1.Form1Create(Sender: TObject); begin OnPaint:=| end;Символ "|" изображает курсор и его не нужно печатать. После нажатия Ctrl+Shift+C строка примет следующий вид:
OnPaint:=@Form1Paint;Новый метод Form1Paint будет добавлен к классу TForm1. Сработает завершение класса и в итоге Вы получите:
procedure TForm1.Form1Paint(Sender: TObject); begin | end;Однако никто не запрещает Вам дать своё собственное название процедуре обработки события просто напечатав его:
OnPaint:=@ThePaintMethod;
Завершение локальной переменной (Local Variable Completion) будучи частью системы завершения кода позволяет просто добавить определение локальной переменной в строке, где идентификатору присваивается некоторое выражение:
procedure TForm1.Form1Create(Sender: TObject); begin i:=3; end;Поместите курсор на символ "i" (сразу за ним) и нажмите Ctrl+Shift+C. Результат не заставит себя ждать:
procedure TForm1.Form1Create(Sender: TObject); var i: Integer; begin i:=3; end;Система сначала проверяет, не объявлена ли уже эта переменная, и, если нет - добавляет. Тип переменной выбирается исходя из значения в правой части оператора присваивания. В нашем случае это 3, что больше всего похоже на Integer. Вот другой пример. Наберите:
TWhere = (Behind, Middle, InFront); procedure TForm1.Form1Create(Sender: TObject); var a: array[TWhere] of char; begin for Where:=Low(a) to High(a) do writeln(a[Where]); end;Поместите курсор на "Where" и нажмите Ctrl+Shift+C. Получится следующее:
procedure TForm1.Form1Create(Sender: TObject); var a: array[TWhere] of char; Where: TWhere; begin for Where:=Low(a) to High(a) do writeln(a[Where]); end;
Система завершения кода старается сохранить комментарии там, где они изначально расположены. Например:
FList: TList; // list of TComponent FInt: integer;При вставке новой переменной между FList и FInt, комментарий в строке с FList будет сохранён. То же самое справедливо и для более сложного случая:
FList: TList; { list of TComponent This is a comment over several lines, starting in the FList line, so codetools assumes it belongs to the FLIst line and will not break this relationship. Code is inserted behind the comment. } FInt: integer;и даже если комментарий начинается в следующей строке:
FList: TList; // list of TComponent { This comment belongs to the statement below. New code is inserted above this comment and behind the comment of the FList line. } FInt: integer;