Sign in Join
Wiki copytemporary

SynEdit Highlighter/ru: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11Для получения дополнительной информации о SynEdit перейдите к статье: [[SynEdit]]
22
33------------------------------------------------------------------------
44
55Прим.перев.: для облегчения читаемости статьи далее под термином **Highlighter** будет подразумеваться механизм(маркер) подсветки синтаксиса.
66
77------------------------------------------------------------------------
88
99# Понимание SynEdit Highlighter
1010
1111## Взаимоотношения SynEdit - Highlighter
1212
1313**SynEdit - Highlighter** имеют взаимоотношение **N к 1**.
1414
1515- Один экземпляр Highlighter может обслуживать **N** (много) экземпляров SynEdits
1616- Каждый SynEdit имеет только один Highlighter
1717- Но: один текст (текстовый буфер) может иметь много маркеров подсветки синтаксиса, если он используется несколькими SynEdit (каждый SynEdit будет иметь один HL, но все HL будут работать с одним и тем же документом)
1818
1919В результате:
2020
2121- Экземпляр Highlighter не имеет (фиксированной) ссылки на SynEdit.
2222
2323
2424(Однако Highlighter'ы хранят список SynEditTextBuffers, к которому они прикреплены)
2525
2626- Все данные для Highlighter (должны быть) сохранены в SynEdit (фактически в TextBuffer SynEdit (называемом «Линии»).
2727
2828Однако перед каждым вызовом Highlighter SynEdit гарантирует, что для Highlighter.CurrentLines будут установлены текущие строки SynEdits. Таким образом, маркер может получить доступ к данным в любое время. Формат хранения данных определяется маркером (TSynCustomHighlighter.AttachToLines).
2929
3030## Сканирование и возврат атрибутов подсветки
3131
3232Ожидается, что Highlighter будет работать на основе каждой строки.
3333
3434Если какой-либо текст был изменен, SynEdit будет вызывать (TSynCustomHighlighter.ScanFrom / в настоящее время вызывается из TSynEdit.ScanFrom) с диапазоном строк. Highlighter должен знать состояние предыдущей строки.
3535
3636Если требуются атрибуты подсветки, SynEdit будет запрашивать их также для каждой строки. SynEdit будет проходить через отдельные токены на линии. В настоящее время это происходит из вложенных процедур PaintLines в SynEdit.PaintTextLines. Он вызывает TSynCustomHighlighter.StartAtLineIndex, за которым следует HL.GetTokenEx/HL.GetTokenAttribute до тех пор, пока HL.GetEol имеет значение false.
3737
3838Кроме того, базовый класс для данных Highlighter'а (см. AttachToLines) основан на хранении данных на каждой строке, а TextBuffer (строки) SynEdit выполняет обслуживание этих данных для их синхронизации. То есть: всякий раз, когда строки текста вставляются или удаляются, записи также вставляются или удаляются из данных highlighter'ов (следовательно, в каждой строке должна быть одна запись).
3939
4040Обычно Highlighter'ы сохраняют статус конца строки в этом поле. Поэтому, если Highlighter будет работать со строчкой, он продолжит ввод состояния из предыдущей строки.
4141
4242## Folding (сворачивание текста)
4343
4444Событие фолдинга (схлопывания/сворачивания текста) SynEdit обрабатывается модулями SynEditFoldedView и SynGutterCodeFolding. Highlighter'ы, которые реализуют сворачивание, должны основываться на TSynCustomFoldHighlighter.
4545
4646Базовая информация для связи между SynEditFoldedView и Highlighter'ом требует 2 значения, сохраненных для каждой строчки (конечно, сам highlighter может хранить больше информации):
4747
4848- FoldLevel в конце строки
4949- Минимальный FoldLevel, встречающийся где-либо в строке
5050
5151Foldlevel указывает, сколько (вложенных) уровней схлопывания/сворачивания текста существует. Он повышается всякий раз, когда уровень сворачивания начинается, и понижается, когда уровень сворачивания заканчивается:
5252
5353``` pascal
5454 EndLvl MinLvl
5555 Procedure a; 1 - 0
5656 Begin 2 -- 1 -
5757 b:= 1; 2 -- 2 --
5858 if c > b then begin 3 --- 2 --
5959 c:=b; 3 --- 3 ---
6060 end else begin 3 --- 2 --
6161 b:=c; 3 --- 3 ---
6262 end; 2 -- 2 --
6363 end; 0 0 // Оператор end закрывает оба уровня сворачивания текста: операторов
6464 //begin и procedure
6565```
6666
6767На строке
6868
6969``` pascal
7070Procedure a; 1 - 0
7171```
7272
7373MinLvl равен 0, потому что строка началась с уровня 0 (и она никогда не спускалась / не закрывалась). Аналогично во всех строках, где есть только ключевое слово, открывающее уровень сворачивания текста ("begin").
7474
7575А строка
7676
7777``` pascal
7878 end else begin 3 --- 2 --
7979```
8080
8181начинается с уровня 3, а также заканчивается им (один закрытый, один открытый). Но так как она спускается первой, минимальный уровень, встречающийся где-либо в строке, равен 2.
8282
8383Без MinLvl было бы невозможно сказать, что уровень сворачивания текста заканчивается на этой строке.
8484
8585MaxLvl не существует, потому что уровни сворачивания текста, начинающиеся и заканчивающиеся на одной и той же линии, не могут быть свернуты в любом случае. Не нужно их обнаруживать.
8686
8787``` pascal
8888 if a then begin b:=1; c:=2; end; // нет уровня сворачивания текста на этой строке
8989```
9090
9191# Создание SynEdit Highlighter
9292
9393Начиная с версии 0.9.31 rev.35115, сворачивание текста в highlighter'е изменилось. Реализовать базовое свертывание текста теперь проще.
9494
9595Все исходники можно найти в каталоге установки Lazarus в: examples\SynEdit\NewHighlighterTutorial\\
9696
9797Проект HighlighterTutorial содержит 3 различных примера highlighter'ов:
9898
9999- SimpleHl: используется в шаге 1 ниже
100100- ContextHl: используется в шаге 2 ниже
101101- FoldHl: используется в шаге 3 ниже
102102
103103SimpleHl и ContextHl тоже будут работать с v.0.9.30 Лазаруса
104104
105105Сворачивание текста в highlighter'е в действии
106106
107-<figure>
108-<img src="SynEditFoldingHighlighterDemo.png" title="SynEditFoldingHighlighterDemo.png" />
109-<figcaption>SynEditFoldingHighlighterDemo.png</figcaption>
110-</figure>
107+![](/wiki-images/a/a2/SynEditFoldingHighlighterDemo.png)
111108
112109## Основы: возврат токенов и атрибутов
113110
114111Как указано, модуль **SimpleHl** демонстрирует этот процесс.
115112
116113### Что он делает
117114
118115- Он разбивает каждую строку на слова и пробелы (или табуляции)
119116 - Пробелы являются частью текста и также должны быть подсвечены
120117- Этот пример позволяет указать разные цвета для
121118
122119
123120
124121\- текста (по умолчанию не подсвечен)
125122
126123\- пробелов (по умолчанию в серебристой рамке)
127124
128125\- слов, разделенных пробелами, начинающихся с a,e,i,o,u (по умолчанию выделены жирным шрифтом)
129126
130127\- "не" слов (по умолчанию на красном фоне)
131128
132129### Как это работает
133130
134131- Creation
135132
136133Highlighter создает Атрибуты, которые могут вернуть Words(слова) и Spaces(пробелы).
137134
138135- SetLine
139136
140137Событие вызывается SynEdit до того, как будет прорисована строка (или до того, как потребуется информация о выделении)
141138
142139- GetTokenEx, GetTokenAttribute, Next, GetEol
143140
144141События используются SynEdit для перебора строки. Обратите внимание, что первый токен (слово или пробел) должен быть готов после SetLine, без вызова Next.
145142
146143> **Note:** токены, возвращаемые для каждой строки, должны представлять исходный текст строки и возвращаться в правильном порядке.
147144
148145- GetToken, GetTokenPos, GetTokenKind
149146
150147SynEdit использует их, например, для поиска подходящих скобок. Если tokenKind возвращает разные значения для каждого атрибута, то скобки совпадают только в том случае, если они имеют одинаковый вид (например, если был строковый атрибут, скобки вне строки не будут совпадать с скобками внутри строки).
151148
152149### Другие заметки
153150
154151Для удобства чтения highlighter не имеет оптимизации, поэтому он может быть очень медленным для больших текстов. Многие из поставляемых highlighter'ов используют хеш-функции, чтобы найти слово (или любую группу символов).
155152
156153## Шаг 2: Использование диапазонов
157154
158155Как указано, модуль **ContextHl** демонстрирует этот процесс.
159156
160157Следующий пример допускает влияние содержимого строки на последующие строки. Например: "`(*`" в Паскале делает все последующие строки комментарием, пока не будет найден "`*)`".
161158
162159Этот пример расширяет показанный выше **SimpleHl**: Токены `--` и `++` (должны быть обрамлены пробелом или началом/концом строки, чтобы стать их собственным токеном) будут переключать слова, начинающиеся с a,e,i,o,u
163160
164161Несколько `++` и `--` могут быть вложенными. Тогда для каждого слова должны быть заданы `--` и `++`, прежде чем оно снова будет подсвечено.
165162
166163Then we extend the scanner. The pre-scan to store the information calls the same functions as the highlighter. It is automatically called, if anything changes. (It is called for all lines below the changed line, until a line returns the same Range-value as it already had) Затем мы расширяем сканер. Предварительное сканирование для хранения информации вызывает те же функции, что и highlighter. Он автоматически вызывается, если что-то меняется (он вызывается для всех строк ниже измененной строки, пока строка не вернет то же значение Range, что и раньше).
167164
168165Текущее количество "`--`" рассчитывается в
169166
170167``` pascal
171168 FCurRange: Integer;
172169```
173170
174171Количество уменьшается посредством "`++`"
175172
176173Для хранения информации мы используем:
177174
178175GetRange
179176Вызывается после полного сканирования строки, чтобы получить значение в конце строки. Значение будет сохранено.
180177
181178SetRange
182179Вызывается до сканирования линии. Устанавливает значение, хранящееся в конце предыдущей строки.
183180
184181ResetRange
185182Вызывается до сканирования 1-й строки (так как предыдущей строки нет).
186183
187184> **Note:** Сканирование запускается при *каждом* изменении строки (при каждом нажатии клавиши). Он сканирует текущую строку и все строки ниже, пока строка не вернет тот же диапазон, который у нее уже был. См.: <http://forum.lazarus.freepascal.org/index.php/topic,21727.msg139420.html#msg139420>
188185
189186### Важное замечание о диапазонах
190187
191188Назначение диапазона - позволить Highlighter начать сканирование в любой строке. Highlighter никогда не понадобится смотреть на предыдущую строку. Любая информация, необходимая для сканирования текущей строки, может быть получена из значения диапазонов.
192189
193190Пример:
194191
195192``` pascal
196193 writeln; (*
197194 readln;
198195 *)
199196```
200197
201198при сканировании "readln" Highlighter знает из диапазона, который находится в комментарии, ему не нужно осматривать предыдущие строки.
202199
203200Поэтому сканирование может начинаться с любой строки.
204201
205202Это также объясняет примечание в предыдущей главе: "пока строка не вернет тот же диапазон, который у нее уже был". Ибо, даже если текст строки не был изменен, если значение диапазона в начале строк изменилось, то результат сканирования также изменится.
206203
207204## Шаг 3: Добавление маркеров сворачивания текста
208205
209206Как указано выше, модуль **FoldHl** демонстрирует этот процесс.
210207
211208Например, highlighter должен сворачивать весь текст между отдельно стоящими "`-(-`", "`-)-`".
212209
213210Изменим наследование:
214211
215212``` pascal
216213 uses SynEditHighlighterFoldBase;
217214 ...
218215 TSynDemoHl = class(TSynCustomFoldHighlighter)
219216```
220217
221218Изменим способ хранения информации о диапазоне, поскольку базовый класс использует ее для информации о схлопывании текста:
222219
223220``` pascal
224221procedure TSynDemoHl.SetRange(Value: Pointer);
225222begin
226223 inherited;
227224 FCurRange := PtrInt(CodeFoldRange.RangeType);
228225end;
229226
230227procedure TSynDemoHl.ResetRange;
231228begin
232229 inherited;
233230 FCurRange := 0;
234231end;
235232
236233function TSynDemoHl.GetRange: Pointer;
237234begin
238235 CodeFoldRange.RangeType := Pointer(PtrInt(FCurRange));
239236 inherited;
240237end;
241238```
242239
243240Теперь добавим код в сканер, который сообщает highlighter'у об открытии и закрытии маркеров схлопывания текста:
244241
245242``` pascal
246243procedure TSynDemoHl.FindTokenEnd;
247244begin
248245 ...
249246
250247 if (FTokenEnd = FTokenPos+1) and (FLineText[FTokenPos] = '[') then
251248 StartCodeFoldBlock(nil);
252249 if (FTokenEnd = FTokenPos+1) and (FLineText[FTokenPos] = ']') then
253250 EndCodeFoldBlock();
254251end;
255252```
256253
257254- **Для 0.9.30**
258255
259256Пожалуйста, смотрите историю [этой страницы](http://wiki.lazarus.freepascal.org/index.php?title=SynEdit_Highlighter&oldid=76020), если вы используете версию 0.9.30.
260257
261258#### Подробнее о StartCodeFoldBlock / EndCodeFoldBlock
262259
263260``` pascal
264261 function StartCodeFoldBlock(ABlockType: Pointer; IncreaseLevel: Boolean = true): TSynCustomCodeFoldBlock; virtual;
265262```
266263
267264ABlockType
268265может использоваться для указания идентификатора блока.\
269266
270267Поле обычно не используется в качестве указателя (хотя это и не запрещено). Обычно идентификаторы - это числовое перечисление.\
271268Если у вас есть блоки разных типов (например, в Паскале: begin/end; repeat/until, ...), и вы не хотите, чтобы блок закрывался по неправильному ключевому слову ("end" не должен закрывать "repeat"), тогда вы можете дать им идентификаторы:
272269
273270``` pascal
274271StartCodeFoldBlock(PtrUInt(1)) // или другие числа
275272```
276273
277274IncreaseLevel
278275Если установлено значение False, будет вставлен блок, который нельзя свернуть.\
279276Подобные блоки можно использовать для внутреннего отслеживания.
280277
281278ПРИМЕЧАНИЕ: все уровни сворачивания текста должны быть вложенными, они не могут перекрываться. То есть последний открытый уровень сворачивания текста должен быть закрыт первым.\
282279Это относится к EndLvl, как показано в разделе [[SynEdit Highlighter/ru|Folding (сворачивание текста)]] выше.\
283280Перекрытия (как `IFDEF` и `begin` в IDE) не могут быть выполнены таким образом.
284281
285282``` pascal
286283 procedure EndCodeFoldBlock(DecreaseLevel: Boolean = True); virtual;
287284```
288285
289286DecreaseLevel
290287Параметр *должен* соответствовать `IncreaseLevel`, как это было указано в `StartCodeFoldBlock`\
291288
292289True означает, что уровень сворачивания текста заканчивается; False означает, что внутренний блок окончен. Если есть несоответствие, уровни сворачивания текста будут либо продолжаться, либо заканчиваться раньше.\
293290`TopCodeFoldBlockType` может использоваться для указания идентификатора самого внутреннего открытого блока. Можно использовать разные идентификаторы для внутренних блоков, и используя их, чтобы установить значение.
294291
295292``` pascal
296293 function TopCodeFoldBlockType(DownIndex: Integer = 0): Pointer;
297294```
298295
299296Возвращает идентификатор самого внутреннего блока.
300297
301298DownIndex
302299может быть использован для получения идентификатора для другого блока.
303300
304301`DownIndex=1` обозначает блок, который окружает самый внутренний.
305302
306303# Конфигурируемые Highlighter'ы (включая сторонние)
307304
308305## SynAnySyn
309306
310307Настоящий простой Highlighter. Более эталонная реализация, чем реальный пригодный для использования Highlighter.
311308
312309## SynFacilSyn
313310
314311Гибкий полностью настраиваемый Highlighter. См. [SynFacilSyn](https://github.com/t-edson/SynFacilSyn).
315312
316313# Ссылки
317314
318315Темы на форуме:
319316
320317- "Создание Highlighter'а для синтаксиса С" [[http://www.lazarus.freepascal.org/index.php/topic,10260.0.html topic,10260]]
321318- "Создание Syntax Highlighter" [[http://www.lazarus.freepascal.org/index.php/topic,10959.msg54714 topic,10959.msg54714]]
322319- Получение подсветки для печати [[http://www.lazarus.freepascal.org/index.php/topic,11384.msg57160.html|http://www.lazarus.freepascal.org/index.php/topic,11384.msg57160.html#msg57160 11384.msg57160]]
323320- Некоторые подробности о диапазонах и использовании объектов для хранения, подробнее [[http://forum.lazarus.freepascal.org/index.php/topic,21727.msg139354.html|http://forum.lazarus.freepascal.org/index.php/topic,21727.msg139354.html#msg139354 topic,21727.msg139354]] и [[http://forum.lazarus.freepascal.org/index.php/topic,21727.msg139419.html|http://forum.lazarus.freepascal.org/index.php/topic,21727.msg139419.html#msg139419 topic,21727.msg139419]]
324321- Подробнее о диапазонах, рабочей копии vs immutable, foldblocks...[[http://forum.lazarus.freepascal.org/index.php/topic,30685.msg195464.html|http://forum.lazarus.freepascal.org/index.php/topic,30685.msg195464.html#msg195464 http://forum.lazarus.freepascal.org/index.php/topic,30685]]
325322- Разметка (подсветка) всех вхождений токена (или регулярного выражения)[[http://forum.lazarus.freepascal.org/index.php?topic=26833 topic=26833]]
326323- Разметка и сворачивание информации:: [[http://forum.lazarus.freepascal.org/index.php?topic=30122]]
327324- Как сделать отмену в TSynEditMarkup [[http://forum.lazarus.freepascal.org/index.php/topic,30122.msg207931.html|http://forum.lazarus.freepascal.org/index.php/topic,30122.msg207931.html#msg207931]]
328325
329326- Сворачивание текста
330327 - "SynEdit - улучшенные highlighter'ы для обработки Code Folding?" [[http://www.lazarus.freepascal.org/index.php/topic,7879.0.html topic,7879]]
331328 - "SynEdit - Добавление поддержки свертывания кода для Java" [[http://www.lazarus.freepascal.org/index.php/topic,7338.0.html topic,7338]]
332329 - "CodeFolding" конфигурация [[http://www.lazarus.freepascal.org/index.php/topic,11064 topic,11064]]
333330 - Fold блокирует ключевое слово "end" (конец строки перед следующим ключевым словом) [[http://forum.lazarus.freepascal.org/index.php/topic,23411.msg139621.html|http://forum.lazarus.freepascal.org/index.php/topic,23411.msg139621.html#msg139621 topic,23411.msg139621]]
334331 - Сворачивание выделенного текста из кода (код пользователя / приложения):: [[http://forum.lazarus.freepascal.org/index.php/topic,24473.msg147312.html|http://forum.lazarus.freepascal.org/index.php/topic,24473.msg147312.html#msg147312 24473.msg147312]]
335332 - Получение состояния уровня сворачивания текста (сохранение состояния уровня сворачивания в сеансе работы) [[http://forum.lazarus.freepascal.org/index.php?topic=26748 topic=26748]]
336333
337334---
338335*From the Free Pascal wiki, page "SynEdit Highlighter/ru", as it stood on 2020-02-11 after a change by Andry, 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](/wiki-contact).*
339336Category: SynEdit

Back to history