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
| Old | New | Line | |
|---|---|---|---|
| 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 |
