Sign in Join
Wiki copytemporary

SynEdit Highlighter/ru: what changed

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

Title: SynEdit Highlighter/ru

OldNewLine
1+Для получения дополнительной информации о SynEdit перейдите к статье: [[SynEdit]]
2+
3+------------------------------------------------------------------------
4+
5+Прим.перев.: для облегчения читаемости статьи далее под термином **Highlighter** будет подразумеваться механизм(маркер) подсветки синтаксиса.
6+
7+------------------------------------------------------------------------
8+
9+# Понимание SynEdit Highlighter
10+
11+## Взаимоотношения SynEdit - Highlighter
12+
13+**SynEdit - Highlighter** имеют взаимоотношение **N к 1**.
14+
15+- Один экземпляр Highlighter может обслуживать **N** (много) экземпляров SynEdits
16+- Каждый SynEdit имеет только один Highlighter
17+- Но: один текст (текстовый буфер) может иметь много маркеров подсветки синтаксиса, если он используется несколькими SynEdit (каждый SynEdit будет иметь один HL, но все HL будут работать с одним и тем же документом)
18+
19+В результате:
20+
21+- Экземпляр Highlighter не имеет (фиксированной) ссылки на SynEdit.
22+
23+
24+(Однако Highlighter'ы хранят список SynEditTextBuffers, к которому они прикреплены)
25+
26+- Все данные для Highlighter (должны быть) сохранены в SynEdit (фактически в TextBuffer SynEdit (называемом «Линии»).
27+
28+Однако перед каждым вызовом Highlighter SynEdit гарантирует, что для Highlighter.CurrentLines будут установлены текущие строки SynEdits. Таким образом, маркер может получить доступ к данным в любое время. Формат хранения данных определяется маркером (TSynCustomHighlighter.AttachToLines).
29+
30+## Сканирование и возврат атрибутов подсветки
31+
32+Ожидается, что Highlighter будет работать на основе каждой строки.
33+
34+Если какой-либо текст был изменен, SynEdit будет вызывать (TSynCustomHighlighter.ScanFrom / в настоящее время вызывается из TSynEdit.ScanFrom) с диапазоном строк. Highlighter должен знать состояние предыдущей строки.
35+
36+Если требуются атрибуты подсветки, SynEdit будет запрашивать их также для каждой строки. SynEdit будет проходить через отдельные токены на линии. В настоящее время это происходит из вложенных процедур PaintLines в SynEdit.PaintTextLines. Он вызывает TSynCustomHighlighter.StartAtLineIndex, за которым следует HL.GetTokenEx/HL.GetTokenAttribute до тех пор, пока HL.GetEol имеет значение false.
37+
38+Кроме того, базовый класс для данных Highlighter'а (см. AttachToLines) основан на хранении данных на каждой строке, а TextBuffer (строки) SynEdit выполняет обслуживание этих данных для их синхронизации. То есть: всякий раз, когда строки текста вставляются или удаляются, записи также вставляются или удаляются из данных highlighter'ов (следовательно, в каждой строке должна быть одна запись).
39+
40+Обычно Highlighter'ы сохраняют статус конца строки в этом поле. Поэтому, если Highlighter будет работать со строчкой, он продолжит ввод состояния из предыдущей строки.
41+
42+## Folding (сворачивание текста)
43+
44+Событие фолдинга (схлопывания/сворачивания текста) SynEdit обрабатывается модулями SynEditFoldedView и SynGutterCodeFolding. Highlighter'ы, которые реализуют сворачивание, должны основываться на TSynCustomFoldHighlighter.
45+
46+Базовая информация для связи между SynEditFoldedView и Highlighter'ом требует 2 значения, сохраненных для каждой строчки (конечно, сам highlighter может хранить больше информации):
47+
48+- FoldLevel в конце строки
49+- Минимальный FoldLevel, встречающийся где-либо в строке
50+
51+Foldlevel указывает, сколько (вложенных) уровней схлопывания/сворачивания текста существует. Он повышается всякий раз, когда уровень сворачивания начинается, и понижается, когда уровень сворачивания заканчивается:
52+
53+``` pascal
54+ EndLvl MinLvl
55+ Procedure a; 1 - 0
56+ Begin 2 -- 1 -
57+ b:= 1; 2 -- 2 --
58+ if c > b then begin 3 --- 2 --
59+ c:=b; 3 --- 3 ---
60+ end else begin 3 --- 2 --
61+ b:=c; 3 --- 3 ---
62+ end; 2 -- 2 --
63+ end; 0 0 // Оператор end закрывает оба уровня сворачивания текста: операторов
64+ //begin и procedure
65+```
66+
67+На строке
68+
69+``` pascal
70+Procedure a; 1 - 0
71+```
72+
73+MinLvl равен 0, потому что строка началась с уровня 0 (и она никогда не спускалась / не закрывалась). Аналогично во всех строках, где есть только ключевое слово, открывающее уровень сворачивания текста ("begin").
74+
75+А строка
76+
77+``` pascal
78+ end else begin 3 --- 2 --
79+```
80+
81+начинается с уровня 3, а также заканчивается им (один закрытый, один открытый). Но так как она спускается первой, минимальный уровень, встречающийся где-либо в строке, равен 2.
82+
83+Без MinLvl было бы невозможно сказать, что уровень сворачивания текста заканчивается на этой строке.
84+
85+MaxLvl не существует, потому что уровни сворачивания текста, начинающиеся и заканчивающиеся на одной и той же линии, не могут быть свернуты в любом случае. Не нужно их обнаруживать.
86+
87+``` pascal
88+ if a then begin b:=1; c:=2; end; // нет уровня сворачивания текста на этой строке
89+```
90+
91+# Создание SynEdit Highlighter
92+
93+Начиная с версии 0.9.31 rev.35115, сворачивание текста в highlighter'е изменилось. Реализовать базовое свертывание текста теперь проще.
94+
95+Все исходники можно найти в каталоге установки Lazarus в: examples\SynEdit\NewHighlighterTutorial\\
96+
97+Проект HighlighterTutorial содержит 3 различных примера highlighter'ов:
98+
99+- SimpleHl: используется в шаге 1 ниже
100+- ContextHl: используется в шаге 2 ниже
101+- FoldHl: используется в шаге 3 ниже
102+
103+SimpleHl и ContextHl тоже будут работать с v.0.9.30 Лазаруса
104+
105+Сворачивание текста в highlighter'е в действии
106+
107+<figure>
108+<img src="SynEditFoldingHighlighterDemo.png" title="SynEditFoldingHighlighterDemo.png" />
109+<figcaption>SynEditFoldingHighlighterDemo.png</figcaption>
110+</figure>
111+
112+## Основы: возврат токенов и атрибутов
113+
114+Как указано, модуль **SimpleHl** демонстрирует этот процесс.
115+
116+### Что он делает
117+
118+- Он разбивает каждую строку на слова и пробелы (или табуляции)
119+ - Пробелы являются частью текста и также должны быть подсвечены
120+- Этот пример позволяет указать разные цвета для
121+
122+
123+
124+\- текста (по умолчанию не подсвечен)
125+
126+\- пробелов (по умолчанию в серебристой рамке)
127+
128+\- слов, разделенных пробелами, начинающихся с a,e,i,o,u (по умолчанию выделены жирным шрифтом)
129+
130+\- "не" слов (по умолчанию на красном фоне)
131+
132+### Как это работает
133+
134+- Creation
135+
136+Highlighter создает Атрибуты, которые могут вернуть Words(слова) и Spaces(пробелы).
137+
138+- SetLine
139+
140+Событие вызывается SynEdit до того, как будет прорисована строка (или до того, как потребуется информация о выделении)
141+
142+- GetTokenEx, GetTokenAttribute, Next, GetEol
143+
144+События используются SynEdit для перебора строки. Обратите внимание, что первый токен (слово или пробел) должен быть готов после SetLine, без вызова Next.
145+
146+> **Note:** токены, возвращаемые для каждой строки, должны представлять исходный текст строки и возвращаться в правильном порядке.
147+
148+- GetToken, GetTokenPos, GetTokenKind
149+
150+SynEdit использует их, например, для поиска подходящих скобок. Если tokenKind возвращает разные значения для каждого атрибута, то скобки совпадают только в том случае, если они имеют одинаковый вид (например, если был строковый атрибут, скобки вне строки не будут совпадать с скобками внутри строки).
151+
152+### Другие заметки
153+
154+Для удобства чтения highlighter не имеет оптимизации, поэтому он может быть очень медленным для больших текстов. Многие из поставляемых highlighter'ов используют хеш-функции, чтобы найти слово (или любую группу символов).
155+
156+## Шаг 2: Использование диапазонов
157+
158+Как указано, модуль **ContextHl** демонстрирует этот процесс.
159+
160+Следующий пример допускает влияние содержимого строки на последующие строки. Например: "`(*`" в Паскале делает все последующие строки комментарием, пока не будет найден "`*)`".
161+
162+Этот пример расширяет показанный выше **SimpleHl**: Токены `--` и `++` (должны быть обрамлены пробелом или началом/концом строки, чтобы стать их собственным токеном) будут переключать слова, начинающиеся с a,e,i,o,u
163+
164+Несколько `++` и `--` могут быть вложенными. Тогда для каждого слова должны быть заданы `--` и `++`, прежде чем оно снова будет подсвечено.
165+
166+Then 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, что и раньше).
167+
168+Текущее количество "`--`" рассчитывается в
169+
170+``` pascal
171+ FCurRange: Integer;
172+```
173+
174+Количество уменьшается посредством "`++`"
175+
176+Для хранения информации мы используем:
177+
178+GetRange
179+Вызывается после полного сканирования строки, чтобы получить значение в конце строки. Значение будет сохранено.
180+
181+SetRange
182+Вызывается до сканирования линии. Устанавливает значение, хранящееся в конце предыдущей строки.
183+
184+ResetRange
185+Вызывается до сканирования 1-й строки (так как предыдущей строки нет).
186+
187+> **Note:** Сканирование запускается при *каждом* изменении строки (при каждом нажатии клавиши). Он сканирует текущую строку и все строки ниже, пока строка не вернет тот же диапазон, который у нее уже был. См.: <http://forum.lazarus.freepascal.org/index.php/topic,21727.msg139420.html#msg139420>
188+
189+### Важное замечание о диапазонах
190+
191+Назначение диапазона - позволить Highlighter начать сканирование в любой строке. Highlighter никогда не понадобится смотреть на предыдущую строку. Любая информация, необходимая для сканирования текущей строки, может быть получена из значения диапазонов.
192+
193+Пример:
194+
195+``` pascal
196+ writeln; (*
197+ readln;
198+ *)
199+```
200+
201+при сканировании "readln" Highlighter знает из диапазона, который находится в комментарии, ему не нужно осматривать предыдущие строки.
202+
203+Поэтому сканирование может начинаться с любой строки.
204+
205+Это также объясняет примечание в предыдущей главе: "пока строка не вернет тот же диапазон, который у нее уже был". Ибо, даже если текст строки не был изменен, если значение диапазона в начале строк изменилось, то результат сканирования также изменится.
206+
207+## Шаг 3: Добавление маркеров сворачивания текста
208+
209+Как указано выше, модуль **FoldHl** демонстрирует этот процесс.
210+
211+Например, highlighter должен сворачивать весь текст между отдельно стоящими "`-(-`", "`-)-`".
212+
213+Изменим наследование:
214+
215+``` pascal
216+ uses SynEditHighlighterFoldBase;
217+ ...
218+ TSynDemoHl = class(TSynCustomFoldHighlighter)
219+```
220+
221+Изменим способ хранения информации о диапазоне, поскольку базовый класс использует ее для информации о схлопывании текста:
222+
223+``` pascal
224+procedure TSynDemoHl.SetRange(Value: Pointer);
225+begin
226+ inherited;
227+ FCurRange := PtrInt(CodeFoldRange.RangeType);
228+end;
229+
230+procedure TSynDemoHl.ResetRange;
231+begin
232+ inherited;
233+ FCurRange := 0;
234+end;
235+
236+function TSynDemoHl.GetRange: Pointer;
237+begin
238+ CodeFoldRange.RangeType := Pointer(PtrInt(FCurRange));
239+ inherited;
240+end;
241+```
242+
243+Теперь добавим код в сканер, который сообщает highlighter'у об открытии и закрытии маркеров схлопывания текста:
244+
245+``` pascal
246+procedure TSynDemoHl.FindTokenEnd;
247+begin
248+ ...
249+
250+ if (FTokenEnd = FTokenPos+1) and (FLineText[FTokenPos] = '[') then
251+ StartCodeFoldBlock(nil);
252+ if (FTokenEnd = FTokenPos+1) and (FLineText[FTokenPos] = ']') then
253+ EndCodeFoldBlock();
254+end;
255+```
256+
257+- **Для 0.9.30**
258+
259+Пожалуйста, смотрите историю [этой страницы](http://wiki.lazarus.freepascal.org/index.php?title=SynEdit_Highlighter&oldid=76020), если вы используете версию 0.9.30.
260+
261+#### Подробнее о StartCodeFoldBlock / EndCodeFoldBlock
262+
263+``` pascal
264+ function StartCodeFoldBlock(ABlockType: Pointer; IncreaseLevel: Boolean = true): TSynCustomCodeFoldBlock; virtual;
265+```
266+
267+ABlockType
268+может использоваться для указания идентификатора блока.\
269+
270+Поле обычно не используется в качестве указателя (хотя это и не запрещено). Обычно идентификаторы - это числовое перечисление.\
271+Если у вас есть блоки разных типов (например, в Паскале: begin/end; repeat/until, ...), и вы не хотите, чтобы блок закрывался по неправильному ключевому слову ("end" не должен закрывать "repeat"), тогда вы можете дать им идентификаторы:
272+
273+``` pascal
274+StartCodeFoldBlock(PtrUInt(1)) // или другие числа
275+```
276+
277+IncreaseLevel
278+Если установлено значение False, будет вставлен блок, который нельзя свернуть.\
279+Подобные блоки можно использовать для внутреннего отслеживания.
280+
281+ПРИМЕЧАНИЕ: все уровни сворачивания текста должны быть вложенными, они не могут перекрываться. То есть последний открытый уровень сворачивания текста должен быть закрыт первым.\
282+Это относится к EndLvl, как показано в разделе [[SynEdit Highlighter/ru|Folding (сворачивание текста)]] выше.\
283+Перекрытия (как `IFDEF` и `begin` в IDE) не могут быть выполнены таким образом.
284+
285+``` pascal
286+ procedure EndCodeFoldBlock(DecreaseLevel: Boolean = True); virtual;
287+```
288+
289+DecreaseLevel
290+Параметр *должен* соответствовать `IncreaseLevel`, как это было указано в `StartCodeFoldBlock`\
291+
292+True означает, что уровень сворачивания текста заканчивается; False означает, что внутренний блок окончен. Если есть несоответствие, уровни сворачивания текста будут либо продолжаться, либо заканчиваться раньше.\
293+`TopCodeFoldBlockType` может использоваться для указания идентификатора самого внутреннего открытого блока. Можно использовать разные идентификаторы для внутренних блоков, и используя их, чтобы установить значение.
294+
295+``` pascal
296+ function TopCodeFoldBlockType(DownIndex: Integer = 0): Pointer;
297+```
298+
299+Возвращает идентификатор самого внутреннего блока.
300+
301+DownIndex
302+может быть использован для получения идентификатора для другого блока.
303+
304+`DownIndex=1` обозначает блок, который окружает самый внутренний.
305+
306+# Конфигурируемые Highlighter'ы (включая сторонние)
307+
308+## SynAnySyn
309+
310+Настоящий простой Highlighter. Более эталонная реализация, чем реальный пригодный для использования Highlighter.
311+
312+## SynFacilSyn
313+
314+Гибкий полностью настраиваемый Highlighter. См. [SynFacilSyn](https://github.com/t-edson/SynFacilSyn).
315+
316+# Ссылки
317+
318+Темы на форуме:
319+
320+- "Создание Highlighter'а для синтаксиса С" [[http://www.lazarus.freepascal.org/index.php/topic,10260.0.html topic,10260]]
321+- "Создание Syntax Highlighter" [[http://www.lazarus.freepascal.org/index.php/topic,10959.msg54714 topic,10959.msg54714]]
322+- Получение подсветки для печати [[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]]
323+- Некоторые подробности о диапазонах и использовании объектов для хранения, подробнее [[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]]
324+- Подробнее о диапазонах, рабочей копии 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]]
325+- Разметка (подсветка) всех вхождений токена (или регулярного выражения)[[http://forum.lazarus.freepascal.org/index.php?topic=26833 topic=26833]]
326+- Разметка и сворачивание информации:: [[http://forum.lazarus.freepascal.org/index.php?topic=30122]]
327+- Как сделать отмену в TSynEditMarkup [[http://forum.lazarus.freepascal.org/index.php/topic,30122.msg207931.html|http://forum.lazarus.freepascal.org/index.php/topic,30122.msg207931.html#msg207931]]
328+
329+- Сворачивание текста
330+ - "SynEdit - улучшенные highlighter'ы для обработки Code Folding?" [[http://www.lazarus.freepascal.org/index.php/topic,7879.0.html topic,7879]]
331+ - "SynEdit - Добавление поддержки свертывания кода для Java" [[http://www.lazarus.freepascal.org/index.php/topic,7338.0.html topic,7338]]
332+ - "CodeFolding" конфигурация [[http://www.lazarus.freepascal.org/index.php/topic,11064 topic,11064]]
333+ - 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]]
334+ - Сворачивание выделенного текста из кода (код пользователя / приложения):: [[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]]
335+ - Получение состояния уровня сворачивания текста (сохранение состояния уровня сворачивания в сеансе работы) [[http://forum.lazarus.freepascal.org/index.php?topic=26748 topic=26748]]
336+
337+---
338+*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).*
339+Category: SynEdit

Back to history