Sign in Join
Wiki copytemporary

ATSynEdit: what changed

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

OldNewLine
11# About
22
33**ATSynEdit** is the multi-line editor control.
44
55- Homepage: <https://github.com/Alexey-T/ATSynEdit>
66- Support for EControl adapter: <https://github.com/Alexey-T/EControl>
77
88Author: Alexey Torgashin
99
10-<figure>
11-<img src="atsynedit.png" title="atsynedit.png" />
12-<figcaption>atsynedit.png</figcaption>
13-</figure>
10+![](/wiki-images/e/eb/Atsynedit.png)
1411
1512# License
1613
1714ATSynEdit files licensed under MPL 2.0. You can optionally use them under LGPL if your project needs it.
1815
1916EControl files licensed under special license (readme.txt), usage allowed only in open-source projects.
2017
2118# Keyboard shortcuts
2219
2320Run demo_editor and call menu item "Help - Commands'. You'll see all shortcuts in a dialog like this:
2421
25-<figure>
26-<img src="atsynedit_keymap.png" title="atsynedit_keymap.png" />
27-<figcaption>atsynedit_keymap.png</figcaption>
28-</figure>
22+![](/wiki-images/d/db/Atsynedit_keymap.png)
2923
3024# Mouse shortcuts
3125
3226Multi-carets:
3327
3428- Ctrl+click - add/delete caret
3529- Ctrl+drag - add caret with selection
3630- Ctrl+Shift+click - add carets column in several lines (from previous caret to clicked line)
3731
3832Select:
3933
4034- Alt+drag - select column of text (Note: it may look weird if word-wrap on, because wrap is not considered here at all. Simple rectangle of coordinates \[x1,y1\]-\[x2,y2\] is always selected, even if this gives bad looking screen)
4135- drag on gutter's line numbers - select by entire lines
4236- double-click and immediately drag - select text by words
4337
4438Clicks:
4539
4640- double-click - select clicked word
4741- triple-click - select entire line (limited by end-of-lines)
4842- middle-button click - start "Browser Scroll" mode: circle mark appears and mouse moving around this mark auto-scrolls text in 4 directions; speed of scrolling depends on distance of cursor from circle mark (any click to turn off)
4943
5044# Multi-carets
5145
5246Multi-carets are several carets at once. All carets work together for many editing commands: caret moving, text typing, deleting, selection with keyboard. See "Mouse shortcuts", how to add/remove carets.
5347
5448Animation:
5549
56-<figure>
57-<img src="atsynedit-carets.gif" title="atsynedit-carets.gif" />
58-<figcaption>atsynedit-carets.gif</figcaption>
59-</figure>
50+![](/wiki-images/c/cf/Atsynedit-carets.gif)
6051
6152## Multi-selections
6253
6354If you add caret with Ctrl+click, caret has no selection. If you add caret with Ctrl+drag, caret will have selection. You can add selections to carets later, by Shift+arrows, Shift+Home, Shift+End etc.
6455
6556Multi-selections are handled specially on copy/paste. If you copy selections, then move carets, then paste, paste will insert clipboard lines into carets: line-1 at caret-1, line-2 at caret-2 etc (only if carets count equals to lines count in clipboard, otherwise result is different).
6657
6758Animation shows this:
6859
69-<figure>
70-<img src="atsynedit-sel.gif" title="atsynedit-sel.gif" />
71-<figcaption>atsynedit-sel.gif</figcaption>
72-</figure>
60+![](/wiki-images/6/67/Atsynedit-sel.gif)
7361
7462## Clipboard commands with selections
7563
7664Clipboard-related commands work with carets, both with selections and without them. Some details about this:
7765
78-<table border="1">
79-
80-<tr>
81-
82-<th>
83-
8466Command
8567
86-</th>
87-
88-<th width="42%">
89-
9068Behaviour, when there're no selections
9169
92-</th>
93-
94-<th width="42%">
95-
9670Behaviour, when at last one selection present
9771
98-</th>
99-
100-</tr>
101-
102-<tr>
103-
104-<td>
105-
10672Copy to clipboard
10773
108-</td>
109-
110-<td>
111-
11274Copies entire lines, containing carets.
11375
11476`                   (Ignores multiple carets on same line.)`
11577
116-</td>
117-
118-<td>
119-
12078Copies only selections text.
12179
12280`                   (Ignores carets without selections.)`
12381
124-</td>
125-
126-</tr>
127-
128-<tr>
129-
130-<td>
131-
13282Cut to clipboard
13383
134-</td>
135-
136-<td>
137-
13884Similarly to "Copy" w/o selections.
13985
140-</td>
141-
142-<td>
143-
14486Similarly to "Copy" with selections.
14587
146-</td>
147-
148-</tr>
149-
150-<tr>
151-
152-<td>
153-
15488Paste from clipboard
15589
156-</td>
157-
158-<td colspan="2">
159-
16090First, selections are cleared (deleted). Then, command pastes text into each caret position. Special case is when clipboard lines count equals to carets count - in this case, first line is inserted at first caret, second line is inserted at 2nd caret, etc.
16191
162-</td>
163-
164-</tr>
165-
166-<tr>
167-
168-<td>
169-
17092Delete char
17193
172-</td>
173-
174-<td>
175-
17694Deletes one char at each caret position.
17795
178-</td>
179-
180-<td>
181-
18296Deletes only selections text. (Ignores carets without selections.)
18397
184-</td>
185-
186-</tr>
187-
188-</table>
189-
19098# Help topics
19199
192100## Compilation errors
193101
194102**IME error on GTK2.**
195103
196104You may get this error:
197105
198106`atsynedit_adapter_gtk2ime.pas(131,5) Error: Identifier not found "IM_Context_Set_Cursor_Pos"`
199107
200108To fix it, edit the file "atsynedit/atsynedit_package.lpk" and remove this block there:
201109
202110``` xml
203111 <Other>
204112 <CustomOptions Value="-dGTK2_IME_CODE"/>
205113 <OtherDefines Count="1">
206114 <Define0 Value="GTK2_IME_CODE"/>
207115 </OtherDefines>
208116 </Other>
209117```
210118
211119Alternative solution when symbol "IM_Context_Set_Cursor_Pos" cannot be found: you need to open IDE dialog "Tools / Configure 'Build Lazarus'", and there enable the define: WITH_GTK2_IM; then recompile the IDE.
212120
213121## What limitations it has?
214122
215123- All lines have the same height
216124- Cannot set different fonts for different parts
217125- No support for right-to-left mode, only limited support for Arabic text
218126
219127## What features it has?
220128
221129- Fast editing of big texts (tested 10M log)
222130- Syntax highliting using 'adapters'
223131- Multi-carets from birth. All code carets aware.
224132- Strings object is holder of text. 2+ editors can have 1 strings obj. (Split editor.)
225133- Unicode (used UnicodeString in Strings obj)
226134- Word-wrap (wrap at control edge, wrap at column)
227135- Inter-line gaps (see #Gaps_object)
228136- Undo/redo
229137- Column blocks
230138- Gutter (line numbers, folding, colored line states)
231139- Bookmarks (with icons)
232140- Supports Win/Unix/Mac line ends
233141- Minimap like Sublime Text
234142- Micromap (owner drawn)
235143- Ruler (at top edge)
236144- Renders wrapped indent (line contains some indent, wrapped parts contain the same indent)
237145- Renders CJK chars with big width
238146- Renders some Unicode chars as "AABB" hex code
239147- Renders unprintable chars (space, tab, line end)
240148- Can highlight/activate URLs
241149- Can set color attributes for ranges (to highlight pair brackets or same words)
242150- Supports proportional fonts
243151
244152## Difference from other components
245153
246154- Adapters to support syntax highlighting. In theory, using adapters it's possible to support syntax files from any app: Sublime Text, Atom, EmEditor etc.
247155- Muti-carets, multi-selections
248156- Inter-line gaps, can insert pictures in it
249157- Minimap, Micromap (rare in others editors)
250158- Smooth per-pixel scrolling (option)
251159
252160## Does it need to be installed?
253161
254162It may be installed or not.
255163
256164- Use not installed: see below "Basic example how to use".
257165- Use installed: in Lazarus IDE, open package file "atsynedit\atsynedit_package". Install it in Package dialog. You 'll have components installed in "Misc" page of component pallette: TATSynEdit, TATEdit (single line edit), etc.
258166
259167## What parts does it have?
260168
261169### Strings object
262170
263171type TATStrings in unit atstrings. Storage of text lines. It includes managing of Undo/Redo too.
264172
265173Methods:
266174
267175- To add/delete lines: LineAdd, LineInsert, LineDelete. They automatically create/update Undo/Redo data. Methods with "Raw" word in name: ignore Undo/Redo data, usually don't use them.
268176- To insert/delete blocks of lines: LineBlockInsert, LineBlockDelete.
269177- Methods starting with "Text" support basic text operations: TextReplace, TextAppend, TextInsert etc. They operate on one caret position and get parameters AShift (shift of caret after text operation, by x/y) and APosAfter (position of caret after the operation).
270178- To load entire content into Strings object: LoadFromFile, LoadFromStream, LoadFromString (multi-line string).
271179
272180To get/set lines, use properties:
273181
274182- Lines\[i\]: text, of type UnicodeString
275183- LinesUTF8\[i\]: text, of type string (faster than Lines\[\])
276184- LinesLen\[i\]: integer: same as Length(Lines\[i\]), but faster
277185- LinesEnds\[i\]: enum: kind of end-of-line chars
278186- LinesHidden\[i\]: bool: true if line is fully hidden (inside folded block)
279187- LinesFoldFrom\[i\]: byte: 0 if line is not folded, \>0 if line is folded from this char-pos
280188- LinesState\[i\]: enum: line state, normal/changed/saved, it's shown by color on gutter
281189- LinesSeparator\[i\]: enum: kind of horizontal separator line to paint near text (rarely needed for apps).
282190
283191Auto-adjustments of other objects when lines are added/deleted:
284192
285193- Strings object updates Strings.Bookmarks, Strings.Bookmarks2 objects.
286194- Strings object updates Gaps (actually Strings.Gaps) object.
287195- If Strings.GutterDecor1 / .GutterDecor2 properties are set, Strings object updates GutterDecor object(s). Properties are not set in ATSynEdit, application must set them.
288196- Objects Attribs / Markers / HotSpots are updated not inside Strings object, but in main ATSynEdit code.
289197
290198### Strings.Bookmarks object
291199
292200type TATBookmarks in unit atsynedit_bookmarks. Holds list of bookmarks of type TATBookmarkItem. Each item has props:
293201
294202- Data.LineNum: line index (in Strings object)
295203- Data.Kind: int kind value
296204- Data.Hint: tooltip string, which is shown on mouse-over for bookmark gutter icon
297205- Data.Tag: int value, for custom application needs
298206- Data.DeleteOnDelLine: bool, enables to auto delete item, when its line is deleted
299207- Data.ShowInBookmarkList: bool, for custom application needs
300208
301209Kind specifies background color and gutter icon for bookmarks of this kind. Kind value 0 must not be used (previously value 0 meaned "no bookmark"), value 1 should be used for "usual" bookmarks, value\>1 should be used for custom application bookmarks. ATSynEdit has 2 events: to paint bookmark icon (from its kind), and to get color of bookmark (from its kind).
302210
303211Bookmarks object is always sorted by line index. Duplicate bookmarks (for the same line index) are auto removed. To change bookmarks, use methods Add, Delete, DeleteForLine, Clear. Also use method Find.
304212
305213### Strings.Bookmarks2 object
306214
307215Same type as Strings.Bookmarks object. This list holds "background bookmarks", which have lower paint priority than usual bookmarks. So if a line has usual bookmark - it is painted, and if not - background bookmark is searched for this line and painted. Bookmarks2 allows to colorize entire lines, but not to interfere with usual bookmarks - it is "passive coloring". Additionally, marks in Bookmarks2 don't have gutter icon, and mouse-over tooltip. Marks in Bookmarks/Bookmarks2 use the same Kind values space.
308216
309217### Carets object
310218
311219type TATCarets in unit atsynedit_carets. Holds list of items of type TATCaretItem. Each caret item has properties:
312220
313221- PosY: index of line in Strings object, 0-based
314222- PosX: offset of caret from line start, 0-based
315223- EndY, EndX: position for selection edge for this caret, or -1/-1 if no selection for this caret
316224- CoordY, CoordX: screen coordinates (usually you don't need these)
317225
318226For forward sel, pair (PosY, PosX) is after pair (EndY, EndX); for backward sel the order is reversed. So if you want to get pos of selection start (e.g. to delete this selection), get minimal pair.
319227
320228Carets object is sorted by text position, method Sort does this. Duplicate carets (they appear during work with multi-carets) are auto removed. If you try to add overlapping caret selections, they will be auto merged to one caret. To add/delete carets, use methods Add, Clear. Also ATSynEdit has methods DoCaretNNNNN, eg DoCaretSingle makes single caret.
321229
322230### CaretsSel object
323231
324232type TATCaretSelections in unit atsynedit_carets. It is helping object, internally it holds array of carets coordinates, sorted by text position, but only for carets with selection (carets w/o selection are not included). It is read-only, ie it's needed only to find some info about selections, not to store it. Finding some info about selections is much faster with CaretsSel object, though you can use Carets object too (slower). It uses binary search.
325233
326234Note: object is updated (from contents of Carets) in Paint method, not immediately after you add/change something in Carets.
327235
328236### Keymap object
329237
330238type TATKeymap in unit atsynedit_keymap.
331239
332240Holds list of keyboard actions of type TATKeymapItem. It's inited at start. And to init items at start, two "filling" procedures exist in code: one for TATSynEdit, and simpler one for TATEdit (single line version, less commands). And you can add more keymap items or change hotkeys for any keymap item.
333241
334242Each keymap item has 2 (yet) hotkeys, second is needed for e.g.: Copy-to-clipboard (Ctrl+C and Ctrl+Ins), Paste-from-clipbd (Ctrl+V and Shift+Ins) etc.
335243
336244When adding keymap item, you can set simple hotkeys (e.g. "Alt+Shift+F") or key combos (several simple hotkeys which must be pressed in order). Example adds commands with simple hotkey and combo:
337245
338246``` pascal
339247 var
340248 M: TATKeymap;
341249 const
342250 cmd_My1 = 3000;
343251 cmd_My2 = 3001;
344252 begin
345253 M.Add(cmd_My1, 'cmd with simple hotkey', ['Ctrl+Shift+F'], []);
346254 M.Add(cmd_My2, 'cmd with 3 keys combo', ['Ctrl+B', 'Ctrl+B', 'Ctrl+M'], []);
347255```
348256
349257After you added new keymap items, add reaction for them: use OnCommand event as usual.
350258
351259### WrapInfo object
352260
353261type TATWrapInfo in unit atsynedit_wrapinfo. Holds list of wrap items, each is info about wrapped part of line or about entire line:
354262
355263- its original line index in Strings object (0-base)
356264- its char offset in original line (0-base)
357265- its length
358266- its indent size (if feature "wrapped indent" is on)
359267- its final-state: is it first/middle part of wrapped line, or final part
360268
361269One wrap item renders on control as one short line. It is part of wrapped line or entire line. Also used for wrap-mode off, this is simple case - one wrap item per one line. Wrap items exist only for lines not hidden by folding feature (with Strings.LinesHidden\[i\]=true).
362270
363271WrapInfo has weird property VirtualMode. This prop is set if a) no fold ranges created (ie no lexer is active, or lexer didn't create fold ranges), b) word wrapping is off. VirtualMode means that WrapInfo don't store any data in internal TList, it is empty and fast, it only reads props of Strings and returns them. Indeed, when no fold ranges, and word wrapping off, all items of WrapInfo must be simple "mirror" of Strings items props (and count is the same).
364272
365273VirtualMode makes loading of huge logs (100-500Mb) ~1.5 times faster.
366274
367275### Undo object
368276
369277Holds list of actions for Undo/Redo. You must not touch it. Filled auto by changing-adding-deleting of lines in Strings object (by any API). Also holds end-of-lines markers (changed ends-of-lines can undo too).
370278
371279Items in Undo object have 'group marker'. This means that some editor commands set this marker, and items after marked item will undo as a group (if option "Group undo" on). You can set group marker (for next editor action) by method SetGroupMark of Strings object.
372280
373281### Gutter object
374282
375283type TATGutter in unit atsynedit_gutter. Holds list of gutter items. Each gutter item has properties:
376284
377285- Visible: gutter band is shown on gutter
378286- Size: width of gutter band
379287- Left/Right: calculated auto from Visible and Size, coordinates of band on control
380288
381289To know which band of gutter does what, use properties of ATSynEdit: GutterBandNum (index of band for line numbers), GutterBandFold (index of band for folding) etc. Example shows/hides gutter column:
382290
383291``` pascal
384292 ed.Gutter[ed.GutterBandNum].Visible:= checkGutterNum.Checked;
385293 ed.Gutter.Update;
386294 ed.Update;
387295```
388296
389297### GutterDecor object
390298
391299type TATGutterDecor in unit atsynedit_gutter_decor. Holds list of items, which are painted over gutter, for decoration purpose. Items can hold short text strings (e.g. normal/square/curly brackets to show pair brackets in text), or icon from TImageList. If item has not empty Text, then text is shown, otherwise icon is shown. Each item has props:
392300
393301- Data.LineNum: line index (in Strings object)
394302- Data.Tag: int value, for custom app needs
395303- Data.ImageIndex: icon index in TATSynEdit.ImagesGutterDecor image list
396304- Data.Text: short string
397305- Data.TextBold: bool, text font is bold
398306- Data.TextItalic: bool, text font is italic
399307- Data.TextColor: color of text
400308- Data.DeleteOnDelLine: bool, enables to auto delete item, when its line is deleted
401309
402310Items in this object are auto adjusted when text is edited. But note: for this to work, you must assign GutterDecor object to Strings object prop GutterDecor1 or GutterDecor2.
403311
404312Duplicate items (for the same line index) are auto removed. TATSynEdit.GutterBandDecor sets index of gutter band for decorations. If -1, then gutter band for bookmarks is used.
405313
406314### Fold object
407315
408316type TATSynRanges in unit atsynedit_ranges. Holds list of ranges which can be folded and show plus/minus on gutter. Each range is of type TATSynRange, it has properties:
409317
410318- Y: starting line of range (0-based)
411319- Y2: ending line of range (if same as Y, plus/minus not shown on gutter)
412320- X: char-offset in starting line (must be \>0, 1 means fold from 1st char)
413321- Folded: boolean, state: folded/unfolded
414322- Staple: boolean, true if range has staple shown (vertical line on text area)
415323- Hint: string, shown in rectangle when range is folded
416324
417325Example adds few ranges to text:
418326
419327``` pascal
420328 Edit1.Fold.Clear;
421329 Edit1.Fold.Add(1, 4, 15, false, ''); //line 4 to 15, range1
422330 Edit1.Fold.Add(1, 5, 9, false, ''); //line 5 to 9, nested into range1
423331 Edit1.Fold.Add(1, 7, 8, false, ''); //line 7 to 8, nested into range2
424332```
425333
426334- To fold/unfold a range, don't change Folded field, use methods of ATSynEdit: DoRangeFold, DoRangeUnfold.
427335- To fold/unfold all, use commands via DoCommand method: cCommand_FoldAll, cCommand_UnfoldAll.
428336- In Fold object, items are not sorted, but usually syntax-adapters add these ranges as sorted (by Y).
429337- Fold object has methods, which find ranges, e.g. all ranges which contain any line from N1 to N2, etc.
430338
431339### Markers object
432340
433341type TATMarkers in unit atsynedit_markers. Holds list of marker items. Each marker item has properties:
434342
435343- PosX, PosY: position of marker (like caret position).
436344- LineLen: if not 0, render horizontal line to the left (LineLen\<0) or right (LineLen\>0).
437345- CoordX, CoordY: screen coordinates.
438346- CoordX2, CoordY2: screen coordinates of LineLen end.
439347- SelX, SelY: used in CudaText: when "collect marker" gets this marker, caret will be with selection.
440348 - if SelY=0 - SelX is length of selection (single line selection)
441349 - if SelY\>0 - SelY is Y-delta of selection end, SelX is absolute X of selection-end
442350- Tag (Int64): some integer value, useful for apps: e.g. CudaText holds here tabstop-index (several markers with the same index \>0 give multi-carets, when user jumps to one of markers).
443351- Value (Int64): some integer value, attached to marker.
444352- Ptr (TObject): some application-specific object. Object is freed automatically when marker item is freed.
445353- MicromapOnly (enum): where to render the marker: on text area, on micromap, on both text/micromap.
446354
447355Markers are painted as red triangles below their positions (PosX, PosY). They are needed e.g. for making snippets, where user needs to jump over snippet's insert points and wants to see these points. Also it's good for IDE to have commands like "drop marker here", "remove last marker", "swap caret and last marker" etc (like CodeRush).
448356
449357Example code from CudaText:
450358
451359``` pascal
452360procedure EditorMarkerGotoLast(Ed: TATSynEdit; AndDelete: boolean);
453361var
454362 Caret: TATCaretItem;
455363 Mark: TATMarkerItem;
456364begin
457365 if Ed.Carets.Count<>1 then exit;
458366 if Ed.Markers.Count=0 then exit;
459367 Caret:= Ed.Carets[0];
460368 Mark:= Ed.Markers[Ed.Markers.Count-1];
461369 Caret.PosX:= Mark.PosX;
462370 Caret.PosY:= Mark.PosY;
463371 if AndDelete then
464372 Ed.Markers.Delete(Ed.Markers.Count-1);
465373 Ed.DoGotoCaret(cEdgeTop);
466374 Ed.Update;
467375end;
468376```
469377
470378### Attribs object
471379
472380type TATMarkers (same as for Markers). Holds list of additional color/font attribute items, these items are added to syntax hiliting and selection hiliting. Each item has properties:
473381
474382- PosX, PosY: see Markers object.
475383- SelX, SelY: see Markers object.
476384- Ptr (TObject): object of type TATLinePartClass, which has Data field. This Data is record, it contains all color/font/borders properties of this attr.
477385
478386Attribs object is used in CudaText: app adds attr items to hilite brackets, to hilite misspelled words, etc.
479387
480388If you add attrib item, create new object of type TATLinePartClass, fill its Data, and pass object to TATMarkers.Add. This object is freed automatically if attrib item deleted or all attribs cleared.
481389
482390### Colors object
483391
484392Contains color properties of type TColor. For ex, Colors.TextFont is text font color, Colors.TextBG is text background color.
485393
486394### Gaps object
487395
488396type TATGaps in unit atsynedit_gaps. Holds list of items TATGapItem. Each item has properties:
489397
490398- LineIndex: index in Strings object; and special value -1 is allowed: gap before the first line
491399- Size: height in pixels
492400- Tag: int value, to difference gaps from several plugins
493401- Bitmap: TBitmap which is painted on gap, when it's visible
494402- DeleteOnDelLine: bool, enables to auto delete item, when its line is deleted
495403
496404Gap item paints as rectangle (it is owner-drawn), just between lines N and N+1, not overlapping text. When user adds/deletes lines, gaps auto-shift with their lines; gap is deleted if its line is deleted.
497405
498406Use cases:
499407
500408- App wants to show picture preview, when picture is linked in HTML. It creates small preview picture e.g. 100x50, and adds gap: gap size=52, picture height and 2 pixels of border.
501409- App runs linter on JS code, for JS errors it places bookmarks, and adds gaps with size=15 for error lines: in gaps it writes errors, maybe icons.
502410
503411To add/remove gaps:
504412
505413``` pascal
506414 //to add
507415 ed.Gaps.Add(NLine, NGapHeight, FSomeBitmap, NGapTag);
508416 ed.Update;
509417 //to delete this gap
510418 ed.Gaps.DeleteForLineRange(NLine, NLine);
511419 ed.Update;
512420```
513421
514422Event OnDrawGap is used to paint gaps, it gets canvas, and rectangle on canvas. Note: if you add gap to the last line, you must turn on option OptLastLineOnTop, better don't turn it off later (to not scroll, when gap added/removed).
515423
516424### DimRanges object
517425
518426type TATDimRanges in unit atsynedit_dimranges. Holds list of ranges which shade/dim/fade text font color, ie blend font color with background color. Each range is of type TATDimRange, it has properties:
519427
520428- LineFrom: range starts from this line (0-based)
521429- LineTo: range ends on this line (can use MaxInt)
522430- DimValue: dim value from 0 to 255. 0 means "no effect", 255 means "transparent text".
523431
524432To dim all text except current paragraph, you must add 2 ranges: before/after paragraph, then correct line indexes in them.
525433
526434There is no checking for line indexes, ranges can have any indexes, but overlapping indexes not good (first found range will give dim value).
527435
528436Example adds 2 ranges and changes them later:
529437
530438``` pascal
531439 R1:= Ed.DimRanges.Add(0, 6, 100{dim value});
532440 R2:= Ed.DimRanges.Add(20, MaxInt, 100{dim value});
533441 Ed.Update;
534442 //change ranges
535443 R1.LineTo:= 10;
536444 R2.LineFrom:= 30;
537445 Ed.Update;
538446```
539447
540448### Hotspots object
541449
542450type TATHotspots in unit atsynedit_hotspots. Holds list of ranges, which need special tooltip on mouse-over. Each range is of type TATHotspotItem, it has props:
543451
544452- PosX, PosY: start of range (text position)
545453- EndX, EndY: end of range
546454- Tag: some custom int value
547455- TagString: some custom string value
548456
549457You can add hotspots via Add method, and find them later by Tag, by TagString, by text position - methods FindByPos, FindByTag, FindByTagString. To support mouse-over for these ranges, two events exist: OnHotspotEnter, OnHotspotExit.
550458
551459Example of usage: in HTML file, app can find all HTML color tokens \#rrggbb, add hotspots for them, and store color value to Tag. Then on mouse-over app can show colored tooltip.
552460
553461### Micromap object
554462
555463type TATMicromap in unit atsynedit_micromap. Holds list of micromap's columns (by default only one column is added, and CudaText adds 2nd column to paint marks from plugins). The micromap is shown by ATSynEdit property OptMicromapVisible and must be painted by the app via OnDrawMicromap event. Micromap object has Columns property, "array of TATMicromapColumn".
556464
557465Type TATMicromapColumn is record with the fields:
558466
559467- NTag: Int64 tag value, so app can find this column by tag - app can change this
560468- NWidthPercents: Width of column in percents of average char width - app can change this
561469- NWidthPixels: Width of column in pixels - auto calculated, app must not change this
562470- NLeft, NRight: Control-related coordinates of column left and right edge - auto calculated, app must not change this
563471
564472Micromap object holds Columns prop, and several methods to add/delete columns: ColumnAdd, ColumnDelete, and method ColumnFromTag to find columns by int tag. Application can paint anything on micromap, but it's recommented to paint small marks per editor lines. To get coordinates of such marks, use ATSynEdit method RectMicromapMark, it does calculations to get coords for all editor lines, to fill the entire micromap.
565473
566474## What are the basic methods/properties?
567475
568476Objects
569477
570478- property Strings
571479- property Strings.Bookmarks
572480- property Strings.Bookmarks2
573481- property Carets
574482- property Gutter
575483- property GutterDecor
576484- property Keymap
577485- property Fold
578486- property Markers
579487- property Attribs
580488- property Colors
581489- property WrapInfo
582490- property Gaps
583491- property DimRanges
584492- property Hotspots
585493- property Micromap
586494
587495Files
588496
589497- procedure LoadFromFile(const AFilename: string): load text from file (filename uses utf-8)
590498- procedure SaveToFile(const AFilename: string): save text to file
591499
592500States
593501
594502- property Modified: modified state (cleared on file loading)
595503- property ModeOverwrite: mode insert/overwrite state (Ins key toggles it)
596504- property ModeReadOnly: mode read-only state
597505
598506More
599507
600508- procedure Update(AUpdateWrapInfo: boolean = false; AUpdateCaretsCoords: boolean = true): to repaint+invalidate; 1st param must be True to recalculate WrapInfo object (usually it's auto updated, but if text change detection don't work, e.g. Strings object changed directly, pass True here)
601509- procedure DoCommand(ACmd: integer; const AText: UnicodeString = ""): command runner, pass command code to run it (see file atsynedit_commands)
602510- property Text: unicodestring which gives entire editor text with LF separator
603511
604512Caret
605513
606514- procedure DoCaretSingle(AX, AY: integer): make single caret
607515- function CaretPosToClientPos(P: TPoint): TPoint: convert caret coords to screen coords
608516- function ClientPosToCaretPos(P: TPoint; out AEndOfLinePos: boolean): TPoint: convert screen coords to caret coords
609517- function IsLineWithCaret(ALine: integer): boolean: is line index contains any caret
610518
611519Selection
612520
613521(P.Y is line index, P.X is char offset in line, 0-based)
614522
615523- property SelRect: for column selection, rectangle of column block
616524- function IsSelRectEmpty: is column selection exists
617525- procedure DoSelect_All: select all text (makes single caret)
618526- procedure DoSelect_Line(P: TPoint): select single line
619527- procedure DoSelect_Word(P: TPoint): select single word
620528- procedure DoSelect_LineRange(ALineFrom: integer; P: TPoint): select several lines from line index to given point
621529
622530View
623531
624532- property LineTop: index of line visible at the top of control (almost vertical scroll, though exact scroll pos is different, it considers WrapInfo object)
625533- property LineBottom: index of line visible at the bottom of control (because of folding you cannot simply calculate it)
626534- property ColumnLeft: index of left visible column (ie, horizontal scroll)
627535- procedure DoRangeFold(ARange: TATSynRange): fold range, which exists in the Fold object
628536- procedure DoRangeUnfold(ARange: TATSynRange): unfold range
629537- procedure DoScrollByDelta(Dx, Dy: integer): scroll editor area by Dx chars right and Dy lines down
630538- procedure DoGotoPos(APnt: TPoint; AIndentHorz, AIndentVert: integer): scroll editor to given caret pos
631539
632540Options
633541
634542I cannot post here options. See ATSynEdit properties beginning with "Opt", these are props which can change in design time in IDE. You can place on form ATSynEdit and see in IDE its "Opt" properties and see their values.
635543
636544Encodings
637545
638546- Strings.EncodingDetect: boolean
639547- Strings.Encoding: enum (cEncAnsi, cEncUTF8, cEncWideLE, cEncWideBE)
640548- Strings.EncodingCodepage: string (used for Encoding=cEncAnsi)
641549- Strings.SaveSignUtf8: boolean
642550
643551## Basic example how to use?
644552
645553- Add "uses ATSynEdit".
646554- Add variable "ed".
647555- Add OnCreate event to new form.
648556- How to load file "unit1.pas":
649557
650558``` pascal
651559 procedure TForm1.FormCreate(Sender: TObject);
652560 begin
653561 ed:= TATSynEdit.Create(Self);
654562 ed.Parent:= Self;
655563 ed.Align:= alClient;
656564 ed.Font.Name:= 'Courier New';
657565 ed.OptUnprintedVisible:= false;
658566 ed.OptRulerVisible:= false;
659567 ed.LoadFromFile(ExtractFilePath(Application.ExeName)+'unit1.pas');
660568 end;
661569```
662570
663571- How to save file: call "ed.SaveToFile()".
664572
665573## How does it manage the coordinates?
666574
667575Carets object contains list of caret items. Each caret item, e.g. Carets\[0\], has properties:
668576
669577- PosY: line index in Strings object
670578- PosX: char offset from line start
671579- EndY, EndX: selection end for caret, both -1 if no selection
672580- CoordX, CoordY: control-relative coordinates of caret
673581
674582Strings object holds UnicodeString's, where each WideChar gives offset=1, and even Tab char gives offset=1. Component auto-calculates properties CoordX/CoordY for all carets, screen coordinates. Don't touch CoordX/CoordY, change only PosX/PosY and EndX/EndY.
675583
676584### Text position to/from screen coordinates
677585
678586Methods allow to convert text position to/from control-relative coordinates (in pixels). Layout of gutter, ruler and other UI elements are already considered by methods.
679587
680588``` pascal
681589 function CaretPosToClientPos(P: TPoint): TPoint;
682590 function ClientPosToCaretPos(P: TPoint;
683591 out ADetails: TATPosDetails;
684592 AGapCoordAction: TATGapCoordAction=cGapCoordToLineEnd): TPoint;
685593```
686594
687595- Param ADetails: returned True if text pos is after real end-of-line, False if text pos is inside line.
688596- Param AGapCoordAction: you must specify here what to do if coordinates are over inter-line gap (see Gaps object).
689597
690598Functions give Result.Y\<0 if cannot calculate pos. For example, if line index is too big or line with this index is fully folded.
691599
692600Property OptCaretVirtual exists. If it's True, caret is allowed after end-of-line. Methods consider it.
693601
694602### Text posistion to/from column index
695603
696604To convert text coordinate X to/from column index (considering Tab chars), ATSynEdit has TabHelper object and methods of Strings object. Here is example, code which converts X of first caret to its column index:
697605
698606``` pascal
699607 var
700608 ed: TATSynEdit;
701609 caret: TATCaretItem;
702610 //...
703611
704612 caret:= ed.Carets[0];
705613 if ed.Strings.IsIndexValid(caret.PosY) then
706614 begin
707615 //optimized for huge lines
708616 ColumnIndex:= ed.Strings.CharPosToColumnPos(caret.PosY, caret.PosX, ed.TabHelper)+1;
709617 end;
710618```
711619
712620## How to manage the caret, the selection?
713621
714622See topic "Carets object". To change position of carets, get Carets property of editor, get each item Carets\[N\], and change caret items' properties PosX/PosY and/or EndX/EndY, then call editor's method Update. To change selection for any caret (each caret has own selection): get caret item, and change its propeties EndX/EndY (set both to -1 to remove selection), then call editor's method Update.
715623
716624Better don't do overlapping selections (when e.g. selection of caret-2 overlaps selection of caret-3), but even here you can call Carets.Sort, which should sort carets by PosX/PosY and fix them.
717625
718626You can delete and add caret items.
719627
720628``` pascal
721629 var
722630 Caret: TATCaretItem;
723631 i: integer;
724632 begin
725633 for i:= 0 to edit.Carets.Count-1 do
726634 begin
727635 Caret:= edit.Carets[i];
728636 {
729637 change here Caret.PosX and Caret.PosY
730638 }
731639 end;
732640 edit.Update;
733641 end;
734642```
735643
736644## How to modify text?
737645
738646See topic above about "Strings object". To change text, get Strings property of editor. "Strings.Count" is number of lines in object. Then access methods of Strings: LineAdd, LineInsert, LineDelete. To get/set lines by index, use properties of Strings: Strings.Lines\[i\], etc, listed above.
739647
740648To do advanced changes, use TATStrings.Text* methods:
741649
742650``` pascal
743651 procedure TextInsert(AX, AY: integer; const AText: atString; AOverwrite: boolean;
744652 out AShift, APosAfter: TPoint);
745653 procedure TextAppend(const AText: atString; out AShift, APosAfter: TPoint);
746654 procedure TextInsertColumnBlock(AX, AY: integer; ABlock: TATStrings;
747655 AOverwrite: boolean);
748656 procedure TextDeleteLeft(AX, AY: integer; ALen: integer; out AShift, APosAfter: TPoint);
749657 procedure TextDeleteRight(AX, AY: integer; ALen: integer; out AShift,
750658 APosAfter: TPoint; ACanDelEol: boolean=true);
751659 function TextDeleteRange(AFromX, AFromY, AToX, AToY: integer; out AShift, APosAfter: TPoint): boolean;
752660 procedure TextInsertEol(AX, AY: integer; AKeepCaret: boolean;
753661 const AStrIndent: atString; out AShift, APosAfter: TPoint);
754662 procedure TextDeleteLine(AX, AY: integer; out AShift, APosAfter: TPoint);
755663 procedure TextReplaceInOneLine(AY, AX1, AX2: integer; const AText: atString);
756664 procedure TextReplaceRange(AFromX, AFromY, AToX, AToY: integer;
757665 const AText: atString; out AShift, APosAfter: TPoint);
758666 function TextReplaceLines_UTF8(ALineFrom, ALineTo: integer; ANewLines: TStringList): boolean;
759667 function TextSubstring(AX1, AY1, AX2, AY2: integer; const AEolString: string = #10): atString;
760668```
761669
762670Strings.Lines\[i\] do not contain end-of-line chars. Usually not needed to change end-of-line chars. You can change them using Strings.LinesEnds\[i\].
763671
764672- After changes in Strings object, always call editor.Update(true). Update(false) only repaints editor, Update(true) invalidates WrapInfo object first (must be called after changes to Strings, or after window resize).
765673- You can read Strings from one editor1 and set it to Strings of editor2. This will make editor2 having same strings object (needed for split-tab).
766674
767675## How to highlight syntax?
768676
769677There're 2 ways:
770678
771679- OnCalcHilite event (better, if your code is small, e.g. colorize few tokens)
772680- Adapter (better, if your code is big, e.g. support for EControl lexers)
773681
774682You can set handler of OnCalcHilite event. Adapter is different way to do the same. Instead of using event, you write code in the new class which is child of TATAdapterHilite. Then you link adapter to editor:
775683
776684``` pascal
777685 Adapter.AddEditor(editor1);
778686 Adapter.AddEditor(editor2); //if 2 editors share the same text buffer
779687```
780688
781689To unlink adapter from all editors, call:
782690
783691``` pascal
784692 Adapter.AddEditor(nil);
785693```
786694
787695If adapter assigned, editor will call adapter's methods (first called adapter, next OnCalcHilite).
788696
789697See file "atsynedit_adapters.pas". It has base class for such adapters. Main method in adapter, which is called by ATSynEdit is:
790698
791699``` pascal
792700 procedure OnEditorCalcHilite(Sender: TObject;
793701 var AParts: TATLineParts;
794702 ALineIndex, ACharIndex, ALineLen: integer;
795703 var AColorAfterEol: TColor);
796704```
797705
798706Parameters here:
799707
800708- Sender: editor object.
801709- AParts: array of "parts": each part has Offset, Len, colors, font style. Adapter must create parts for entire string (1st part takes 1st chars of string, last part takes last chars of string). See code about TATLineParts.
802710- ALineIndex: index of passed line (0-based).
803711- ACharIndex: index of 1st char in line (it's not 0 if wrapped part of line is shown, 0-based).
804712- ALineLen: length of substring to hilite.
805713- AColorAfterEol: return here color for background of line after end-of-line. Return clNone if no color needed.
806714
807715Real string which you must hilite: see example project how to get it.
808716
809717## How to set same text-source to several editors?
810718
811719Several editors can have same text source, text source is ATStrings object which holds
812720
813721- same text for all editors
814722- separate folding states for each editor
815723
816724Each editor holds its own WrapInfo object so wrapped states are separate. Each editor holds its own Carets object, so carets/selections are separate.
817725
818726Tech details: items in ATStrings object have fields:
819727
820728``` pascal
821729 ItemHidden: packed array[0..cMaxStringsClients-1] of ByteBool;
822730 // this line is fully hidden
823731 ItemFoldFrom: packed array[0..cMaxStringsClients-1] of byte;
824732 // 0: line not folded
825733 // >0: line folded from this char-pos
826734```
827735
828736(constant can be changed in source). These fields allow to hold separate fold-info for each "editor client" of ATStrings. Each client must have it's own EditorIndex (0 to cMaxStringsClients-1). Example, make 4 editors ed0..ed3.
829737
830738- change cMaxStringsClients to 4
831739- set ed0.EditorIndex:=0
832740- set ed1.EditorIndex:=1
833741- set ed2.EditorIndex:=2
834742- set ed3.EditorIndex:=3
835743- set ed1.Strings:=ed0.Strings
836744- set ed2.Strings:=ed0.Strings
837745- set ed3.Strings:=ed0.Strings
838746
839747If user changes text in ed3, ed3 will update but others will not: you need to force it:
840748
841749- ed0.Update(true)
842750- ed1.Update(true)
843751- ed2.Update(true)
844752
845753## How to add/change commands?
846754
847755To change action of internal commands (file atsynedit_commands.pas) you need to use event OnCommand which can do any action for any command (and return AHandled=True to disable default code). Better use OnCommand handler for all commands, even new ones. So you can call DoCommand() and have one handler to do anything.
848756
849757Example func which adds new commands to Keymap object:
850758
851759``` pascal
852760const
853761 cmd_FileNew = 2500;
854762 ...
855763
856764procedure InitKeymapApp(M: TATKeymap);
857765begin
858766 M.Add(cmd_FileNew, 'file: new file', ['Ctrl+N'], []);
859767 M.Add(cmd_FileOpen, 'file: open file', ['Ctrl+O'], []);
860768 M.Add(cmd_FileSave, 'file: save file', ['Ctrl+S'], []);
861769 ...
862770end;
863771```
864772
865773Example handler for new commands: some close editor tabs (special case: cannot close tabs just inside OnCommand) and some commands copy something to clipboard:
866774
867775``` pascal
868776procedure TfmMain.EditorOnCommand(Sender: TObject; Cmd: integer; var Handled: boolean);
869777begin
870778 Handled:= true; //by default command marked as handled
871779 case Cmd of
872780 cmd_FileNew,
873781 cmd_FileOpen,
874782 cmd_FileClose,
875783 cmd_FileCloseAll,
876784 cmd_FileReopen:
877785 begin
878786 //these commands close tabs so cannot handle here.
879787 //we start timer which will handle them.
880788 TimerCmd.Tag:= Cmd;
881789 TimerCmd.Enabled:= true;
882790 end;
883791
884792 cmd_CopyLine: DoCopyLine;
885793 cmd_CopyFilenameFull: DoCopyFilenameFull;
886794 cmd_CopyFilenameDir: DoCopyFilenameDir;
887795 cmd_CopyFilenameName: DoCopyFilenameName;
888796
889797 else
890798 Handled:= false; //unknown command: don't mark handled
891799 end;
892800end;
893801```
894802
895803## How to emulate Edit, Combobox?
896804
897805Special child of ATSynEdit exists: ATEdit. It's single line control in which all multiline text editing is disabled. And paste can give only single line. And scrollbars are disabled. And many commands (like "Move lines up/down") are removed from Keymap object.
898806
899807Special child of ATEdit exists: ATComboEdit. It has micromap area which paints dropdown arrow to look like combobox. Click on this arrow gives popup-menu with entered variants.
900808
901-<figure>
902-<img src="atsynedit_combo.png" title="atsynedit_combo.png" />
903-<figcaption>atsynedit_combo.png</figcaption>
904-</figure>
809+![](/wiki-images/3/3b/Atsynedit_combo.png)
905810
906811Variants must be added to history by user code, using OnCommand handler: react to command "cCommand_KeyEnter". Example of handler:
907812
908813``` pascal
909814procedure TfmCombo.ComboCommand(Sender: TObject; ACmd: integer;
910815 var AHandled: boolean);
911816var
912817 s: string;
913818 n: integer;
914819begin
915820 if ACmd=cCommand_KeyEnter then
916821 begin
917822 with ed do
918823 begin
919824 s:= UTF8Encode(Trim(Text));
920825
921826 Text:= '';
922827 DoCaretSingle(0, 0);
923828
924829 n:= Items.IndexOf(s);
925830 if n>=0 then Items.Delete(n);
926831 Items.Insert(0, s);
927832 end;
928833 AHandled:= true;
929834 end;
930835end;
931836```
932837
933838## What props are useful for statusbar?
934839
935840- number of carets: Carets.Count
936841- positions of carets: Carets.Items\[i\].PosX (column), PosY (line)
937842- text encoding: Strings.Encoding
938843 - cEncAnsi: then codepage is given by Strings.EncodingCodepage
939844 - cEncUTF8: then presence of utf8 bom is given by Strings.SaveSignUtf8
940845 - cEncWideLE, cEncWideBE: then presense of Unicode bom is given by Strings.SaveSignWide
941846- text line endings (win/unix/mac): Strings.Endings
942847- tab-char width: OptTabSize
943848- tab-char entered by spaces: OptTabSpaces
944849- index of top visible line: LineTop
945850- index of left visible column: ColumnLeft
946851- modified state: Modified
947852- read-only mode: ModeReadOnly
948853- insert/overwrite mode: ModeOverwrite
949854- word-wrap mode: OptWrapMode
950855- unprinted chars shown: OptUnprintedVisible
951856- selection: get Carets.Items\[i\].PosX/PosY/EndX/EndY, for state of column selection get IsSelRectEmpty, SelRect
952857
953858## How to customize popup menu?
954859
955860These properties allow to change all menus in control (you cannot modify default menu):
956861
957862- PopupText: for text area
958863- PopupRuler
959864- PopupMinimap
960865- PopupMicromap
961866- PopupGutterBm: for bookmarks column
962867- PopupGutterFold: for folding column
963868- PopupGutterNum: for line numbers column
964869
965870Prop OptMouse* exists, which allows to show menus on mouse-down or on mouse-up.
966871
967872## How to know indexes of changed lines in OnChange?
968873
969874You have event Strings.OnLog. It gives parameters:
970875
971876- ALine: index of line at which change occurs
972877- ALen: len of text which is added (ALen\>0) or deleted (ALen\<0) at line start
973878
974879Get the log, save it, and then parse it during OnChange.
975880
976881## How to export text to HTML?
977882
978883Unit atsynedit_export_html has the function which does HTML report. It has several parameters to customize output. See example of usual parameters in the demo_editor.
979884
980885ATSynEdit has API which allows to make report to any format. It's function DoCalcLineHiliteEx. It returns syntax highlight info for any line. Info is stored in TATLineParts struct, see HTML exporter code how to use it.
981886
982887## Short description of events?
983888
984889- OnBeforeCalcHilite: Called before OnCalcHilite for first visible line.
985890- OnCalcBookmarkColor: Called to determine color of bookmark (background color of line with bookmark).
986891- OnCalcHilite: Called to calculate syntax highlight for a line (or wrapped part of line). You need to fill TATLineParts struct for this line.
987892- OnCalcStaple: Called to determine color of block staple (vertical line which is painted near fold-ranges).
988893- OnChange: Called after any command which changes text.
989894- OnChangeCaretPos: Called after any command which changes caret(s) position(s).
990895- OnChangeState: Called after any command which changes editor state, such as read-only, insert/overwrite, word-wrap etc.
991896- OnClickDouble: Called on double-click on text area.
992897- OnClickGutter: Called on click on gutter area.
993898- OnClickMicromap: Called on click on micromap area.
994899- OnClickMiddle: Called on middle button click on text area.
995900- OnClickTriple: Called on triple-click on text area.
996901- OnClickMoveCaret: Called just before changing caret pos via mouse click (you get prev caret pos, new caret pos).
997902- OnClickEndSelect: Called just after mouse-up, if single selection made (you get selection start pos, end pos).
998903- OnCommand: Called before running any command. You can disable any command processing, or perform custom processing before/instead of command.
999904- OnCommandAfter: Called after running command.
1000905- OnDrawBookmarkIcon: Called on painting gutter mark for line with bookmark.
1001906- OnDrawEditor: Called after painting entire text area.
1002907- OnDrawLine: Called after painting line (or wrapped part of line) on canvas. You can paint some "image" over this line.
1003908- OnDrawMicromap: Called on painting micromap area.
1004909- OnDrawRuler: Called after painting ruler area.
1005910- OnScroll: Called after any command which scrolls text.
1006911- OnPaste: Called just before pasting text. App can disable/override default pasting.
1007912- OnHotspotEnter: Called when mouse moving enters one of the ranges in Hotspots object.
1008913- OnHotspotExit: Called when mouse moving exits hotspot.
1009914
1010915## What is logic of grouped-undo?
1011916
1012917There're kinds of group-marks in undo-items: hard-marks, soft-marks.
1013918
1014919- Hard-marks are placed on undo-items if you call Strings.BeginUndoGroup, editing, Strings.EndUndoGroup. All items marked with hard-marks are undone as group, even of OptUndoGrouped=false.
1015920- Soft-marks used only if OptUndoGrouped=true. Soft-marks are placed on one next undo-item, when user does mouse click (or some similar action); or in code by call Strings.SetGroupMark. All items beginning with last soft-marked item are undone as group.
1016921
1017922Example 1. OptUndoGrouped=true, such undo-items exist:
1018923
1019924- item1
1020925- item2
1021926- item3, soft-mark
1022927- item4
1023928- item5, hard-mark
1024929- item6, hard-mark
1025930- item7
1026931- item8, soft-mark
1027932- item9
1028933- item10
1029934
1030935Undo:
1031936
1032937- undone: item10..8; until soft-mark.
1033938- undone: item7..3; until soft-mark.
1034939- undone: item2..1.
1035940
1036941Example 2. OptUndoGrouped=false, undo-items:
1037942
1038943- item1
1039944- item2
1040945- item3, hard-mark
1041946- item4, hard-mark
1042947- item5, hard-mark
1043948- item6
1044949- item7
1045950
1046951Undo:
1047952
1048953- Undone: item7
1049954- Undone: item6
1050955- Undone: item5..3
1051956- Undone: item2
1052957- Undone: item1
1053958
1054959Soft-marks also used together with hard-marks. Soft-mark is placed with first hard-mark after call Strings.BeginUndoGroup. This allows to undo N groups of edits, if many items hard-marked (w/out gaps).
1055960
1056961Example 3. Undo-items:
1057962
1058963- item1, hard-mark
1059964- item2, hard-mark, soft-mark
1060965- item3, hard-mark
1061966- item4, hard-mark, soft-mark
1062967- item5, hard-mark
1063968- item6, hard-mark
1064969
1065970Undo:
1066971
1067972- undone: item6..4
1068973- undone: item3..2
1069974- undone: item1
1070975
1071976## How to record/playback macros?
1072977
1073978Macro support must be done on app level, component does support by event OnCommand. Handle OnCommand and you get all command codes which run. Real app adds more commands in Keymap object and it must decide which of them it wants to record (e.g. don't record commands to show dialogs, and record commands which comment/uncomment lines).
1074979
1075980To playback macro, call DoCommand for all codes in macro.
1076981
1077982Hint. CudaText also adds 3 commands to help with macros:
1078983
1079984- "mouse click" (command recorded in OnClick event of editor)
1080985- "goto pos" (command recorded by GoTo dialog)
1081986- "finder action" (command recorded when doing find/replace actions)
1082987
1083988## How to configure modifier-keys with click?
1084989
1085990You can configure some of mouse clicks with modifier keys: simple click, Ctrl+click, Shift+click, Ctrl+Shift+click, middle click, etc. Property MouseMap configures it, it's array, type TATMouseActions. Default code configures it in InitMouseActions like this:
1086991
1087992``` pascal
1088993procedure InitMouseActions(var M: TATMouseActions);
1089994//...
1090995begin
1091996 SetLength(M, 0);
1092997 Add(cMouseActionClickSimple, [ssLeft]);
1093998 Add(cMouseActionClickRight, [ssRight]);
1094999 Add(cMouseActionClickAndSelBlock, [ssLeft, ssShift]);
10951000 Add(cMouseActionMakeCaret, [ssLeft, ssXControl]);
10961001 Add(cMouseActionMakeCaretsColumn, [ssLeft, ssXControl, ssShift]);
10971002 Add(cMouseActionNiceScrolling, [ssMiddle]);
10981003 //...
10991004```
11001005
11011006## Does it use PrimarySelection on gtk?
11021007
11031008It can use it, all methods which work with clipboard, get AClipboardObject:TClipboard, here you can pass Clipboard, PrimarySelection, SecondarySelection.
11041009
11051010Command codes for Paste: exist for Clipboard, and for PrimarySelection. This is part which reacts to command codes:
11061011
11071012``` pascal
11081013 cCommand_ClipboardCopy: Res:= DoCommand_ClipboardCopy(false, Clipboard);
11091014 cCommand_ClipboardCopyAdd: Res:= DoCommand_ClipboardCopy(true, Clipboard);
11101015 cCommand_ClipboardCut: Res:= DoCommand_ClipboardCut(Clipboard);
11111016
11121017 //use Clipboard:TClipboard
11131018 cCommand_ClipboardPaste: Res:= DoCommand_ClipboardPaste(false, false, Clipboard);
11141019 cCommand_ClipboardPaste_Select: Res:= DoCommand_ClipboardPaste(false, true, Clipboard);
11151020 cCommand_ClipboardPaste_KeepCaret: Res:= DoCommand_ClipboardPaste(true, false, Clipboard);
11161021 cCommand_ClipboardPaste_Column: Res:= DoCommand_ClipboardPasteColumnBlock(false, Clipboard);
11171022 cCommand_ClipboardPaste_ColumnKeepCaret: Res:= DoCommand_ClipboardPasteColumnBlock(true, Clipboard);
11181023
11191024 //same, but use PrimarySelection:TClipboard
11201025 cCommand_ClipboardAltPaste: Res:= DoCommand_ClipboardPaste(false, false, PrimarySelection);
11211026 cCommand_ClipboardAltPaste_Select: Res:= DoCommand_ClipboardPaste(false, true, PrimarySelection);
11221027 cCommand_ClipboardAltPaste_KeepCaret: Res:= DoCommand_ClipboardPaste(true, false, PrimarySelection);
11231028 cCommand_ClipboardAltPaste_Column: Res:= DoCommand_ClipboardPasteColumnBlock(false, PrimarySelection);
11241029 cCommand_ClipboardAltPaste_ColumnKeepCaret: Res:= DoCommand_ClipboardPasteColumnBlock(true, PrimarySelection);
11251030```
11261031
11271032Default code uses PrimarySelection in one place: on copy, it copies to Clipboard, then (under Linux/Mac) copies the same to PrimarySelection.
11281033
11291034To make middle-button-click paste from PrimarySelection, you need to handle OnClickMiddle, here's example from CudaText:
11301035
11311036``` pascal
11321037procedure TEditorFrame.EditorOnClickMiddle(Sender: TObject; var AHandled: boolean);
11331038begin
11341039 AHandled:= false;
11351040 if EditorOps.OpMouseMiddleClickPaste then
11361041 begin
11371042 AHandled:= true;
11381043 (Sender as TATSynEdit).DoCommand(cmd_MouseClickAtCursor);
11391044 (Sender as TATSynEdit).DoCommand(cCommand_ClipboardAltPaste); //uses PrimarySelection:TClipboard
11401045 exit;
11411046 end;
11421047end;
11431048```
11441049
11451050## How to handle OnCalcLineHilite event
11461051
11471052This event has type:
11481053
11491054``` pascal
11501055type
11511056 TATSynEditCalcHiliteEvent = procedure(Sender: TObject; var AParts: TATLineParts;
11521057 ALineIndex, ACharIndex, ALineLen: integer; var AColorAfterEol: TColor) of object;
11531058```
11541059
11551060Handler gets:
11561061
11571062- ALineIndex: index of line (in Strings object)
11581063- ACharIndex: offset of fragment in line (not trivial only if line is wrapped)
11591064- ALineLen: length of fragment in line (in WideChars)
11601065
11611066Handler must fill:
11621067
11631068- AParts: array of highlighted parts, one part per one "syntax token" (e.g. one symbol, one keyword, one string const, one comment, etc). Fill all other parts with zeros.
11641069- AColorAfterEol: color of background after line end (for empty area).
11651070
11661071Handler must handle the situation of line-wrapping inside long comments (or long string constants, etc), when one fragment gives part for ALineIndex=N, and next fragment gives part for ALineIndex=N+1.
11671072
11681073Example. Handler gets string with Pascal syntax like "procedure AB(name: word)". It must create such "parts" for this string:
11691074
11701075- part\[0\]: for "procedure": TATLinePart properties: offset=0, len=9, etc
11711076- part\[1\]: for "AB": offset=10, len=2
11721077- part\[2\]: for "(": offset=12, len=1
11731078- part\[3\]: for "name": offset=13, len=1
11741079- part\[4\]: for ":": offset=17, len=1
11751080- part\[5\]: for "word": offset=19, len=4
11761081- part\[6\]: for ")": offset=23, len=1
11771082- other parts must have offset=0, len=0
11781083
11791084## Details about highlighting rendering
11801085
11811086Central procedure is DoCalcLineHilite. For items of WrapInfo object, it calculates "line parts" (array TATLineParts of record TATLinePart). Each "part" here is one colored/styled fragment of string. After line parts are calculated, they pass to ATSynEdit_CanvasProc.CanvasTextOut to render.
11821087
11831088DoCalcLineHilite works like this:
11841089
11851090- Updates adapter's "dynamic hilites enabled" flag (to on/off) and calls adapter's OnCalcLineHilite. This makes initial version of line parts.
11861091- Calculated line parts are added to cache (same calculation of same WrapInfo item will not happen, only cache item is used).
11871092- Updates line parts via OnCalcHilite event (it's additional method to hilite, first is adapter, usually only adapter is used).
11881093- Calls DoPartCalc_ApplyAttribsOver to apply Attribs object to line parts. E.g. add parts for spell-checker bad words. E.g. add parts for underlined URLs.
11891094- Calls DoPartCalc_ApplySelectionOver to apply selection (for all multi-carets, or one column selection) to parts.
11901095
11911096DoPartCalc_ApplySelectionOver is complex function. It updates TATLineParts. How it works:
11921097
11931098- It tests entire TATLineParts - is it all selected (optimization for selected-all), or all unselected (optimization for selected-none), or partly selected.
11941099- If all selected, it just marks all parts (already calc'ed) with selection color. If partly selected, it finds parts, which are all/partly selected.
11951100- For parts, which are all selected, it marks them with selection color.
11961101- For parts, which are partly selected, it makes slow loop over offsets in parts. This slow loop finds chars which are selected and inserts new parts (with selection color) for them.
11971102
11981103To find/insert parts to TATLineParts, funcs DoPartFind/ DoPartInsert are used.
11991104
12001105## How to control vert/horz scrolling?
12011106
12021107Property LineTop is index of top visible line, it is not precise vert scroll position, if long wrapped lines present.
12031108
12041109Public prop: WrapInfo, it's list of wrapinfo items. You can read it to get all info about wrapped lines, at which offsets they are wrapped. This allows to make smart jump of caret on wrapped lines.
12051110
12061111Public props: ScrollVert, ScrollHorz. They are records, NPos is scroll pos. ScrollVert.NPos is index in WrapInfo list, change it to make precise scrolling (call Update() then). ScrollHorz.NPos is horz scroll pos, now it's the same as prop ColumnLeft.
12071112
12081113Method DoScrollByDelta(Dx, Dy) allows to increment/decrement horz (Dx) and vert (Dy) scroll positions.
12091114
12101115## When Update call is needed?
12111116
12121117First param of Update() method is boolean: force updating the WrapInfo object.
12131118
12141119- Update(true): needed if you changed Strings contents - directly via Strings object; or changed Fold ranges - directly via Fold object. Ie, if WrapInfo object needs update (and component cannot detect this automatically).
12151120- Update(false): needed if you only changed look, ie carets, scroll position, bookmarks, markers, etc.
12161121- no Update() call is needed, if you only called DoCommand(). DoCommand does it automatically.
12171122
12181123## Behaviour of column selection
12191124
12201125ATSynEdit gives two modes of column selection, which have differences when you select over wrapped lines, or lines with full-width characters. This is controlled by property OptCaretsPrimitiveColumnSelection.
12211126
12221127- Value True: "pritimive mode" which behaves much like Sublime Text. In this mode editor places multi-selections over visual rectangle of characters. In this mode, one line can have 6 chars selected, and another line can have 8 chars selected. This depends on visual positions of chars in those lines.
12231128
12241129- Value False: in this mode, all affected lines have the same number of selected chars. But when full-width chars (e.g. CJK) are present in text, selection may look weird. Here is an example picture where starting lines are ASCII and ending lines have full-width chars.
12251130
1226-<figure>
1227-<img src="cudatext-column-sel-cjk.png" title="cudatext-column-sel-cjk.png" />
1228-<figcaption>cudatext-column-sel-cjk.png</figcaption>
1229-</figure>
1131+![](/wiki-images/5/5b/Cudatext-column-sel-cjk.png)
12301132
12311133It is not a bug. In this example, user selected column block from column 7 (at line 1) until column 20 (at line 6), so column block takes columns 7...20 from all lines. On first ASCII lines, columns 7...20 take different visual area, than columns on last lines. When you copy/paste that block to another program, block may look differently. But that block contains equal number of chars on each line.
12321134
12331135Even more weird look happens when user selects column block over word-wrapped lines.
12341136
1235-<figure>
1236-<img src="cudatext-column-sel-weird.png" title="cudatext-column-sel-weird.png" />
1237-<figcaption>cudatext-column-sel-weird.png</figcaption>
1238-</figure>
1137+![](/wiki-images/b/ba/Cudatext-column-sel-weird.png)
12391138
12401139Here is the code's logic in all these cases (with full-width characters and with word-wrapped lines). Code calculates (line1, column1) text position of column block left-top edge. Then code calculates (line2, column2) text position of column block right-bottom edge. Then code selects characters in range column1...column2 in all those affected lines line1...line2. And this code logic produces so weird look in word-wrapped mode.
12411140
12421141## Horizontal scrollbar only increases but never shrinks?
12431142
12441143Horizontal scrollbar has unusual behaviour: its width only increases (when long lines appear in the visible area). It never shrinks when we scroll to shorter lines. It was made as a workaround to GTK2 widgetset freezes - it freezes when scrollbar shows/hides/shows/hides/etc on scrolling.
12451144
12461145You can try to disable this workaround in file atsynedit_defines.inc:
12471146
12481147``` pascal
12491148{$define fix_horzscroll} //workaround for gtk2 widgetset unstable: it freezes app
12501149 //when horz-scroll hides/shows/hides/...
12511150 //ok also for win32
12521151```
12531152
12541153## more
12551154
12561155# Auto-completion lists
12571156
12581157## General auto-completion API
12591158
12601159You need to install package ATSynEdit_Cmp, from repo <https://github.com/Alexey-T/ATSynEdit_Cmp> .
12611160
12621161Unit atsynedit_cmp_form (from ATSynEdit_Cmp package) has the function to show completion listbox in the editor. This listbox is not modal, it only emulates modality of some degree. It keeps editor caret blinking. While completion listbox is shown, editor doesn't get keyboard input, but listbox passes typing letters/Backspace/Left/Right to the editor, so it looks like editor gets some part of keyboard input. Listbox performs insertion of chosen item on Enter/Tab keys, and it hides on Esc-key or clicking outside of the listbox.
12631162
12641163``` pascal
12651164type
12661165 TATCompletionPropEvent = procedure (Sender: TObject;
12671166 AContent: TStringList; out ACharsLeft, ACharsRight: integer) of object;
12681167 TATCompletionResultEvent = procedure (Sender: TObject;
12691168 const ASnippetId: string; ASnippetIndex: integer) of object;
12701169
12711170procedure EditorShowCompletionListbox(AEd: TATSynEdit;
12721171 AOnGetProp: TATCompletionPropEvent;
12731172 AOnResult: TATCompletionResultEvent = nil;
12741173 AOnChoose: TATCompletionResultEvent = nil;
12751174 const ASnippetId: string = '';
12761175 ASelectedIndex: integer = 0;
12771176 AAllowCarets: boolean = false);
12781177```
12791178
12801179Callback AOnGetProp params:
12811180
12821181- AContent: list of lines, each line is S_id+'\|'+S_text+'\|'+S_desc. S_id is a prefix before actual text (good to pass here short texts: "var", "func"). S_text (it is usually second item) is text which is inserted into editor on closing listbox. After id/text several items may exist, all '\|'-separated, they will show in additional listbox columns. For example, this TStringList line makes the listbox line of 3 columns:
12831182
12841183``` pascal
12851184 'func|FuncName|(param1, param2)'#9'Func description';
12861185```
12871186
12881187- - If you need to insert not only S_text but also additional substring (showing in the listbox only S_text), use such S_text: S_text+#1+S_before_caret+#1+S_after_caret (also substrings will be added before and after caret). This is used by HTML autocompletion: here S_before_caret is "\>" and S_after_caret is "\</tag\>".
12891188 - At the end of lines, you may add Chr(9)+S_tooltip: string S_tooltip will show outside of listbox, in a tooltip, when user selects its line. To show multi-line tooltip, separate it with Chr(2).
12901189
12911190- ACharsLeft, ACharsRight: count of chars, to the left and right of the caret, which will be replaced by selected listbox item. These counts must be detected by app. Some app may need to count only word chars, some may need spaces/brackets.
12921191
12931192Notes:
12941193
12951194- Function EditorShowCompletionListbox doesn't work for a) read-only editor, b) multi-carets.
12961195- You can change colors/sizes/font of listbox: see unit source, it gives the global variable (record with many fields).
12971196
12981197## Auto-completion using acp-files
12991198
13001199Unit atsynedit_cmp_acpfiles has the function to show auto-completion listbox, from CudaText/SynWrite .acp files. Acp file must be in the format described in the SynWrite help file. You may copy acp files from CudaText distro, or from additional lexers, ie from CudaText lexers on CudaText.SF.net.
13011200
1302-<figure>
1303-<img src="cudatext-php-complete.png" title="cudatext-php-complete.png" />
1304-<figcaption>cudatext-php-complete.png</figcaption>
1305-</figure>
1201+![](/wiki-images/a/a3/Cudatext-php-complete.png)
13061202
13071203## Auto-completion for CSS
13081204
13091205Unit atsynedit_cmp_css has the function to show auto-completion listbox, for CSS syntax. You must pass path of the file "data/autocompletespec/css_list.ini" from CudaText distro. CSS completion supports several modes:
13101206
13111207- caret is on css attrib before ":" char - then list of css attribs is shown (which match the substring under caret),
13121208- caret is after css attrib and ":" char (before closing ";") - then list of values for this attrib is shown.
13131209- and more
13141210
1315-<figure>
1316-<img src="cudatext-css-complete.png" title="cudatext-css-complete.png" />
1317-<figcaption>cudatext-css-complete.png</figcaption>
1318-</figure>
1211+![](/wiki-images/5/55/Cudatext-css-complete.png)
13191212
13201213## Auto-completion for HTML
13211214
13221215Unit atsynedit_cmp_html has the function to show auto-completion listbox, for HTML syntax. You must pass path of the file "data/autocompletespec/html_list.ini" from CudaText distro. HTML completion supports several modes:
13231216
13241217- caret on opening/closing tag name: list of tags shown
13251218- caret after tag, on attribute place before "=": list of attribs for found tag shown
13261219- caret after tag, after attribute, after "=": list of values of found tag+attrib shown
13271220- and more
13281221
1329-<figure>
1330-<img src="cudatext-html-complete.png" title="cudatext-html-complete.png" />
1331-<figcaption>cudatext-html-complete.png</figcaption>
1332-</figure>
1222+![](/wiki-images/7/7b/Cudatext-html-complete.png)
13331223
13341224# Finder
13351225
13361226Finder class TATEditorFinder is in unit atsynedit_finder. To use it, create Finder object and set its props:
13371227
13381228- Editor: editor object (text will be read automatically)
13391229- StrFind: search-string (simple string or regex)
13401230- StrReplace: replace-string (simple string or some regex, eg with "\$0", "\$1")
13411231- boolean search flags:
13421232 - OptRegex: treat StrFind/StrReplace as regex
13431233 - OptCase: case-sensitive (for normal and regex modes)
13441234 - OptWords: whole-words search (for non-regex mode, for regex you must use "\b" specifier in search-string)
13451235 - OptBack: backward search (for non-regex mode)
13461236 - OptWrapped: continue search from top, if bottom reached (for back-search: continue from bottom, if top reached)
13471237 - OptInSelection: do search/replace in selection only (limitation exists: only 1st selection used)
13481238 - OptFromCaret: search goes from position of first caret (else: from start/end of text)
13491239 - OptConfirmReplace: each replace will occur after event OnConfirmReplace
13501240
13511241Dialog of demo:
13521242
1353-<figure>
1354-<img src="atsynedit_finder.png" title="atsynedit_finder.png" />
1355-<figcaption>atsynedit_finder.png</figcaption>
1356-</figure>
1243+![](/wiki-images/b/b3/Atsynedit_finder.png)
13571244
13581245Methods to find/replace text (they will place selection on found match, and scroll editor to caret, except method CountAll):
13591246
13601247``` pascal
13611248function DoAction_FindSimple(const APosStart: TPoint): boolean;
13621249function DoAction_FindOrReplace(AReplace, AForMany: boolean; out AChanged: boolean;
13631250 AUpdateCaret: boolean): boolean;
13641251function DoAction_ReplaceSelected(AUpdateCaret: boolean): boolean;
13651252procedure DoAction_FindAll(AResults: TATFinderResults; AWithEvent: boolean);
13661253function DoAction_CountAll(AWithEvent: boolean): integer;
13671254procedure DoAction_ExtractAll(AWithEvent: boolean; AMatches: TStringList; ASorted: boolean;
13681255 ADuplicates: TDuplicates);
13691256function DoAction_ReplaceAll: integer;
13701257```
13711258
13721259Found position and len are saved in props: MatchPos, MatchLen. Internally Finder object uses ATStringBuffer to read editor lines into buffer and search inside buffer. Then ATStringBuffer methods used to calculate caret position (from buffer offset) and place caret/selection. Note for regex search: buffer contains only "\n" line ends, other ends don't happen (original file may have any ends).
13731260
13741261Finder has events:
13751262
13761263- OnProgress: allows to show progressbar during search
13771264- OnFound: called when result found
13781265- OnConfirmReplace: called before replacing text (to show confirmation) if OptConfirmReplace set
13791266
13801267Finder can handle multi-selections, by option OptInSelection. Selections are handled by CountAll, ReplaceAll. For other actions, selections don't make sense, because find-next makes new smaller selection (for found fragment) so prev big selection is lost. Tech note for replace-all: selections are looked from top to bottom, if they don't have common lines, and looked from bottom to top, if they have. This is done to not break lines, which are touched by 2+ selections. Count-all handles selections from top to bottom.
13811268
13821269# Adapters
13831270
13841271## Adapters description
13851272
13861273To support syntax-highlighting in some way, you need
13871274
13881275- Add event OnCalcHilite handler, which must fill the TATLineParts array with needed highlighting, or
13891276- Create the unit with adapter class, child of TATAdapterHilite (unit atsynedit_adapters), which must fill the TATLineParts array, again. Partial code of the adapter class:
13901277
13911278``` pascal
13921279type
13931280 TATAdapterHilite = class(TComponent)
13941281 ...
13951282 public
13961283 constructor Create(AOwner: TComponent); override;
13971284 //
13981285 procedure OnEditorChange(Sender: TObject); virtual;
13991286 //called when editor's text changes.
14001287
14011288 procedure OnEditorChangeEx(Sender: TObject; AChange: TATLineChangeKind; ALine, AItemCount: integer); virtual;
14021289 //detailed version of OnEditorChange
14031290
14041291 procedure OnEditorIdle(Sender: TObject); virtual;
14051292 //called after text is changed, and pause passed (OptIdleInterval)
14061293 //fast changes (faster than OptIdleInterval): called only after last change
14071294
14081295 procedure OnEditorCalcHilite(Sender: TObject;
14091296 var AParts: TATLineParts;
14101297 ALineIndex, ACharIndex, ALineLen: integer;
14111298 var AColorAfterEol: TColor;
14121299 AMainText: boolean); virtual;
14131300 //called to calculate hilite of entire line.
14141301 //ACharIndex is starting offset in this line, >0 if editor scrolled horizontally.
14151302 //ALineLen is len of line part, starting from ACharIndex.
14161303 ...
14171304```
14181305
14191306You need your custom adapter class, child of TATAdapterHilite, which must implement some of methods. You don't have to implement all of them, if your adapter doesn't need that. Then you must create an object of your adapter class, and assign this object to editor's AdapterForHilite property.
14201307
14211308The package ATSynEdit_Ex contains 2 ready adapter classes: for complex EControl .lcf lexers, and for CudaText simplified "lite" lexers. The lexer files are distributed in the CudaText repos on SourceForge.
14221309
14231310## Adapter for EControl lexers
14241311
14251312See [[ATSynEdit EControl adapter]]
14261313
14271314# How to adapt TRegExpr upstream for ATSynEdit
14281315
14291316TRegExpr upstream is <https://github.com/andgineer/TRegExpr> . How to convert it to atsynedit_regexpr unit.
14301317
14311318- rename unit
14321319- change "uses regexpr_unicodedata" to "uses ATSynEdit_UnicodeData"
14331320- comment line {\$I regexpr_compilers.inc}
14341321- set define on: UniCode
14351322- set define on: FastUnicodeData
14361323- set defines off: UseSpaceChars, UseWordChars
14371324- fix vars:
14381325
14391326``` pascal
14401327 RegExprModifierI: boolean = False; // default value for ModifierI
14411328 RegExprModifierR: boolean = True; // default value for ModifierR
14421329 RegExprModifierS: boolean = False; // default value for ModifierS
14431330 RegExprModifierG: boolean = True; // default value for ModifierG
14441331 RegExprModifierM: boolean = True; // default value for ModifierM
14451332 RegExprModifierX: boolean = False; // default value for ModifierX
14461333
14471334 RegExprLineSeparators: RegExprString = #$a#$b#$c
14481335 {$IFDEF UniCode}
14491336 + #$2028#$2029#$85
14501337 {$ENDIF};
14511338 RegExprLinePairedSeparator: RegExprString = ''; //#$d#$a;
14521339
14531340 FUseOsLineEndOnReplace := False;
14541341 FReplaceLineEnd := #10; //not sLineBreak, it is CR LF on Windows
14551342```
14561343
14571344---
14581345*From the Free Pascal wiki, page "ATSynEdit", as it stood on 2024-04-04 after a change by Alextp, 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).*
14591346Category: ATSynEdit
14601347Category: Components

Back to history