Sign in Join
Wiki copytemporary

ATSynEdit: what changed

Revision 3 to 4. 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
1010![](/wiki-images/e/eb/Atsynedit.png)
1111
1212# License
1313
1414ATSynEdit files licensed under MPL 2.0. You can optionally use them under LGPL if your project needs it.
1515
1616EControl files licensed under special license (readme.txt), usage allowed only in open-source projects.
1717
1818# Keyboard shortcuts
1919
2020Run demo_editor and call menu item "Help - Commands'. You'll see all shortcuts in a dialog like this:
2121
2222![](/wiki-images/d/db/Atsynedit_keymap.png)
2323
2424# Mouse shortcuts
2525
2626Multi-carets:
2727
2828- Ctrl+click - add/delete caret
2929- Ctrl+drag - add caret with selection
3030- Ctrl+Shift+click - add carets column in several lines (from previous caret to clicked line)
3131
3232Select:
3333
3434- 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)
3535- drag on gutter's line numbers - select by entire lines
3636- double-click and immediately drag - select text by words
3737
3838Clicks:
3939
4040- double-click - select clicked word
4141- triple-click - select entire line (limited by end-of-lines)
4242- 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)
4343
4444# Multi-carets
4545
4646Multi-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.
4747
4848Animation:
4949
5050![](/wiki-images/c/cf/Atsynedit-carets.gif)
5151
5252## Multi-selections
5353
5454If 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.
5555
5656Multi-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).
5757
5858Animation shows this:
5959
6060![](/wiki-images/6/67/Atsynedit-sel.gif)
6161
6262## Clipboard commands with selections
6363
6464Clipboard-related commands work with carets, both with selections and without them. Some details about this:
6565
6666Command
6767
6868Behaviour, when there're no selections
6969
7070Behaviour, when at last one selection present
7171
7272Copy to clipboard
7373
7474Copies entire lines, containing carets.
7575
7676`                   (Ignores multiple carets on same line.)`
7777
7878Copies only selections text.
7979
8080`                   (Ignores carets without selections.)`
8181
8282Cut to clipboard
8383
8484Similarly to "Copy" w/o selections.
8585
8686Similarly to "Copy" with selections.
8787
8888Paste from clipboard
8989
9090First, 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.
9191
9292Delete char
9393
9494Deletes one char at each caret position.
9595
9696Deletes only selections text. (Ignores carets without selections.)
9797
9898# Help topics
9999
100100## Compilation errors
101101
102102**IME error on GTK2.**
103103
104104You may get this error:
105105
106-```
106+```text
107107atsynedit_adapter_gtk2ime.pas(131,5) Error: Identifier not found "IM_Context_Set_Cursor_Pos"
108108```
109109
110110To fix it, edit the file "atsynedit/atsynedit_package.lpk" and remove this block there:
111111
112112``` xml
113113 <Other>
114114 <CustomOptions Value="-dGTK2_IME_CODE"/>
115115 <OtherDefines Count="1">
116116 <Define0 Value="GTK2_IME_CODE"/>
117117 </OtherDefines>
118118 </Other>
119119```
120120
121121Alternative 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.
122122
123123## What limitations it has?
124124
125125- All lines have the same height
126126- Cannot set different fonts for different parts
127127- No support for right-to-left mode, only limited support for Arabic text
128128
129129## What features it has?
130130
131131- Fast editing of big texts (tested 10M log)
132132- Syntax highliting using 'adapters'
133133- Multi-carets from birth. All code carets aware.
134134- Strings object is holder of text. 2+ editors can have 1 strings obj. (Split editor.)
135135- Unicode (used UnicodeString in Strings obj)
136136- Word-wrap (wrap at control edge, wrap at column)
137137- Inter-line gaps (see #Gaps_object)
138138- Undo/redo
139139- Column blocks
140140- Gutter (line numbers, folding, colored line states)
141141- Bookmarks (with icons)
142142- Supports Win/Unix/Mac line ends
143143- Minimap like Sublime Text
144144- Micromap (owner drawn)
145145- Ruler (at top edge)
146146- Renders wrapped indent (line contains some indent, wrapped parts contain the same indent)
147147- Renders CJK chars with big width
148148- Renders some Unicode chars as "AABB" hex code
149149- Renders unprintable chars (space, tab, line end)
150150- Can highlight/activate URLs
151151- Can set color attributes for ranges (to highlight pair brackets or same words)
152152- Supports proportional fonts
153153
154154## Difference from other components
155155
156156- Adapters to support syntax highlighting. In theory, using adapters it's possible to support syntax files from any app: Sublime Text, Atom, EmEditor etc.
157157- Muti-carets, multi-selections
158158- Inter-line gaps, can insert pictures in it
159159- Minimap, Micromap (rare in others editors)
160160- Smooth per-pixel scrolling (option)
161161
162162## Does it need to be installed?
163163
164164It may be installed or not.
165165
166166- Use not installed: see below "Basic example how to use".
167167- 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.
168168
169169## What parts does it have?
170170
171171### Strings object
172172
173173type TATStrings in unit atstrings. Storage of text lines. It includes managing of Undo/Redo too.
174174
175175Methods:
176176
177177- 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.
178178- To insert/delete blocks of lines: LineBlockInsert, LineBlockDelete.
179179- 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).
180180- To load entire content into Strings object: LoadFromFile, LoadFromStream, LoadFromString (multi-line string).
181181
182182To get/set lines, use properties:
183183
184184- Lines\[i\]: text, of type UnicodeString
185185- LinesUTF8\[i\]: text, of type string (faster than Lines\[\])
186186- LinesLen\[i\]: integer: same as Length(Lines\[i\]), but faster
187187- LinesEnds\[i\]: enum: kind of end-of-line chars
188188- LinesHidden\[i\]: bool: true if line is fully hidden (inside folded block)
189189- LinesFoldFrom\[i\]: byte: 0 if line is not folded, \>0 if line is folded from this char-pos
190190- LinesState\[i\]: enum: line state, normal/changed/saved, it's shown by color on gutter
191191- LinesSeparator\[i\]: enum: kind of horizontal separator line to paint near text (rarely needed for apps).
192192
193193Auto-adjustments of other objects when lines are added/deleted:
194194
195195- Strings object updates Strings.Bookmarks, Strings.Bookmarks2 objects.
196196- Strings object updates Gaps (actually Strings.Gaps) object.
197197- If Strings.GutterDecor1 / .GutterDecor2 properties are set, Strings object updates GutterDecor object(s). Properties are not set in ATSynEdit, application must set them.
198198- Objects Attribs / Markers / HotSpots are updated not inside Strings object, but in main ATSynEdit code.
199199
200200### Strings.Bookmarks object
201201
202202type TATBookmarks in unit atsynedit_bookmarks. Holds list of bookmarks of type TATBookmarkItem. Each item has props:
203203
204204- Data.LineNum: line index (in Strings object)
205205- Data.Kind: int kind value
206206- Data.Hint: tooltip string, which is shown on mouse-over for bookmark gutter icon
207207- Data.Tag: int value, for custom application needs
208208- Data.DeleteOnDelLine: bool, enables to auto delete item, when its line is deleted
209209- Data.ShowInBookmarkList: bool, for custom application needs
210210
211211Kind 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).
212212
213213Bookmarks 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.
214214
215215### Strings.Bookmarks2 object
216216
217217Same 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.
218218
219219### Carets object
220220
221221type TATCarets in unit atsynedit_carets. Holds list of items of type TATCaretItem. Each caret item has properties:
222222
223223- PosY: index of line in Strings object, 0-based
224224- PosX: offset of caret from line start, 0-based
225225- EndY, EndX: position for selection edge for this caret, or -1/-1 if no selection for this caret
226226- CoordY, CoordX: screen coordinates (usually you don't need these)
227227
228228For 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.
229229
230230Carets 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.
231231
232232### CaretsSel object
233233
234234type 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.
235235
236236Note: object is updated (from contents of Carets) in Paint method, not immediately after you add/change something in Carets.
237237
238238### Keymap object
239239
240240type TATKeymap in unit atsynedit_keymap.
241241
242242Holds 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.
243243
244244Each 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.
245245
246246When 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:
247247
248248``` pascal
249249 var
250250 M: TATKeymap;
251251 const
252252 cmd_My1 = 3000;
253253 cmd_My2 = 3001;
254254 begin
255255 M.Add(cmd_My1, 'cmd with simple hotkey', ['Ctrl+Shift+F'], []);
256256 M.Add(cmd_My2, 'cmd with 3 keys combo', ['Ctrl+B', 'Ctrl+B', 'Ctrl+M'], []);
257257```
258258
259259After you added new keymap items, add reaction for them: use OnCommand event as usual.
260260
261261### WrapInfo object
262262
263263type TATWrapInfo in unit atsynedit_wrapinfo. Holds list of wrap items, each is info about wrapped part of line or about entire line:
264264
265265- its original line index in Strings object (0-base)
266266- its char offset in original line (0-base)
267267- its length
268268- its indent size (if feature "wrapped indent" is on)
269269- its final-state: is it first/middle part of wrapped line, or final part
270270
271271One 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).
272272
273273WrapInfo 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).
274274
275275VirtualMode makes loading of huge logs (100-500Mb) ~1.5 times faster.
276276
277277### Undo object
278278
279279Holds 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).
280280
281281Items 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.
282282
283283### Gutter object
284284
285285type TATGutter in unit atsynedit_gutter. Holds list of gutter items. Each gutter item has properties:
286286
287287- Visible: gutter band is shown on gutter
288288- Size: width of gutter band
289289- Left/Right: calculated auto from Visible and Size, coordinates of band on control
290290
291291To 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:
292292
293293``` pascal
294294 ed.Gutter[ed.GutterBandNum].Visible:= checkGutterNum.Checked;
295295 ed.Gutter.Update;
296296 ed.Update;
297297```
298298
299299### GutterDecor object
300300
301301type 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:
302302
303303- Data.LineNum: line index (in Strings object)
304304- Data.Tag: int value, for custom app needs
305305- Data.ImageIndex: icon index in TATSynEdit.ImagesGutterDecor image list
306306- Data.Text: short string
307307- Data.TextBold: bool, text font is bold
308308- Data.TextItalic: bool, text font is italic
309309- Data.TextColor: color of text
310310- Data.DeleteOnDelLine: bool, enables to auto delete item, when its line is deleted
311311
312312Items 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.
313313
314314Duplicate 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.
315315
316316### Fold object
317317
318318type 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:
319319
320320- Y: starting line of range (0-based)
321321- Y2: ending line of range (if same as Y, plus/minus not shown on gutter)
322322- X: char-offset in starting line (must be \>0, 1 means fold from 1st char)
323323- Folded: boolean, state: folded/unfolded
324324- Staple: boolean, true if range has staple shown (vertical line on text area)
325325- Hint: string, shown in rectangle when range is folded
326326
327327Example adds few ranges to text:
328328
329329``` pascal
330330 Edit1.Fold.Clear;
331331 Edit1.Fold.Add(1, 4, 15, false, ''); //line 4 to 15, range1
332332 Edit1.Fold.Add(1, 5, 9, false, ''); //line 5 to 9, nested into range1
333333 Edit1.Fold.Add(1, 7, 8, false, ''); //line 7 to 8, nested into range2
334334```
335335
336336- To fold/unfold a range, don't change Folded field, use methods of ATSynEdit: DoRangeFold, DoRangeUnfold.
337337- To fold/unfold all, use commands via DoCommand method: cCommand_FoldAll, cCommand_UnfoldAll.
338338- In Fold object, items are not sorted, but usually syntax-adapters add these ranges as sorted (by Y).
339339- Fold object has methods, which find ranges, e.g. all ranges which contain any line from N1 to N2, etc.
340340
341341### Markers object
342342
343343type TATMarkers in unit atsynedit_markers. Holds list of marker items. Each marker item has properties:
344344
345345- PosX, PosY: position of marker (like caret position).
346346- LineLen: if not 0, render horizontal line to the left (LineLen\<0) or right (LineLen\>0).
347347- CoordX, CoordY: screen coordinates.
348348- CoordX2, CoordY2: screen coordinates of LineLen end.
349349- SelX, SelY: used in CudaText: when "collect marker" gets this marker, caret will be with selection.
350350 - if SelY=0 - SelX is length of selection (single line selection)
351351 - if SelY\>0 - SelY is Y-delta of selection end, SelX is absolute X of selection-end
352352- 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).
353353- Value (Int64): some integer value, attached to marker.
354354- Ptr (TObject): some application-specific object. Object is freed automatically when marker item is freed.
355355- MicromapOnly (enum): where to render the marker: on text area, on micromap, on both text/micromap.
356356
357357Markers 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).
358358
359359Example code from CudaText:
360360
361361``` pascal
362362procedure EditorMarkerGotoLast(Ed: TATSynEdit; AndDelete: boolean);
363363var
364364 Caret: TATCaretItem;
365365 Mark: TATMarkerItem;
366366begin
367367 if Ed.Carets.Count<>1 then exit;
368368 if Ed.Markers.Count=0 then exit;
369369 Caret:= Ed.Carets[0];
370370 Mark:= Ed.Markers[Ed.Markers.Count-1];
371371 Caret.PosX:= Mark.PosX;
372372 Caret.PosY:= Mark.PosY;
373373 if AndDelete then
374374 Ed.Markers.Delete(Ed.Markers.Count-1);
375375 Ed.DoGotoCaret(cEdgeTop);
376376 Ed.Update;
377377end;
378378```
379379
380380### Attribs object
381381
382382type 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:
383383
384384- PosX, PosY: see Markers object.
385385- SelX, SelY: see Markers object.
386386- Ptr (TObject): object of type TATLinePartClass, which has Data field. This Data is record, it contains all color/font/borders properties of this attr.
387387
388388Attribs object is used in CudaText: app adds attr items to hilite brackets, to hilite misspelled words, etc.
389389
390390If 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.
391391
392392### Colors object
393393
394394Contains color properties of type TColor. For ex, Colors.TextFont is text font color, Colors.TextBG is text background color.
395395
396396### Gaps object
397397
398398type TATGaps in unit atsynedit_gaps. Holds list of items TATGapItem. Each item has properties:
399399
400400- LineIndex: index in Strings object; and special value -1 is allowed: gap before the first line
401401- Size: height in pixels
402402- Tag: int value, to difference gaps from several plugins
403403- Bitmap: TBitmap which is painted on gap, when it's visible
404404- DeleteOnDelLine: bool, enables to auto delete item, when its line is deleted
405405
406406Gap 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.
407407
408408Use cases:
409409
410410- 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.
411411- 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.
412412
413413To add/remove gaps:
414414
415415``` pascal
416416 //to add
417417 ed.Gaps.Add(NLine, NGapHeight, FSomeBitmap, NGapTag);
418418 ed.Update;
419419 //to delete this gap
420420 ed.Gaps.DeleteForLineRange(NLine, NLine);
421421 ed.Update;
422422```
423423
424424Event 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).
425425
426426### DimRanges object
427427
428428type 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:
429429
430430- LineFrom: range starts from this line (0-based)
431431- LineTo: range ends on this line (can use MaxInt)
432432- DimValue: dim value from 0 to 255. 0 means "no effect", 255 means "transparent text".
433433
434434To dim all text except current paragraph, you must add 2 ranges: before/after paragraph, then correct line indexes in them.
435435
436436There is no checking for line indexes, ranges can have any indexes, but overlapping indexes not good (first found range will give dim value).
437437
438438Example adds 2 ranges and changes them later:
439439
440440``` pascal
441441 R1:= Ed.DimRanges.Add(0, 6, 100{dim value});
442442 R2:= Ed.DimRanges.Add(20, MaxInt, 100{dim value});
443443 Ed.Update;
444444 //change ranges
445445 R1.LineTo:= 10;
446446 R2.LineFrom:= 30;
447447 Ed.Update;
448448```
449449
450450### Hotspots object
451451
452452type 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:
453453
454454- PosX, PosY: start of range (text position)
455455- EndX, EndY: end of range
456456- Tag: some custom int value
457457- TagString: some custom string value
458458
459459You 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.
460460
461461Example 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.
462462
463463### Micromap object
464464
465465type 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".
466466
467467Type TATMicromapColumn is record with the fields:
468468
469469- NTag: Int64 tag value, so app can find this column by tag - app can change this
470470- NWidthPercents: Width of column in percents of average char width - app can change this
471471- NWidthPixels: Width of column in pixels - auto calculated, app must not change this
472472- NLeft, NRight: Control-related coordinates of column left and right edge - auto calculated, app must not change this
473473
474474Micromap 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.
475475
476476## What are the basic methods/properties?
477477
478478Objects
479479
480480- property Strings
481481- property Strings.Bookmarks
482482- property Strings.Bookmarks2
483483- property Carets
484484- property Gutter
485485- property GutterDecor
486486- property Keymap
487487- property Fold
488488- property Markers
489489- property Attribs
490490- property Colors
491491- property WrapInfo
492492- property Gaps
493493- property DimRanges
494494- property Hotspots
495495- property Micromap
496496
497497Files
498498
499499- procedure LoadFromFile(const AFilename: string): load text from file (filename uses utf-8)
500500- procedure SaveToFile(const AFilename: string): save text to file
501501
502502States
503503
504504- property Modified: modified state (cleared on file loading)
505505- property ModeOverwrite: mode insert/overwrite state (Ins key toggles it)
506506- property ModeReadOnly: mode read-only state
507507
508508More
509509
510510- 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)
511511- procedure DoCommand(ACmd: integer; const AText: UnicodeString = ""): command runner, pass command code to run it (see file atsynedit_commands)
512512- property Text: unicodestring which gives entire editor text with LF separator
513513
514514Caret
515515
516516- procedure DoCaretSingle(AX, AY: integer): make single caret
517517- function CaretPosToClientPos(P: TPoint): TPoint: convert caret coords to screen coords
518518- function ClientPosToCaretPos(P: TPoint; out AEndOfLinePos: boolean): TPoint: convert screen coords to caret coords
519519- function IsLineWithCaret(ALine: integer): boolean: is line index contains any caret
520520
521521Selection
522522
523523(P.Y is line index, P.X is char offset in line, 0-based)
524524
525525- property SelRect: for column selection, rectangle of column block
526526- function IsSelRectEmpty: is column selection exists
527527- procedure DoSelect_All: select all text (makes single caret)
528528- procedure DoSelect_Line(P: TPoint): select single line
529529- procedure DoSelect_Word(P: TPoint): select single word
530530- procedure DoSelect_LineRange(ALineFrom: integer; P: TPoint): select several lines from line index to given point
531531
532532View
533533
534534- property LineTop: index of line visible at the top of control (almost vertical scroll, though exact scroll pos is different, it considers WrapInfo object)
535535- property LineBottom: index of line visible at the bottom of control (because of folding you cannot simply calculate it)
536536- property ColumnLeft: index of left visible column (ie, horizontal scroll)
537537- procedure DoRangeFold(ARange: TATSynRange): fold range, which exists in the Fold object
538538- procedure DoRangeUnfold(ARange: TATSynRange): unfold range
539539- procedure DoScrollByDelta(Dx, Dy: integer): scroll editor area by Dx chars right and Dy lines down
540540- procedure DoGotoPos(APnt: TPoint; AIndentHorz, AIndentVert: integer): scroll editor to given caret pos
541541
542542Options
543543
544544I 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.
545545
546546Encodings
547547
548548- Strings.EncodingDetect: boolean
549549- Strings.Encoding: enum (cEncAnsi, cEncUTF8, cEncWideLE, cEncWideBE)
550550- Strings.EncodingCodepage: string (used for Encoding=cEncAnsi)
551551- Strings.SaveSignUtf8: boolean
552552
553553## Basic example how to use?
554554
555555- Add "uses ATSynEdit".
556556- Add variable "ed".
557557- Add OnCreate event to new form.
558558- How to load file "unit1.pas":
559559
560560``` pascal
561561 procedure TForm1.FormCreate(Sender: TObject);
562562 begin
563563 ed:= TATSynEdit.Create(Self);
564564 ed.Parent:= Self;
565565 ed.Align:= alClient;
566566 ed.Font.Name:= 'Courier New';
567567 ed.OptUnprintedVisible:= false;
568568 ed.OptRulerVisible:= false;
569569 ed.LoadFromFile(ExtractFilePath(Application.ExeName)+'unit1.pas');
570570 end;
571571```
572572
573573- How to save file: call "ed.SaveToFile()".
574574
575575## How does it manage the coordinates?
576576
577577Carets object contains list of caret items. Each caret item, e.g. Carets\[0\], has properties:
578578
579579- PosY: line index in Strings object
580580- PosX: char offset from line start
581581- EndY, EndX: selection end for caret, both -1 if no selection
582582- CoordX, CoordY: control-relative coordinates of caret
583583
584584Strings 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.
585585
586586### Text position to/from screen coordinates
587587
588588Methods 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.
589589
590590``` pascal
591591 function CaretPosToClientPos(P: TPoint): TPoint;
592592 function ClientPosToCaretPos(P: TPoint;
593593 out ADetails: TATPosDetails;
594594 AGapCoordAction: TATGapCoordAction=cGapCoordToLineEnd): TPoint;
595595```
596596
597597- Param ADetails: returned True if text pos is after real end-of-line, False if text pos is inside line.
598598- Param AGapCoordAction: you must specify here what to do if coordinates are over inter-line gap (see Gaps object).
599599
600600Functions give Result.Y\<0 if cannot calculate pos. For example, if line index is too big or line with this index is fully folded.
601601
602602Property OptCaretVirtual exists. If it's True, caret is allowed after end-of-line. Methods consider it.
603603
604604### Text posistion to/from column index
605605
606606To 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:
607607
608608``` pascal
609609 var
610610 ed: TATSynEdit;
611611 caret: TATCaretItem;
612612 //...
613613
614614 caret:= ed.Carets[0];
615615 if ed.Strings.IsIndexValid(caret.PosY) then
616616 begin
617617 //optimized for huge lines
618618 ColumnIndex:= ed.Strings.CharPosToColumnPos(caret.PosY, caret.PosX, ed.TabHelper)+1;
619619 end;
620620```
621621
622622## How to manage the caret, the selection?
623623
624624See 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.
625625
626626Better 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.
627627
628628You can delete and add caret items.
629629
630630``` pascal
631631 var
632632 Caret: TATCaretItem;
633633 i: integer;
634634 begin
635635 for i:= 0 to edit.Carets.Count-1 do
636636 begin
637637 Caret:= edit.Carets[i];
638638 {
639639 change here Caret.PosX and Caret.PosY
640640 }
641641 end;
642642 edit.Update;
643643 end;
644644```
645645
646646## How to modify text?
647647
648648See 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.
649649
650650To do advanced changes, use TATStrings.Text* methods:
651651
652652``` pascal
653653 procedure TextInsert(AX, AY: integer; const AText: atString; AOverwrite: boolean;
654654 out AShift, APosAfter: TPoint);
655655 procedure TextAppend(const AText: atString; out AShift, APosAfter: TPoint);
656656 procedure TextInsertColumnBlock(AX, AY: integer; ABlock: TATStrings;
657657 AOverwrite: boolean);
658658 procedure TextDeleteLeft(AX, AY: integer; ALen: integer; out AShift, APosAfter: TPoint);
659659 procedure TextDeleteRight(AX, AY: integer; ALen: integer; out AShift,
660660 APosAfter: TPoint; ACanDelEol: boolean=true);
661661 function TextDeleteRange(AFromX, AFromY, AToX, AToY: integer; out AShift, APosAfter: TPoint): boolean;
662662 procedure TextInsertEol(AX, AY: integer; AKeepCaret: boolean;
663663 const AStrIndent: atString; out AShift, APosAfter: TPoint);
664664 procedure TextDeleteLine(AX, AY: integer; out AShift, APosAfter: TPoint);
665665 procedure TextReplaceInOneLine(AY, AX1, AX2: integer; const AText: atString);
666666 procedure TextReplaceRange(AFromX, AFromY, AToX, AToY: integer;
667667 const AText: atString; out AShift, APosAfter: TPoint);
668668 function TextReplaceLines_UTF8(ALineFrom, ALineTo: integer; ANewLines: TStringList): boolean;
669669 function TextSubstring(AX1, AY1, AX2, AY2: integer; const AEolString: string = #10): atString;
670670```
671671
672672Strings.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\].
673673
674674- 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).
675675- 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).
676676
677677## How to highlight syntax?
678678
679679There're 2 ways:
680680
681681- OnCalcHilite event (better, if your code is small, e.g. colorize few tokens)
682682- Adapter (better, if your code is big, e.g. support for EControl lexers)
683683
684684You 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:
685685
686686``` pascal
687687 Adapter.AddEditor(editor1);
688688 Adapter.AddEditor(editor2); //if 2 editors share the same text buffer
689689```
690690
691691To unlink adapter from all editors, call:
692692
693693``` pascal
694694 Adapter.AddEditor(nil);
695695```
696696
697697If adapter assigned, editor will call adapter's methods (first called adapter, next OnCalcHilite).
698698
699699See file "atsynedit_adapters.pas". It has base class for such adapters. Main method in adapter, which is called by ATSynEdit is:
700700
701701``` pascal
702702 procedure OnEditorCalcHilite(Sender: TObject;
703703 var AParts: TATLineParts;
704704 ALineIndex, ACharIndex, ALineLen: integer;
705705 var AColorAfterEol: TColor);
706706```
707707
708708Parameters here:
709709
710710- Sender: editor object.
711711- 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.
712712- ALineIndex: index of passed line (0-based).
713713- ACharIndex: index of 1st char in line (it's not 0 if wrapped part of line is shown, 0-based).
714714- ALineLen: length of substring to hilite.
715715- AColorAfterEol: return here color for background of line after end-of-line. Return clNone if no color needed.
716716
717717Real string which you must hilite: see example project how to get it.
718718
719719## How to set same text-source to several editors?
720720
721721Several editors can have same text source, text source is ATStrings object which holds
722722
723723- same text for all editors
724724- separate folding states for each editor
725725
726726Each editor holds its own WrapInfo object so wrapped states are separate. Each editor holds its own Carets object, so carets/selections are separate.
727727
728728Tech details: items in ATStrings object have fields:
729729
730730``` pascal
731731 ItemHidden: packed array[0..cMaxStringsClients-1] of ByteBool;
732732 // this line is fully hidden
733733 ItemFoldFrom: packed array[0..cMaxStringsClients-1] of byte;
734734 // 0: line not folded
735735 // >0: line folded from this char-pos
736736```
737737
738738(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.
739739
740740- change cMaxStringsClients to 4
741741- set ed0.EditorIndex:=0
742742- set ed1.EditorIndex:=1
743743- set ed2.EditorIndex:=2
744744- set ed3.EditorIndex:=3
745745- set ed1.Strings:=ed0.Strings
746746- set ed2.Strings:=ed0.Strings
747747- set ed3.Strings:=ed0.Strings
748748
749749If user changes text in ed3, ed3 will update but others will not: you need to force it:
750750
751751- ed0.Update(true)
752752- ed1.Update(true)
753753- ed2.Update(true)
754754
755755## How to add/change commands?
756756
757757To 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.
758758
759759Example func which adds new commands to Keymap object:
760760
761761``` pascal
762762const
763763 cmd_FileNew = 2500;
764764 ...
765765
766766procedure InitKeymapApp(M: TATKeymap);
767767begin
768768 M.Add(cmd_FileNew, 'file: new file', ['Ctrl+N'], []);
769769 M.Add(cmd_FileOpen, 'file: open file', ['Ctrl+O'], []);
770770 M.Add(cmd_FileSave, 'file: save file', ['Ctrl+S'], []);
771771 ...
772772end;
773773```
774774
775775Example handler for new commands: some close editor tabs (special case: cannot close tabs just inside OnCommand) and some commands copy something to clipboard:
776776
777777``` pascal
778778procedure TfmMain.EditorOnCommand(Sender: TObject; Cmd: integer; var Handled: boolean);
779779begin
780780 Handled:= true; //by default command marked as handled
781781 case Cmd of
782782 cmd_FileNew,
783783 cmd_FileOpen,
784784 cmd_FileClose,
785785 cmd_FileCloseAll,
786786 cmd_FileReopen:
787787 begin
788788 //these commands close tabs so cannot handle here.
789789 //we start timer which will handle them.
790790 TimerCmd.Tag:= Cmd;
791791 TimerCmd.Enabled:= true;
792792 end;
793793
794794 cmd_CopyLine: DoCopyLine;
795795 cmd_CopyFilenameFull: DoCopyFilenameFull;
796796 cmd_CopyFilenameDir: DoCopyFilenameDir;
797797 cmd_CopyFilenameName: DoCopyFilenameName;
798798
799799 else
800800 Handled:= false; //unknown command: don't mark handled
801801 end;
802802end;
803803```
804804
805805## How to emulate Edit, Combobox?
806806
807807Special 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.
808808
809809Special 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.
810810
811811![](/wiki-images/3/3b/Atsynedit_combo.png)
812812
813813Variants must be added to history by user code, using OnCommand handler: react to command "cCommand_KeyEnter". Example of handler:
814814
815815``` pascal
816816procedure TfmCombo.ComboCommand(Sender: TObject; ACmd: integer;
817817 var AHandled: boolean);
818818var
819819 s: string;
820820 n: integer;
821821begin
822822 if ACmd=cCommand_KeyEnter then
823823 begin
824824 with ed do
825825 begin
826826 s:= UTF8Encode(Trim(Text));
827827
828828 Text:= '';
829829 DoCaretSingle(0, 0);
830830
831831 n:= Items.IndexOf(s);
832832 if n>=0 then Items.Delete(n);
833833 Items.Insert(0, s);
834834 end;
835835 AHandled:= true;
836836 end;
837837end;
838838```
839839
840840## What props are useful for statusbar?
841841
842842- number of carets: Carets.Count
843843- positions of carets: Carets.Items\[i\].PosX (column), PosY (line)
844844- text encoding: Strings.Encoding
845845 - cEncAnsi: then codepage is given by Strings.EncodingCodepage
846846 - cEncUTF8: then presence of utf8 bom is given by Strings.SaveSignUtf8
847847 - cEncWideLE, cEncWideBE: then presense of Unicode bom is given by Strings.SaveSignWide
848848- text line endings (win/unix/mac): Strings.Endings
849849- tab-char width: OptTabSize
850850- tab-char entered by spaces: OptTabSpaces
851851- index of top visible line: LineTop
852852- index of left visible column: ColumnLeft
853853- modified state: Modified
854854- read-only mode: ModeReadOnly
855855- insert/overwrite mode: ModeOverwrite
856856- word-wrap mode: OptWrapMode
857857- unprinted chars shown: OptUnprintedVisible
858858- selection: get Carets.Items\[i\].PosX/PosY/EndX/EndY, for state of column selection get IsSelRectEmpty, SelRect
859859
860860## How to customize popup menu?
861861
862862These properties allow to change all menus in control (you cannot modify default menu):
863863
864864- PopupText: for text area
865865- PopupRuler
866866- PopupMinimap
867867- PopupMicromap
868868- PopupGutterBm: for bookmarks column
869869- PopupGutterFold: for folding column
870870- PopupGutterNum: for line numbers column
871871
872872Prop OptMouse* exists, which allows to show menus on mouse-down or on mouse-up.
873873
874874## How to know indexes of changed lines in OnChange?
875875
876876You have event Strings.OnLog. It gives parameters:
877877
878878- ALine: index of line at which change occurs
879879- ALen: len of text which is added (ALen\>0) or deleted (ALen\<0) at line start
880880
881881Get the log, save it, and then parse it during OnChange.
882882
883883## How to export text to HTML?
884884
885885Unit 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.
886886
887887ATSynEdit 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.
888888
889889## Short description of events?
890890
891891- OnBeforeCalcHilite: Called before OnCalcHilite for first visible line.
892892- OnCalcBookmarkColor: Called to determine color of bookmark (background color of line with bookmark).
893893- OnCalcHilite: Called to calculate syntax highlight for a line (or wrapped part of line). You need to fill TATLineParts struct for this line.
894894- OnCalcStaple: Called to determine color of block staple (vertical line which is painted near fold-ranges).
895895- OnChange: Called after any command which changes text.
896896- OnChangeCaretPos: Called after any command which changes caret(s) position(s).
897897- OnChangeState: Called after any command which changes editor state, such as read-only, insert/overwrite, word-wrap etc.
898898- OnClickDouble: Called on double-click on text area.
899899- OnClickGutter: Called on click on gutter area.
900900- OnClickMicromap: Called on click on micromap area.
901901- OnClickMiddle: Called on middle button click on text area.
902902- OnClickTriple: Called on triple-click on text area.
903903- OnClickMoveCaret: Called just before changing caret pos via mouse click (you get prev caret pos, new caret pos).
904904- OnClickEndSelect: Called just after mouse-up, if single selection made (you get selection start pos, end pos).
905905- OnCommand: Called before running any command. You can disable any command processing, or perform custom processing before/instead of command.
906906- OnCommandAfter: Called after running command.
907907- OnDrawBookmarkIcon: Called on painting gutter mark for line with bookmark.
908908- OnDrawEditor: Called after painting entire text area.
909909- OnDrawLine: Called after painting line (or wrapped part of line) on canvas. You can paint some "image" over this line.
910910- OnDrawMicromap: Called on painting micromap area.
911911- OnDrawRuler: Called after painting ruler area.
912912- OnScroll: Called after any command which scrolls text.
913913- OnPaste: Called just before pasting text. App can disable/override default pasting.
914914- OnHotspotEnter: Called when mouse moving enters one of the ranges in Hotspots object.
915915- OnHotspotExit: Called when mouse moving exits hotspot.
916916
917917## What is logic of grouped-undo?
918918
919919There're kinds of group-marks in undo-items: hard-marks, soft-marks.
920920
921921- 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.
922922- 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.
923923
924924Example 1. OptUndoGrouped=true, such undo-items exist:
925925
926926- item1
927927- item2
928928- item3, soft-mark
929929- item4
930930- item5, hard-mark
931931- item6, hard-mark
932932- item7
933933- item8, soft-mark
934934- item9
935935- item10
936936
937937Undo:
938938
939939- undone: item10..8; until soft-mark.
940940- undone: item7..3; until soft-mark.
941941- undone: item2..1.
942942
943943Example 2. OptUndoGrouped=false, undo-items:
944944
945945- item1
946946- item2
947947- item3, hard-mark
948948- item4, hard-mark
949949- item5, hard-mark
950950- item6
951951- item7
952952
953953Undo:
954954
955955- Undone: item7
956956- Undone: item6
957957- Undone: item5..3
958958- Undone: item2
959959- Undone: item1
960960
961961Soft-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).
962962
963963Example 3. Undo-items:
964964
965965- item1, hard-mark
966966- item2, hard-mark, soft-mark
967967- item3, hard-mark
968968- item4, hard-mark, soft-mark
969969- item5, hard-mark
970970- item6, hard-mark
971971
972972Undo:
973973
974974- undone: item6..4
975975- undone: item3..2
976976- undone: item1
977977
978978## How to record/playback macros?
979979
980980Macro 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).
981981
982982To playback macro, call DoCommand for all codes in macro.
983983
984984Hint. CudaText also adds 3 commands to help with macros:
985985
986986- "mouse click" (command recorded in OnClick event of editor)
987987- "goto pos" (command recorded by GoTo dialog)
988988- "finder action" (command recorded when doing find/replace actions)
989989
990990## How to configure modifier-keys with click?
991991
992992You 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:
993993
994994``` pascal
995995procedure InitMouseActions(var M: TATMouseActions);
996996//...
997997begin
998998 SetLength(M, 0);
999999 Add(cMouseActionClickSimple, [ssLeft]);
10001000 Add(cMouseActionClickRight, [ssRight]);
10011001 Add(cMouseActionClickAndSelBlock, [ssLeft, ssShift]);
10021002 Add(cMouseActionMakeCaret, [ssLeft, ssXControl]);
10031003 Add(cMouseActionMakeCaretsColumn, [ssLeft, ssXControl, ssShift]);
10041004 Add(cMouseActionNiceScrolling, [ssMiddle]);
10051005 //...
10061006```
10071007
10081008## Does it use PrimarySelection on gtk?
10091009
10101010It can use it, all methods which work with clipboard, get AClipboardObject:TClipboard, here you can pass Clipboard, PrimarySelection, SecondarySelection.
10111011
10121012Command codes for Paste: exist for Clipboard, and for PrimarySelection. This is part which reacts to command codes:
10131013
10141014``` pascal
10151015 cCommand_ClipboardCopy: Res:= DoCommand_ClipboardCopy(false, Clipboard);
10161016 cCommand_ClipboardCopyAdd: Res:= DoCommand_ClipboardCopy(true, Clipboard);
10171017 cCommand_ClipboardCut: Res:= DoCommand_ClipboardCut(Clipboard);
10181018
10191019 //use Clipboard:TClipboard
10201020 cCommand_ClipboardPaste: Res:= DoCommand_ClipboardPaste(false, false, Clipboard);
10211021 cCommand_ClipboardPaste_Select: Res:= DoCommand_ClipboardPaste(false, true, Clipboard);
10221022 cCommand_ClipboardPaste_KeepCaret: Res:= DoCommand_ClipboardPaste(true, false, Clipboard);
10231023 cCommand_ClipboardPaste_Column: Res:= DoCommand_ClipboardPasteColumnBlock(false, Clipboard);
10241024 cCommand_ClipboardPaste_ColumnKeepCaret: Res:= DoCommand_ClipboardPasteColumnBlock(true, Clipboard);
10251025
10261026 //same, but use PrimarySelection:TClipboard
10271027 cCommand_ClipboardAltPaste: Res:= DoCommand_ClipboardPaste(false, false, PrimarySelection);
10281028 cCommand_ClipboardAltPaste_Select: Res:= DoCommand_ClipboardPaste(false, true, PrimarySelection);
10291029 cCommand_ClipboardAltPaste_KeepCaret: Res:= DoCommand_ClipboardPaste(true, false, PrimarySelection);
10301030 cCommand_ClipboardAltPaste_Column: Res:= DoCommand_ClipboardPasteColumnBlock(false, PrimarySelection);
10311031 cCommand_ClipboardAltPaste_ColumnKeepCaret: Res:= DoCommand_ClipboardPasteColumnBlock(true, PrimarySelection);
10321032```
10331033
10341034Default code uses PrimarySelection in one place: on copy, it copies to Clipboard, then (under Linux/Mac) copies the same to PrimarySelection.
10351035
10361036To make middle-button-click paste from PrimarySelection, you need to handle OnClickMiddle, here's example from CudaText:
10371037
10381038``` pascal
10391039procedure TEditorFrame.EditorOnClickMiddle(Sender: TObject; var AHandled: boolean);
10401040begin
10411041 AHandled:= false;
10421042 if EditorOps.OpMouseMiddleClickPaste then
10431043 begin
10441044 AHandled:= true;
10451045 (Sender as TATSynEdit).DoCommand(cmd_MouseClickAtCursor);
10461046 (Sender as TATSynEdit).DoCommand(cCommand_ClipboardAltPaste); //uses PrimarySelection:TClipboard
10471047 exit;
10481048 end;
10491049end;
10501050```
10511051
10521052## How to handle OnCalcLineHilite event
10531053
10541054This event has type:
10551055
10561056``` pascal
10571057type
10581058 TATSynEditCalcHiliteEvent = procedure(Sender: TObject; var AParts: TATLineParts;
10591059 ALineIndex, ACharIndex, ALineLen: integer; var AColorAfterEol: TColor) of object;
10601060```
10611061
10621062Handler gets:
10631063
10641064- ALineIndex: index of line (in Strings object)
10651065- ACharIndex: offset of fragment in line (not trivial only if line is wrapped)
10661066- ALineLen: length of fragment in line (in WideChars)
10671067
10681068Handler must fill:
10691069
10701070- 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.
10711071- AColorAfterEol: color of background after line end (for empty area).
10721072
10731073Handler 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.
10741074
10751075Example. Handler gets string with Pascal syntax like "procedure AB(name: word)". It must create such "parts" for this string:
10761076
10771077- part\[0\]: for "procedure": TATLinePart properties: offset=0, len=9, etc
10781078- part\[1\]: for "AB": offset=10, len=2
10791079- part\[2\]: for "(": offset=12, len=1
10801080- part\[3\]: for "name": offset=13, len=1
10811081- part\[4\]: for ":": offset=17, len=1
10821082- part\[5\]: for "word": offset=19, len=4
10831083- part\[6\]: for ")": offset=23, len=1
10841084- other parts must have offset=0, len=0
10851085
10861086## Details about highlighting rendering
10871087
10881088Central 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.
10891089
10901090DoCalcLineHilite works like this:
10911091
10921092- Updates adapter's "dynamic hilites enabled" flag (to on/off) and calls adapter's OnCalcLineHilite. This makes initial version of line parts.
10931093- Calculated line parts are added to cache (same calculation of same WrapInfo item will not happen, only cache item is used).
10941094- Updates line parts via OnCalcHilite event (it's additional method to hilite, first is adapter, usually only adapter is used).
10951095- 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.
10961096- Calls DoPartCalc_ApplySelectionOver to apply selection (for all multi-carets, or one column selection) to parts.
10971097
10981098DoPartCalc_ApplySelectionOver is complex function. It updates TATLineParts. How it works:
10991099
11001100- It tests entire TATLineParts - is it all selected (optimization for selected-all), or all unselected (optimization for selected-none), or partly selected.
11011101- 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.
11021102- For parts, which are all selected, it marks them with selection color.
11031103- 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.
11041104
11051105To find/insert parts to TATLineParts, funcs DoPartFind/ DoPartInsert are used.
11061106
11071107## How to control vert/horz scrolling?
11081108
11091109Property LineTop is index of top visible line, it is not precise vert scroll position, if long wrapped lines present.
11101110
11111111Public 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.
11121112
11131113Public 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.
11141114
11151115Method DoScrollByDelta(Dx, Dy) allows to increment/decrement horz (Dx) and vert (Dy) scroll positions.
11161116
11171117## When Update call is needed?
11181118
11191119First param of Update() method is boolean: force updating the WrapInfo object.
11201120
11211121- 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).
11221122- Update(false): needed if you only changed look, ie carets, scroll position, bookmarks, markers, etc.
11231123- no Update() call is needed, if you only called DoCommand(). DoCommand does it automatically.
11241124
11251125## Behaviour of column selection
11261126
11271127ATSynEdit 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.
11281128
11291129- 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.
11301130
11311131- 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.
11321132
11331133![](/wiki-images/5/5b/Cudatext-column-sel-cjk.png)
11341134
11351135It 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.
11361136
11371137Even more weird look happens when user selects column block over word-wrapped lines.
11381138
11391139![](/wiki-images/b/ba/Cudatext-column-sel-weird.png)
11401140
11411141Here 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.
11421142
11431143## Horizontal scrollbar only increases but never shrinks?
11441144
11451145Horizontal 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.
11461146
11471147You can try to disable this workaround in file atsynedit_defines.inc:
11481148
11491149``` pascal
11501150{$define fix_horzscroll} //workaround for gtk2 widgetset unstable: it freezes app
11511151 //when horz-scroll hides/shows/hides/...
11521152 //ok also for win32
11531153```
11541154
11551155## more
11561156
11571157# Auto-completion lists
11581158
11591159## General auto-completion API
11601160
11611161You need to install package ATSynEdit_Cmp, from repo <https://github.com/Alexey-T/ATSynEdit_Cmp> .
11621162
11631163Unit 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.
11641164
11651165``` pascal
11661166type
11671167 TATCompletionPropEvent = procedure (Sender: TObject;
11681168 AContent: TStringList; out ACharsLeft, ACharsRight: integer) of object;
11691169 TATCompletionResultEvent = procedure (Sender: TObject;
11701170 const ASnippetId: string; ASnippetIndex: integer) of object;
11711171
11721172procedure EditorShowCompletionListbox(AEd: TATSynEdit;
11731173 AOnGetProp: TATCompletionPropEvent;
11741174 AOnResult: TATCompletionResultEvent = nil;
11751175 AOnChoose: TATCompletionResultEvent = nil;
11761176 const ASnippetId: string = '';
11771177 ASelectedIndex: integer = 0;
11781178 AAllowCarets: boolean = false);
11791179```
11801180
11811181Callback AOnGetProp params:
11821182
11831183- 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:
11841184
11851185``` pascal
11861186 'func|FuncName|(param1, param2)'#9'Func description';
11871187```
11881188
11891189- - 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\>".
11901190 - 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).
11911191
11921192- 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.
11931193
11941194Notes:
11951195
11961196- Function EditorShowCompletionListbox doesn't work for a) read-only editor, b) multi-carets.
11971197- You can change colors/sizes/font of listbox: see unit source, it gives the global variable (record with many fields).
11981198
11991199## Auto-completion using acp-files
12001200
12011201Unit 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.
12021202
12031203![](/wiki-images/a/a3/Cudatext-php-complete.png)
12041204
12051205## Auto-completion for CSS
12061206
12071207Unit 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:
12081208
12091209- caret is on css attrib before ":" char - then list of css attribs is shown (which match the substring under caret),
12101210- caret is after css attrib and ":" char (before closing ";") - then list of values for this attrib is shown.
12111211- and more
12121212
12131213![](/wiki-images/5/55/Cudatext-css-complete.png)
12141214
12151215## Auto-completion for HTML
12161216
12171217Unit 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:
12181218
12191219- caret on opening/closing tag name: list of tags shown
12201220- caret after tag, on attribute place before "=": list of attribs for found tag shown
12211221- caret after tag, after attribute, after "=": list of values of found tag+attrib shown
12221222- and more
12231223
12241224![](/wiki-images/7/7b/Cudatext-html-complete.png)
12251225
12261226# Finder
12271227
12281228Finder class TATEditorFinder is in unit atsynedit_finder. To use it, create Finder object and set its props:
12291229
12301230- Editor: editor object (text will be read automatically)
12311231- StrFind: search-string (simple string or regex)
12321232- StrReplace: replace-string (simple string or some regex, eg with "\$0", "\$1")
12331233- boolean search flags:
12341234 - OptRegex: treat StrFind/StrReplace as regex
12351235 - OptCase: case-sensitive (for normal and regex modes)
12361236 - OptWords: whole-words search (for non-regex mode, for regex you must use "\b" specifier in search-string)
12371237 - OptBack: backward search (for non-regex mode)
12381238 - OptWrapped: continue search from top, if bottom reached (for back-search: continue from bottom, if top reached)
12391239 - OptInSelection: do search/replace in selection only (limitation exists: only 1st selection used)
12401240 - OptFromCaret: search goes from position of first caret (else: from start/end of text)
12411241 - OptConfirmReplace: each replace will occur after event OnConfirmReplace
12421242
12431243Dialog of demo:
12441244
12451245![](/wiki-images/b/b3/Atsynedit_finder.png)
12461246
12471247Methods to find/replace text (they will place selection on found match, and scroll editor to caret, except method CountAll):
12481248
12491249``` pascal
12501250function DoAction_FindSimple(const APosStart: TPoint): boolean;
12511251function DoAction_FindOrReplace(AReplace, AForMany: boolean; out AChanged: boolean;
12521252 AUpdateCaret: boolean): boolean;
12531253function DoAction_ReplaceSelected(AUpdateCaret: boolean): boolean;
12541254procedure DoAction_FindAll(AResults: TATFinderResults; AWithEvent: boolean);
12551255function DoAction_CountAll(AWithEvent: boolean): integer;
12561256procedure DoAction_ExtractAll(AWithEvent: boolean; AMatches: TStringList; ASorted: boolean;
12571257 ADuplicates: TDuplicates);
12581258function DoAction_ReplaceAll: integer;
12591259```
12601260
12611261Found 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).
12621262
12631263Finder has events:
12641264
12651265- OnProgress: allows to show progressbar during search
12661266- OnFound: called when result found
12671267- OnConfirmReplace: called before replacing text (to show confirmation) if OptConfirmReplace set
12681268
12691269Finder 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.
12701270
12711271# Adapters
12721272
12731273## Adapters description
12741274
12751275To support syntax-highlighting in some way, you need
12761276
12771277- Add event OnCalcHilite handler, which must fill the TATLineParts array with needed highlighting, or
12781278- 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:
12791279
12801280``` pascal
12811281type
12821282 TATAdapterHilite = class(TComponent)
12831283 ...
12841284 public
12851285 constructor Create(AOwner: TComponent); override;
12861286 //
12871287 procedure OnEditorChange(Sender: TObject); virtual;
12881288 //called when editor's text changes.
12891289
12901290 procedure OnEditorChangeEx(Sender: TObject; AChange: TATLineChangeKind; ALine, AItemCount: integer); virtual;
12911291 //detailed version of OnEditorChange
12921292
12931293 procedure OnEditorIdle(Sender: TObject); virtual;
12941294 //called after text is changed, and pause passed (OptIdleInterval)
12951295 //fast changes (faster than OptIdleInterval): called only after last change
12961296
12971297 procedure OnEditorCalcHilite(Sender: TObject;
12981298 var AParts: TATLineParts;
12991299 ALineIndex, ACharIndex, ALineLen: integer;
13001300 var AColorAfterEol: TColor;
13011301 AMainText: boolean); virtual;
13021302 //called to calculate hilite of entire line.
13031303 //ACharIndex is starting offset in this line, >0 if editor scrolled horizontally.
13041304 //ALineLen is len of line part, starting from ACharIndex.
13051305 ...
13061306```
13071307
13081308You 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.
13091309
13101310The 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.
13111311
13121312## Adapter for EControl lexers
13131313
13141314See [[ATSynEdit EControl adapter]]
13151315
13161316# How to adapt TRegExpr upstream for ATSynEdit
13171317
13181318TRegExpr upstream is <https://github.com/andgineer/TRegExpr> . How to convert it to atsynedit_regexpr unit.
13191319
13201320- rename unit
13211321- change "uses regexpr_unicodedata" to "uses ATSynEdit_UnicodeData"
13221322- comment line {\$I regexpr_compilers.inc}
13231323- set define on: UniCode
13241324- set define on: FastUnicodeData
13251325- set defines off: UseSpaceChars, UseWordChars
13261326- fix vars:
13271327
13281328``` pascal
13291329 RegExprModifierI: boolean = False; // default value for ModifierI
13301330 RegExprModifierR: boolean = True; // default value for ModifierR
13311331 RegExprModifierS: boolean = False; // default value for ModifierS
13321332 RegExprModifierG: boolean = True; // default value for ModifierG
13331333 RegExprModifierM: boolean = True; // default value for ModifierM
13341334 RegExprModifierX: boolean = False; // default value for ModifierX
13351335
13361336 RegExprLineSeparators: RegExprString = #$a#$b#$c
13371337 {$IFDEF UniCode}
13381338 + #$2028#$2029#$85
13391339 {$ENDIF};
13401340 RegExprLinePairedSeparator: RegExprString = ''; //#$d#$a;
13411341
13421342 FUseOsLineEndOnReplace := False;
13431343 FReplaceLineEnd := #10; //not sLineBreak, it is CR LF on Windows
13441344```
13451345
13461346---
13471347*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).*
13481348Category: ATSynEdit
13491349Category: Components

Back to history