1 {
2 Copyright (C) Alexey Torgashin, uvviewsoft.com
3 License: MPL 2.0 or LGPL
4 }
5 unit ATStrings;
6
7 {$mode objfpc}{$H+}
8 {$ModeSwitch advancedrecords}
9 {$MinEnumSize 1}
10
11 interface
12
13 uses
14 {$ifdef windows} Windows, {$endif}
15 SysUtils, Classes, Graphics, Forms,
16 ATStringProc,
17 ATStringProc_UTF8Detect,
18 ATStringProc_UTF8Decode,
19 ATStrings_Undo,
20 ATSynEdit_fgl,
21 ATSynEdit_Gaps,
22 ATSynEdit_Bookmarks,
23 ATSynEdit_Gutter_Decor,
24 ATSynEdit_Commands,
25 EncConv;
26
27 const
28 //set it to number of editors, which share same Strings obj
29 //(needed when UI tab is splitted to N parts, for the same file)
30 //set to 1 to allow only one editor for Strings obj (saves memory)
31 cMaxStringsClients = 2;
32
33 //if update count is less, do smarter wrapinfo update (find, replace items)
34 //smart update used only if lines changed, not deleted/inserted
35 cMaxUpdatesCountEasy = 200;
36
37 cStringsProgressLoadChars = 1000*1000;
38 cStringsProgressSaveLines = 100*1000;
39
40 //force utf8 for huge files on loading
41 cMaxFileSizeMbToDetectEncoding: integer = 50;
42
43 type
44 TATIntegerList = specialize TFPGList<integer>;
45
46 type
47 TATLineIndentKind = (
48 cLineIndentOther,
49 cLineIndentSpaces,
50 cLineIndentTabs
51 );
52
53 TATLineSeparator = (
54 cLineSepNone,
55 cLineSepTop,
56 cLineSepBottom
57 );
58
59 TATFileEncoding = (
60 cEncAnsi,
61 cEncUTF8,
62 cEncWideLE,
63 cEncWideBE,
64 cEnc32LE,
65 cEnc32BE
66 );
67
68 TATBlockChangeKind = (
69 cBlockDeleteLines,
70 cBlockInsertLines,
71 cBlockDeleteColumn,
72 cBlockInsertColumn
73 );
74
75 TATStringGitMarker = (
76 cGitMarkNone,
77 cGitMarkBegin,
78 cGitMarkMiddle,
79 cGitMarkEnd
80 );
81
82 const
83 cEncodingSize: array[TATFileEncoding] of integer = (1, 1, 2, 2, 4, 4);
84
85 type
86 TATTrimSpaces = (
87 cTrimLeft,
88 cTrimRight,
89 cTrimAll
90 );
91
92 type
93 TATLineFlag = (
94 cFlagUnknown,
95 cFlagNo,
96 cFlagYes
97 );
98
99 TATStringsSortAction = (
100 cSortActionAsc,
101 cSortActionDesc,
102 cSortActionAscNoCase,
103 cSortActionDescNoCase
104 );
105
106 type
107 { TATStringItem }
108
109 TATBits2 = 0..3;
110 TATStringItem_FoldFrom = 0..255; //8 bits should be enougth
111
112 TATStringItemEx = bitpacked record
113 Ends: TATBits2;
114 State: TATBits2;
115 HasTab: TATBits2;
116 HasAsciiNoTabs: TATBits2;
117 FoldFrom_0,
118 FoldFrom_1: TATStringItem_FoldFrom;
119 //0: line not folded
120 //>0: line folded from this char-pos
121 Wide: boolean;
122 Updated: boolean;
123 Sep: TATBits2;
124 Hidden_0, Hidden_1: boolean;
125 end;
126
127 TATStringItem = packed record
128 private
129 Buf: string;
GetLinenull130 function GetLine: UnicodeString;
GetLineEndsnull131 function GetLineEnds: TATLineEnds;
GetLineStatenull132 function GetLineState: TATLineState;
133 procedure SetLineW(const S: UnicodeString);
134 procedure SetLineA(const S: string);
135 public
136 Ex: TATStringItemEx;
CharLennull137 function CharLen: integer;
138 property Line: UnicodeString read GetLine write SetLineW;
139 property LineState: TATLineState read GetLineState;
140 property LineEnds: TATLineEnds read GetLineEnds;
LineSubnull141 function LineSub(AFrom, ALen: integer): UnicodeString;
142 procedure LineToBuffer(OtherBuf: PWideChar);
CharAtnull143 function CharAt(AIndex: integer): WideChar;
HasTabnull144 function HasTab: boolean;
HasAsciiNoTabsnull145 function HasAsciiNoTabs: boolean;
146 procedure Init(const S: string; AEnd: TATLineEnds);
147 procedure Init(const S: UnicodeString; AEnd: TATLineEnds);
148 procedure LineStateToChanged;
149 procedure LineStateToSaved; inline;
150 procedure LineStateToNone; inline;
IsFakenull151 function IsFake: boolean; inline;
152 procedure GetIndentProp(out ACharCount: integer; out AKind: TATLineIndentKind);
CharLenWithoutSpacenull153 function CharLenWithoutSpace: integer;
IsBlanknull154 function IsBlank: boolean;
IsGitMarkernull155 function IsGitMarker: TATStringGitMarker;
156 end;
157 PATStringItem = ^TATStringItem;
158
159 { TATStringItemList }
160
161 TATStringItemList = class(TFPSList)
162 public
163 constructor Create;
GetItemnull164 function GetItem(AIndex: integer): PATStringItem;
165 procedure Deref(Item: Pointer); override; overload;
166 procedure SortRange(L, R: integer; Compare: TFPSListCompareFunc);
167 end;
168
169 type
170 TATStringsProgressKind = (
171 cStringsProgressNone,
172 cStringsProgressLoading,
173 cStringsProgressSaving
174 );
175
176 type
TATPointArraynull177 TATStringsGetCarets = function: TATPointArray of object;
TATInt64Arraynull178 TATStringsGetMarkers = function: TATInt64Array of object;
179 TATStringsSetCarets = procedure(const ACarets: TATPointArray) of object;
180 TATStringsSetMarkers = procedure(const AMarkers: TATInt64Array) of object;
181 TATStringsChangeLogEvent = procedure(Sender: TObject; ALine: integer) of object;
182 TATStringsChangeExEvent = procedure(Sender: TObject; AChange: TATLineChangeKind; ALine, AItemCount: integer) of object;
183 TATStringsChangeBlockEvent = procedure(Sender: TObject; const AStartPos, AEndPos: TPoint;
184 AChange: TATBlockChangeKind; ABlock: TStringList) of object;
185 TATStringsUndoEvent = procedure(Sender: TObject; AX, AY: integer) of object;
186
187 type
188 { TATStrings }
189
190 TATStrings = class
191 private
192 FList: TATStringItemList;
193 FListUpdates: TATIntegerList;
194 FListUpdatesHard: boolean;
195 FGaps: TATGaps;
196 FBookmarks: TATBookmarks;
197 FBookmarks2: TATBookmarks;
198 FGutterDecor1: TATGutterDecor;
199 FGutterDecor2: TATGutterDecor;
200 FUndoList,
201 FRedoList: TATUndoList;
202 FCommandCode: integer;
203 FUndoLimit: integer;
204 FEndings: TATLineEnds;
205 FEncoding: TATFileEncoding;
206 FEncodingDetect: boolean;
207 FEncodingDetectDefaultUtf8: boolean;
208 FEncodingCodepage: TEncConvId;
209 FModified: boolean;
210 FModifiedRecent: boolean;
211 FModifiedVersion: Int64;
212 FSaveSignUtf8: boolean;
213 FSaveSignWide: boolean;
214 FReadOnly: boolean;
215 FUndoAfterSave: boolean;
216 FUndoGroupCounter: integer;
217 FOneLine: boolean;
218 FProgressValue: integer;
219 FProgressKind: TATStringsProgressKind;
220 FOnGetCaretsArray: TATStringsGetCarets;
221 FOnGetMarkersArray: TATStringsGetMarkers;
222 FOnSetCaretsArray: TATStringsSetCarets;
223 FOnSetMarkersArray: TATStringsSetMarkers;
224 FOnProgress: TNotifyEvent;
225 FOnChangeLog: TATStringsChangeLogEvent;
226 FOnChangeEx: TATStringsChangeExEvent;
227 FOnUndoBefore: TATStringsUndoEvent;
228 FOnUndoAfter: TATStringsUndoEvent;
229 FOnChangeBlock: TATStringsChangeBlockEvent;
230 FChangeBlockActive: boolean;
231 //to use with OnChangeBlock:
232 //indicates that program can ignore separate line changes in OnChange,
233 //because OnChangeBlock is called for all lines at once
234 FLastCommandChangedLines: integer;
235 FEnabledBookmarksUpdate: boolean;
236 FEnabledChangeEvents: boolean;
237 FLoadingForcedANSI: boolean;
238 FLastUndoY: integer;
239
Compare_Ascnull240 function Compare_Asc(Key1, Key2: Pointer): Integer;
Compare_AscNoCasenull241 function Compare_AscNoCase(Key1, Key2: Pointer): Integer;
Compare_Descnull242 function Compare_Desc(Key1, Key2: Pointer): Integer;
Compare_DescNoCasenull243 function Compare_DescNoCase(Key1, Key2: Pointer): Integer;
244 procedure AddUndoItem(AAction: TATEditAction; AIndex: integer;
245 const AText: atString; AEnd: TATLineEnds; ALineState: TATLineState;
246 ACommandCode: integer);
DebugTextnull247 function DebugText: string;
DoCheckFillednull248 function DoCheckFilled: boolean;
249 procedure DoFinalizeSaving;
GetCaretsArraynull250 function GetCaretsArray: TATPointArray;
GetMarkersArraynull251 function GetMarkersArray: TATInt64Array;
GetLinenull252 function GetLine(AIndex: integer): atString;
GetLineAsciinull253 function GetLineAscii(AIndex: integer): boolean;
GetLineBlanknull254 function GetLineBlank(AIndex: integer): boolean;
GetLineGitMarkernull255 function GetLineGitMarker(AIndex: integer): TATStringGitMarker;
GetLineEndnull256 function GetLineEnd(AIndex: integer): TATLineEnds;
GetLineFoldFromnull257 function GetLineFoldFrom(ALine, AClient: integer): integer;
GetLineHasTabnull258 function GetLineHasTab(AIndex: integer): boolean;
GetLineHasAsciiNoTabsnull259 function GetLineHasAsciiNoTabs(AIndex: integer): boolean;
GetLineHiddennull260 function GetLineHidden(ALine, AClient: integer): boolean;
GetLineSepnull261 function GetLineSep(AIndex: integer): TATLineSeparator;
GetLineStatenull262 function GetLineState(AIndex: integer): TATLineState;
GetLineUpdatednull263 function GetLineUpdated(AIndex: integer): boolean;
GetLineLennull264 function GetLineLen(AIndex: integer): integer;
GetLineLenPhysicalnull265 function GetLineLenPhysical(AIndex: integer): integer;
GetRedoAsStringnull266 function GetRedoAsString: string;
GetRedoCountnull267 function GetRedoCount: integer;
GetRedoEmptynull268 function GetRedoEmpty: boolean;
GetUndoAsStringnull269 function GetUndoAsString: string;
GetUndoCountnull270 function GetUndoCount: integer;
GetUndoEmptynull271 function GetUndoEmpty: boolean;
GetUndoLimitnull272 function GetUndoLimit: integer;
IsLastFakeLineUnneedednull273 function IsLastFakeLineUnneeded: boolean;
274 procedure LineAddEx(const AString: atString; AEnd: TATLineEnds);
275 procedure LineInsertRaw(ALineIndex: integer; const AString: atString; AEnd: TATLineEnds;
276 AWithEvent: boolean=true);
277 procedure LineInsertEx(ALineIndex: integer; const AString: atString; AEnd: TATLineEnds;
278 AWithEvent: boolean=true);
IsSavingWithSignaturenull279 function IsSavingWithSignature: boolean;
280 procedure SetCaretsArray(const L: TATPointArray);
281 procedure SetMarkersArray(const L: TATInt64Array);
282 procedure SetEndings(AValue: TATLineEnds);
283 procedure SetLine(AIndex: integer; const AValue: atString);
284 procedure SetLineEnd(AIndex: integer; AValue: TATLineEnds);
285 procedure SetLineFoldFrom(AIndexLine, AIndexClient: integer; AValue: integer);
286 procedure SetLineHidden(AIndexLine, AIndexClient: integer; AValue: boolean);
287 procedure SetLineSep(AIndex: integer; AValue: TATLineSeparator);
288 procedure SetLineState(AIndex: integer; AValue: TATLineState);
289 procedure SetLineUpdated(AIndex: integer; AValue: boolean);
290 procedure DoLoadFromStream(Stream: TStream; AFromUTF8: boolean; out AForcedToANSI: boolean);
291 procedure DoDetectEndings;
292 procedure DoFinalizeLoading;
293 procedure ClearLineStates(ASaved: boolean);
294 procedure SetModified(AValue: boolean);
295 procedure SetRedoAsString(const AValue: string);
296 procedure SetUndoAsString(const AValue: string);
297 procedure SetUndoLimit(AValue: integer);
298 procedure UndoSingle(ACurList: TATUndoList; out ASoftMarked, AHardMarked,
299 AHardMarkedNext, AUnmodifiedNext: boolean;
300 out ACommandCode: integer;
301 out ATickCount: QWord);
302 procedure AddUpdatesAction(N: integer; AAction: TATEditAction);
303 procedure UpdateModified;
304 public
305 CaretsAfterLastEdition: TATPointArray;
306 EditingActive: boolean;
307 EditingTopLine: integer;
308 constructor Create(AUndoLimit: integer); virtual;
309 destructor Destroy; override;
310 procedure Clear(AWithEvent: boolean=true);
311 procedure ClearSeparators;
Countnull312 function Count: integer;
IsIndexValidnull313 function IsIndexValid(N: integer): boolean; inline;
IsLastLineFakenull314 function IsLastLineFake: boolean;
IsPosFoldednull315 function IsPosFolded(AX, AY, AIndexClient: integer): boolean;
316 procedure LineAddRaw_NoUndo(const S: string; AEnd: TATLineEnds);
317 procedure LineAddRaw_NoUndo(const S: UnicodeString; AEnd: TATLineEnds);
318 procedure LineAddRaw(const AString: atString; AEnd: TATLineEnds; AWithEvent: boolean=true);
319 procedure LineAdd(const AString: atString);
320 procedure LineInsert(ALineIndex: integer; const AString: atString; AWithEvent: boolean=true);
321 procedure LineInsertStrings(ALineIndex: integer; ABlock: TATStrings; AWithFinalEol: boolean);
322 procedure LineDelete(ALineIndex: integer; AForceLast: boolean= true;
323 AWithEvent: boolean=true; AWithUndo: boolean=true);
324 procedure LineMove(AIndexFrom, AIndexTo: integer; AWithUndo: boolean=true);
325 property Lines[Index: integer]: atString read GetLine write SetLine;
326 property LinesAscii[Index: integer]: boolean read GetLineAscii;
327 property LinesLen[Index: integer]: integer read GetLineLen;
328 property LinesLenPhysical[Index: integer]: integer read GetLineLenPhysical;
329 property LinesEnds[Index: integer]: TATLineEnds read GetLineEnd write SetLineEnd;
330 property LinesHidden[IndexLine, IndexClient: integer]: boolean read GetLineHidden write SetLineHidden;
331 property LinesHasTab[Index: integer]: boolean read GetLineHasTab;
332 property LinesHasAsciiNoTabs[Index: integer]: boolean read GetLineHasAsciiNoTabs;
333 property LinesBlank[Index: integer]: boolean read GetLineBlank;
334 property LinesGitMarker[Index: integer]: TATStringGitMarker read GetLineGitMarker;
335 property LinesFoldFrom[IndexLine, IndexClient: integer]: integer read GetLineFoldFrom write SetLineFoldFrom;
336 property LinesState[Index: integer]: TATLineState read GetLineState write SetLineState;
337 property LinesUpdated[Index: integer]: boolean read GetLineUpdated write SetLineUpdated;
338 property LinesSeparator[Index: integer]: TATLineSeparator read GetLineSep write SetLineSep;
LineSubnull339 function LineSub(ALineIndex, APosFrom, ALen: integer): atString;
LineCharAtnull340 function LineCharAt(ALineIndex, ACharIndex: integer): WideChar;
341 procedure GetIndentProp(ALineIndex: integer; out ACharCount: integer; out AKind: TATLineIndentKind);
LineLenWithoutSpacenull342 function LineLenWithoutSpace(ALineIndex: integer): integer;
343 procedure LineBlockDelete(ALine1, ALine2: integer);
344 procedure LineBlockInsert(ALineFrom: integer; ANewLines: TStringList);
ColumnPosToCharPosnull345 function ColumnPosToCharPos(AIndex: integer; AX: integer; ATabHelper: TATStringTabHelper): integer;
CharPosToColumnPosnull346 function CharPosToColumnPos(AIndex: integer; AX: integer; ATabHelper: TATStringTabHelper): integer;
GetItemPtrnull347 function GetItemPtr(AIndex: integer): PATStringItem;
348
349 property Encoding: TATFileEncoding read FEncoding write FEncoding;
350 property EncodingCodepage: TEncConvId read FEncodingCodepage write FEncodingCodepage;
351 property EncodingDetect: boolean read FEncodingDetect write FEncodingDetect;
352 property EncodingDetectDefaultUtf8: boolean read FEncodingDetectDefaultUtf8 write FEncodingDetectDefaultUtf8;
353 property Endings: TATLineEnds read FEndings write SetEndings;
354 property LoadingForcedANSI: boolean read FLoadingForcedANSI;
355 property ListUpdates: TATIntegerList read FListUpdates;
356 property ListUpdatesHard: boolean read FListUpdatesHard write FListUpdatesHard;
357 property Modified: boolean read FModified write SetModified;
358 property ModifiedRecent: boolean read FModifiedRecent write FModifiedRecent;
359 property ModifiedVersion: Int64 read FModifiedVersion;
360 property OneLine: boolean read FOneLine write FOneLine;
361 property ProgressValue: integer read FProgressValue write FProgressValue;
362 property ProgressKind: TATStringsProgressKind read FProgressKind write FProgressKind;
363 property ChangeBlockActive: boolean read FChangeBlockActive write FChangeBlockActive;
364 property EnabledBookmarksUpdate: boolean read FEnabledBookmarksUpdate write FEnabledBookmarksUpdate;
365 property EnabledChangeEvents: boolean read FEnabledChangeEvents write FEnabledChangeEvents;
366 property Gaps: TATGaps read FGaps;
367 property Bookmarks: TATBookmarks read FBookmarks;
368 property Bookmarks2: TATBookmarks read FBookmarks2;
369 property GutterDecor1: TATGutterDecor read FGutterDecor1 write FGutterDecor1;
370 property GutterDecor2: TATGutterDecor read FGutterDecor2 write FGutterDecor2;
371 property CommandCode: integer read FCommandCode write FCommandCode;
372 //actions
373 procedure ActionDeleteFakeLine;
374 procedure ActionDeleteFakeLineAndFinalEol;
375 procedure ActionDeleteDupFakeLines;
376 procedure ActionDeleteAllBlanks;
377 procedure ActionDeleteAdjacentBlanks;
378 procedure ActionDeleteAdjacentDups;
379 procedure ActionDeleteAllDups(AKeepBlanks: boolean);
380 procedure ActionAddFakeLineIfNeeded;
ActionTrimSpacesnull381 function ActionTrimSpaces(AMode: TATTrimSpaces): boolean;
ActionEnsureFinalEolnull382 function ActionEnsureFinalEol: boolean;
ActionTrimFinalEmptyLinesnull383 function ActionTrimFinalEmptyLines: boolean;
384 procedure ActionSort(AAction: TATStringsSortAction; AFrom, ATo: integer);
385 procedure ActionReverseLines;
386 procedure ActionShuffleLines;
387 procedure ActionAddJumpToUndo(constref ACaretsArray: TATPointArray);
388 //file
389 procedure LoadFromStream(Stream: TStream; AFromUTF8: boolean=false);
390 procedure LoadFromFile(const AFilename: string);
391 procedure LoadFromString(const AText: string);
392 procedure LoadFromStrings(AList: TStrings; AEnds: TATLineEnds);
393 procedure SaveToStream(AStream: TStream; AEncoding: TATFileEncoding; AWithSignature: boolean);
394 procedure SaveToFile(const AFilename: string);
395 property SaveSignUtf8: boolean read FSaveSignUtf8 write FSaveSignUtf8;
396 property SaveSignWide: boolean read FSaveSignWide write FSaveSignWide;
397 //text
398 property ReadOnly: boolean read FReadOnly write FReadOnly;
TextString_Unicodenull399 function TextString_Unicode(AMaxLen: integer=0): UnicodeString;
400 procedure TextInsert(AX, AY: integer; const AText: atString; AOverwrite: boolean;
401 out AShift, APosAfter: TPoint);
402 procedure TextAppend(const AText: atString; out AShift, APosAfter: TPoint);
403 procedure TextInsertColumnBlock(AX, AY: integer; ABlock: TATStrings;
404 AOverwrite: boolean);
405 procedure TextDeleteLeft(AX, AY: integer; ALen: integer; out AShift,
406 APosAfter: TPoint; AllowGoToPrevLine: boolean);
407 procedure TextDeleteRight(AX, AY: integer; ALen: integer; out AShift,
408 APosAfter: TPoint; ACanDelEol: boolean=true);
TextDeleteRangenull409 function TextDeleteRange(AFromX, AFromY, AToX, AToY: integer; out AShift, APosAfter: TPoint): boolean;
410 procedure TextInsertEol(AX, AY: integer; AKeepCaret: boolean;
411 const AStrIndent: atString; out AShift, APosAfter: TPoint);
412 procedure TextDeleteLine(AX, AY: integer; out AShift, APosAfter: TPoint);
413 procedure TextReplace_OneLine(AY, AX1, AX2: integer; const AText: atString);
414 procedure TextReplace_OneLine_ReplaceOneEol(AY, AX1, AX2: integer; const ATextPart1, ATextPart2: atString);
415 procedure TextReplaceRange(AFromX, AFromY, AToX, AToY: integer; const AText: atString; out AShift,
416 APosAfter: TPoint; AWithUndoGroup: boolean);
TextReplaceLines_UTF8null417 function TextReplaceLines_UTF8(ALineFrom, ALineTo: integer; ANewLines: TStringList): boolean;
TextSubstringnull418 function TextSubstring(AX1, AY1, AX2, AY2: integer; const AEolString: UnicodeString = #10): atString;
TextSubstringLengthnull419 function TextSubstringLength(AX1, AY1, AX2, AY2: integer; const AEolString: UnicodeString=#10): integer;
420 //undo
421 property OnGetCaretsArray: TATStringsGetCarets read FOnGetCaretsArray write FOnGetCaretsArray;
422 property OnGetMarkersArray: TATStringsGetMarkers read FOnGetMarkersArray write FOnGetMarkersArray;
423 property OnSetCaretsArray: TATStringsSetCarets read FOnSetCaretsArray write FOnSetCaretsArray;
424 property OnSetMarkersArray: TATStringsSetMarkers read FOnSetMarkersArray write FOnSetMarkersArray;
425 procedure SetGroupMark;
426 procedure SetNewCommandMark;
427 procedure BeginUndoGroup;
428 procedure EndUndoGroup;
429 procedure UndoOrRedo(AUndo: boolean; AGrouped: boolean);
430 property UndoLimit: integer read GetUndoLimit write SetUndoLimit;
431 property UndoAfterSave: boolean read FUndoAfterSave write FUndoAfterSave;
432 property UndoCount: integer read GetUndoCount;
433 property RedoCount: integer read GetRedoCount;
434 property UndoEmpty: boolean read GetUndoEmpty;
435 property RedoEmpty: boolean read GetRedoEmpty;
436 property UndoAsString: string read GetUndoAsString write SetUndoAsString;
437 property RedoAsString: string read GetRedoAsString write SetRedoAsString;
438 procedure ClearUndo(ALocked: boolean = false);
439 procedure ClearLineStatesUpdated;
440 procedure DoEventLog(ALine: integer);
441 procedure DoEventChange(AChange: TATLineChangeKind; ALineIndex, AItemCount: integer);
442 //misc
443 procedure ActionSaveLastEditionPos(AX: integer=-1; AY: integer=-1);
444 procedure ActionGotoLastEditionPos;
445 procedure DoOnChangeBlock(AX1, AY1, AX2, AY2: integer;
446 AChange: TATBlockChangeKind; ABlock: TStringList);
447 property LastCommandChangedLines: integer read FLastCommandChangedLines write FLastCommandChangedLines;
448 //events
449 property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
450 property OnChangeLog: TATStringsChangeLogEvent read FOnChangeLog write FOnChangeLog;
451 property OnChangeEx: TATStringsChangeExEvent read FOnChangeEx write FOnChangeEx;
452 property OnChangeBlock: TATStringsChangeBlockEvent read FOnChangeBlock write FOnChangeBlock;
453 property OnUndoBefore: TATStringsUndoEvent read FOnUndoBefore write FOnUndoBefore;
454 property OnUndoAfter: TATStringsUndoEvent read FOnUndoAfter write FOnUndoAfter;
455 end;
456
457 type
458 TBufferUTF8State = ATStringProc_Utf8Detect.TBufferUTF8State;
459
ATStrings_To_StringListnull460 function ATStrings_To_StringList(AStr: TATStrings): TStringList;
DetectStreamUtf8NoBomnull461 function DetectStreamUtf8NoBom(Stream: TStream; BufSizeKb: word): TBufferUTF8State;
DetectStreamUtf16NoBomnull462 function DetectStreamUtf16NoBom(Stream: TStream; BufSizeWords: integer; out IsLE: boolean): boolean;
463
464 var
465 GlobalDetectUtf8BufferKb: integer = 8;
466 GlobalDetectUf16BufferWords: integer = 5;
467
468 implementation
469
470 uses
471 FileUtil,
472 LCLVersion,
473 Math,
474 ATStringProc_Separator;
475
476 const
477 cSignUTF8: string = #$EF#$BB#$BF;
478 cSignWideLE: string = #$FF#$FE;
479 cSignWideBE: string = #$FE#$FF;
480 cSign32LE: string = #$FF#$FE#0#0;
481 cSign32BE: string = #0#0#$FE#$FF;
482
483 procedure DoEncError;
484 begin
485 raise Exception.Create('Unknown enc value');
486 end;
487
488 procedure _ReadFileToStream(AStream: TStream;
489 const AFileName: string;
490 AMaxSize: integer=20*1024*1024);
491 const
492 BufSize = 4096;
493 var
494 Buf: array[0..BufSize-1] of char;
495 fs: TFileStream;
496 NSize, NTotalSize: integer;
497 begin
498 fs:= TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone);
499 try
500 AStream.Position:= 0;
501 NTotalSize:= 0;
502 repeat
503 NSize:= fs.Read(Buf, BufSize);
504 if NSize>0 then
505 begin
506 AStream.Write(Buf, NSize);
507 Inc(NTotalSize, NSize);
508 if NTotalSize>=AMaxSize then
509 Break;
510 end;
511 until NSize<BufSize;
512 finally
513 FreeAndNil(fs);
514 end;
515 end;
516
517 { TATStringItem }
518
IsFakenull519 function TATStringItem.IsFake: boolean; inline;
520 begin
521 Result:=
522 (Buf='') and
523 (LineEnds=cEndNone);
524 end;
525
526 procedure TATStringItem.GetIndentProp(out ACharCount: integer; out
527 AKind: TATLineIndentKind);
528 var
529 NSpaces, NTabs: integer;
530 i: integer;
531 begin
532 ACharCount:= 0;
533 AKind:= cLineIndentOther;
534 NSpaces:= 0;
535 NTabs:= 0;
536
537 for i:= 1 to CharLen do
538 begin
539 case CharAt(i) of
540 ' ':
541 begin
542 Inc(ACharCount);
543 Inc(NSpaces);
544 end;
545 #9:
546 begin
547 Inc(ACharCount);
548 Inc(NTabs);
549 end
550 else
551 Break;
552 end;
553 end;
554
555 if ACharCount=0 then exit;
556 if (NSpaces>0) and (NTabs>0) then exit;
557 if NSpaces>0 then
558 AKind:= cLineIndentSpaces
559 else
560 AKind:= cLineIndentTabs;
561 end;
562
TATStringItem.CharLenWithoutSpacenull563 function TATStringItem.CharLenWithoutSpace: integer;
564 var
565 ch: WideChar;
566 begin
567 Result:= CharLen;
568 repeat
569 if Result<=0 then Break;
570 ch:= CharAt(Result);
571 if not IsCharSpace(ch) then Break;
572 Dec(Result);
573 until false;
574 end;
575
IsBlanknull576 function TATStringItem.IsBlank: boolean;
577 var
578 PtrChar: PChar;
579 Len, i: integer;
580 code: byte;
581 begin
582 Len:= Length(Buf);
583 if Len=0 then
584 exit(true);
585 if Ex.Wide then
586 exit(false);
587 PtrChar:= PChar(Buf);
588 for i:= 1 to Len do
589 begin
590 code:= byte(PtrChar^);
591 Inc(PtrChar);
592 if code=9 then Continue;
593 if code=32 then Continue;
594 exit(false);
595 end;
596 Result:= true;
597 end;
598
TATStringItem.IsGitMarkernull599 function TATStringItem.IsGitMarker: TATStringGitMarker;
600 const
601 MarkBegin: PChar = '<<<<<<< ';
602 MarkMiddle: PChar = '=======';
603 MarkEnd: PChar = '>>>>>>> ';
604 var
605 NLen: integer;
606 begin
607 Result:= cGitMarkNone;
608 if Ex.Wide then exit;
609 NLen:= Length(Buf);
610 if NLen>=7 then
611 begin
612 if NLen>=8 then
613 begin
614 if strlcomp(PChar(Buf), MarkBegin, 8)=0 then
615 exit(cGitMarkBegin);
616 if strlcomp(PChar(Buf), MarkEnd, 8)=0 then
617 exit(cGitMarkEnd);
618 end;
619 if NLen=7 then
620 if strlcomp(PChar(Buf), MarkMiddle, 7)=0 then
621 exit(cGitMarkMiddle);
622 end;
623 end;
624
625
TATStringItem.CharLennull626 function TATStringItem.CharLen: integer;
627 begin
628 if Ex.Wide then
629 Result:= Length(Buf) div 2
630 else
631 Result:= Length(Buf);
632 end;
633
GetLinenull634 function TATStringItem.GetLine: UnicodeString;
635 var
636 NLen, i: integer;
637 begin
638 NLen:= Length(Buf);
639 if NLen=0 then exit('');
640 if Ex.Wide then
641 begin
642 SetLength(Result, NLen div 2);
643 Move(Buf[1], Result[1], NLen);
644 end
645 else
646 begin
647 SetLength(Result, NLen);
648 for i:= 1 to NLen do
649 Result[i]:= WideChar(Ord(Buf[i]));
650 end;
651 end;
652
653 procedure TATStringItem.LineToBuffer(OtherBuf: PWideChar);
654 //OtherBuf must point to WideChar array of enough size
655 var
656 NLen, i: integer;
657 SrcBuf: PChar;
658 begin
659 NLen:= Length(Buf);
660 if NLen=0 then exit;
661 if Ex.Wide then
662 begin
663 Move(Buf[1], OtherBuf^, NLen);
664 end
665 else
666 begin
667 SrcBuf:= PChar(Buf);
668 for i:= 1 to NLen do
669 begin
670 OtherBuf^:= WideChar(Ord(SrcBuf^));
671 Inc(SrcBuf);
672 Inc(OtherBuf);
673 end;
674 end;
675 end;
676
GetLineEndsnull677 function TATStringItem.GetLineEnds: TATLineEnds;
678 begin
679 Result:= TATLineEnds(Ex.Ends);
680 end;
681
TATStringItem.GetLineStatenull682 function TATStringItem.GetLineState: TATLineState;
683 begin
684 Result:= TATLineState(Ex.State);
685 end;
686
687 procedure TATStringItem.SetLineW(const S: UnicodeString);
688 var
689 NLen, i: integer;
690 begin
691 NLen:= Length(S);
692 if NLen=0 then
693 begin
694 Ex.Wide:= false;
695 Buf:= '';
696 end
697 else
698 if not IsStringWithUnicode(S) then
699 begin
700 Ex.Wide:= false;
701 SetLength(Buf, NLen);
702 for i:= 1 to NLen do
703 Buf[i]:= Chr(Ord(S[i]));
704 end
705 else
706 begin
707 Ex.Wide:= true;
708 SetLength(Buf, NLen*2);
709 Move(S[1], Buf[1], NLen*2);
710 end;
711
712 LineStateToChanged;
713 Ex.HasTab:= 0; //cFlagUnknown
714 Ex.HasAsciiNoTabs:= 0; //cFlagUnknown
715 Ex.Updated:= true;
716 end;
717
718 procedure TATStringItem.SetLineA(const S: string);
719 var
720 NLen, N: integer;
721 begin
722 LineStateToChanged;
723 Ex.HasTab:= 0; //cFlagUnknown
724 Ex.HasAsciiNoTabs:= 0; //cFlagUnknown
725 Ex.Updated:= true;
726
727 NLen:= Length(S);
728 if NLen=0 then
729 begin
730 Ex.Wide:= false;
731 Buf:= '';
732 end
733 else
734 if not IsStringWithUnicode(S) then
735 begin
736 Ex.Wide:= false;
737 Buf:= S;
738 //UniqueString(Buf); //makes slower loading file by 5-7%
739 end
740 else
741 begin
742 Ex.Wide:= true;
743 SetLength(Buf, NLen*2);
744 try
745 //this func is the same as Utf8ToUnicode but raises exception
746 N:= CustomUtf8ToUnicode(PUnicodeChar(PChar(Buf)), NLen, PChar(S), NLen);
747 if N>0 then
748 SetLength(Buf, 2*(N-1))
749 else
750 Buf:= '';
751 except
752 //failed to load as UTF8
753 //load it again with FPC which replaces bad characters with '?'
754 //and raise exception, to allow outer procedure to load file again
755 N:= Utf8ToUnicode(PUnicodeChar(PChar(Buf)), NLen, PChar(S), NLen);
756 if N>0 then
757 SetLength(Buf, 2*(N-1))
758 else
759 Buf:= '';
760 RaiseUTF8TextError;
761 end;
762 end;
763 end;
764
765 procedure TATStringItem.Init(const S: string; AEnd: TATLineEnds);
766 begin
767 FillChar(Ex, SizeOf(Ex), 0);
768 SetLineA(S);
769
770 Ex.Ends:= TATBits2(AEnd);
771 Ex.State:= TATBits2(cLineStateAdded);
772 Ex.Updated:= true;
773 end;
774
775 procedure TATStringItem.Init(const S: UnicodeString; AEnd: TATLineEnds);
776 begin
777 FillChar(Ex, SizeOf(Ex), 0);
778 SetLineW(S);
779
780 Ex.Ends:= TATBits2(AEnd);
781 Ex.State:= TATBits2(cLineStateAdded);
782 Ex.Updated:= true;
783 end;
784
785 procedure TATStringItem.LineStateToChanged;
786 //switch LineState to "changed" only for "none"+"saved" lines,
787 //but skip "added" lines
788 // https://github.com/Alexey-T/CudaText/issues/2617
789 begin
790 case TATLineState(Ex.State) of
791 cLineStateNone,
792 cLineStateSaved:
793 Ex.State:= TATBits2(cLineStateChanged);
794 end;
795 end;
796
797 procedure TATStringItem.LineStateToSaved;
798 begin
799 if TATLineState(Ex.State)<>cLineStateNone then
800 Ex.State:= TATBits2(cLineStateSaved);
801 end;
802
803 procedure TATStringItem.LineStateToNone;
804 begin
805 Ex.State:= TATBits2(cLineStateNone);
806 end;
807
TATStringItem.LineSubnull808 function TATStringItem.LineSub(AFrom, ALen: integer): UnicodeString;
809 var
810 NLen, ResLen, i: integer;
811 begin
812 Result:= '';
813 NLen:= Length(Buf);
814 if NLen=0 then exit;
815 if Ex.Wide then
816 begin
817 ResLen:= Max(0, Min(ALen, NLen div 2 - AFrom + 1));
818 SetLength(Result, ResLen);
819 if ResLen>0 then
820 Move(Buf[AFrom*2-1], Result[1], ResLen*2);
821 end
822 else
823 begin
824 ResLen:= Max(0, Min(ALen, NLen-AFrom+1));
825 SetLength(Result, ResLen);
826 for i:= 1 to ResLen do
827 Result[i]:= WideChar(Ord(Buf[i+AFrom-1]));
828 end;
829 end;
830
CharAtnull831 function TATStringItem.CharAt(AIndex: integer): WideChar;
832 var
833 NLen: integer;
834 begin
835 if AIndex<=0 then exit(#0);
836 NLen:= CharLen;
837 if NLen=0 then exit(#0);
838 if AIndex>NLen then exit(#0);
839 if Ex.Wide then
840 Move(Buf[AIndex*2-1], Result, 2)
841 else
842 Result:= WideChar(Ord(Buf[AIndex]));
843 end;
844
HasTabnull845 function TATStringItem.HasTab: boolean;
846 var
847 Value: TATLineFlag;
848 NLen, i: integer;
849 Ptr: PWideChar;
850 begin
851 case TATLineFlag(Ex.HasTab) of
852 cFlagNo:
853 exit(false);
854 cFlagYes:
855 exit(true);
856 end;
857
858 Result:= false;
859 NLen:= Length(Buf);
860 if NLen>0 then
861 if Ex.Wide then
862 begin
863 Ptr:= @Buf[1];
864 for i:= 1 to NLen div 2 do
865 begin
866 if Ptr^=#9 then
867 begin
868 Result:= true;
869 Break
870 end;
871 Inc(Ptr);
872 end;
873 end
874 else
875 begin
876 for i:= 1 to NLen do
877 if Buf[i]=#9 then
878 begin
879 Result:= true;
880 Break
881 end;
882 end;
883
884 if Result then
885 Value:= cFlagYes
886 else
887 Value:= cFlagNo;
888 Ex.HasTab:= TATBits2(Value);
889 end;
890
TATStringItem.HasAsciiNoTabsnull891 function TATStringItem.HasAsciiNoTabs: boolean;
892 var
893 Value: TATLineFlag;
894 NLen, NCode, i: integer;
895 Ptr: PWideChar;
896 begin
897 case TATLineFlag(Ex.HasAsciiNoTabs) of
898 cFlagNo:
899 exit(false);
900 cFlagYes:
901 exit(true);
902 end;
903
904 Result:= true;
905 NLen:= Length(Buf);
906 if NLen>0 then
907 if Ex.Wide then
908 begin
909 Ptr:= @Buf[1];
910 for i:= 1 to NLen div 2 do
911 begin
912 NCode:= Ord(Ptr^);
913 if (NCode<32) or (NCode>=127) then
914 begin
915 Result:= false;
916 Break
917 end;
918 Inc(Ptr);
919 end;
920 end
921 else
922 begin
923 for i:= 1 to NLen do
924 begin
925 NCode:= Ord(Buf[i]);
926 if (NCode<32) or (NCode>=127) then
927 begin
928 Result:= false;
929 Break
930 end;
931 end;
932 end;
933
934 if Result then
935 Value:= cFlagYes
936 else
937 Value:= cFlagNo;
938 Ex.HasAsciiNoTabs:= TATBits2(Value);
939 end;
940
941
ATStrings_To_StringListnull942 function ATStrings_To_StringList(AStr: TATStrings): TStringList;
943 var
944 i: integer;
945 begin
946 Result:= TStringList.Create;
947 for i:= 0 to AStr.Count-1 do
948 Result.Add(AStr.Lines[i]);
949 end;
950
951 { TATStringItemList }
952
953 constructor TATStringItemList.Create;
954 begin
955 inherited Create(SizeOf(TATStringItem));
956 end;
957
GetItemnull958 function TATStringItemList.GetItem(AIndex: integer): PATStringItem; inline;
959 begin
960 Result:= PATStringItem(Get(AIndex));
961 end;
962
963 procedure TATStringItemList.Deref(Item: Pointer);
964 begin
965 PATStringItem(Item)^.Buf:= '';
966 end;
967
968 procedure TATStringItemList.SortRange(L, R: integer; Compare: TFPSListCompareFunc);
969 begin
970 QuickSort(L, R, Compare);
971 end;
972
973 { TATStrings }
974
GetLinenull975 function TATStrings.GetLine(AIndex: integer): atString;
976 begin
977 Result:= FList.GetItem(AIndex)^.Line;
978 end;
979
GetLineAsciinull980 function TATStrings.GetLineAscii(AIndex: integer): boolean;
981 begin
982 Result:= not FList.GetItem(AIndex)^.Ex.Wide;
983 end;
984
GetLineBlanknull985 function TATStrings.GetLineBlank(AIndex: integer): boolean;
986 begin
987 Result:= FList.GetItem(AIndex)^.IsBlank;
988 end;
989
GetLineGitMarkernull990 function TATStrings.GetLineGitMarker(AIndex: integer): TATStringGitMarker;
991 begin
992 Result:= FList.GetItem(AIndex)^.IsGitMarker;
993 end;
994
GetLineLennull995 function TATStrings.GetLineLen(AIndex: integer): integer;
996 begin
997 Result:= FList.GetItem(AIndex)^.CharLen;
998 end;
999
TATStrings.GetLineEndnull1000 function TATStrings.GetLineEnd(AIndex: integer): TATLineEnds;
1001 begin
1002 Result:= FList.GetItem(AIndex)^.LineEnds;
1003 end;
1004
GetLineFoldFromnull1005 function TATStrings.GetLineFoldFrom(ALine, AClient: integer): integer;
1006 begin
1007 case AClient of
1008 0: Result:= FList.GetItem(ALine)^.Ex.FoldFrom_0;
1009 1: Result:= FList.GetItem(ALine)^.Ex.FoldFrom_1;
1010 else Result:= 0;
1011 end;
1012 end;
1013
GetLineHiddennull1014 function TATStrings.GetLineHidden(ALine, AClient: integer): boolean;
1015 begin
1016 case AClient of
1017 0: Result:= FList.GetItem(ALine)^.Ex.Hidden_0;
1018 1: Result:= FList.GetItem(ALine)^.Ex.Hidden_1;
1019 else Result:= false;
1020 end;
1021 end;
1022
TATStrings.GetLineStatenull1023 function TATStrings.GetLineState(AIndex: integer): TATLineState;
1024 begin
1025 Result:= FList.GetItem(AIndex)^.LineState;
1026 end;
1027
TATStrings.GetLineUpdatednull1028 function TATStrings.GetLineUpdated(AIndex: integer): boolean;
1029 begin
1030 Result:= FList.GetItem(AIndex)^.Ex.Updated;
1031 end;
1032
TATStrings.GetLineLenPhysicalnull1033 function TATStrings.GetLineLenPhysical(AIndex: integer): integer;
1034 var
1035 ItemPtr: PATStringItem;
1036 begin
1037 ItemPtr:= FList.GetItem(AIndex);
1038 Result:= ItemPtr^.CharLen + cLineEndLength[ItemPtr^.LineEnds];
1039 end;
1040
GetRedoAsStringnull1041 function TATStrings.GetRedoAsString: string;
1042 begin
1043 Result:= FRedoList.AsString;
1044 end;
1045
TATStrings.GetLineSepnull1046 function TATStrings.GetLineSep(AIndex: integer): TATLineSeparator;
1047 begin
1048 Result:= TATLineSeparator(FList.GetItem(AIndex)^.Ex.Sep);
1049 end;
1050
TATStrings.GetLineHasTabnull1051 function TATStrings.GetLineHasTab(AIndex: integer): boolean;
1052 begin
1053 Result:= FList.GetItem(AIndex)^.HasTab;
1054 end;
1055
GetLineHasAsciiNoTabsnull1056 function TATStrings.GetLineHasAsciiNoTabs(AIndex: integer): boolean;
1057 begin
1058 Result:= FList.GetItem(AIndex)^.HasAsciiNoTabs;
1059 end;
1060
1061
TATStrings.GetUndoCountnull1062 function TATStrings.GetUndoCount: integer;
1063 begin
1064 if Assigned(FUndoList) then
1065 Result:= FUndoList.Count
1066 else
1067 Result:= 0;
1068 end;
1069
TATStrings.GetUndoEmptynull1070 function TATStrings.GetUndoEmpty: boolean;
1071 begin
1072 Result:= FUndoList.IsEmpty;
1073 end;
1074
TATStrings.GetRedoCountnull1075 function TATStrings.GetRedoCount: integer;
1076 begin
1077 if Assigned(FRedoList) then
1078 Result:= FRedoList.Count
1079 else
1080 Result:= 0;
1081 end;
1082
TATStrings.GetRedoEmptynull1083 function TATStrings.GetRedoEmpty: boolean;
1084 begin
1085 Result:= FRedoList.IsEmpty;
1086 end;
1087
TATStrings.GetUndoAsStringnull1088 function TATStrings.GetUndoAsString: string;
1089 begin
1090 Result:= FUndoList.AsString;
1091 end;
1092
1093
TATStrings.GetUndoLimitnull1094 function TATStrings.GetUndoLimit: integer;
1095 begin
1096 if Assigned(FUndoList) then
1097 Result:= FUndoList.MaxCount
1098 else
1099 Result:= 2000;
1100 end;
1101
1102 procedure TATStrings.SetNewCommandMark;
1103 begin
1104 if Assigned(FUndoList) then
1105 FUndoList.NewCommandMark:= true;
1106 end;
1107
1108 procedure TATStrings.SetEndings(AValue: TATLineEnds);
1109 var
1110 typ: TATLineEnds;
1111 i: integer;
1112 begin
1113 if FReadOnly then Exit;
1114
1115 FEndings:= AValue;
1116 for i:= 0 to Count-1 do
1117 begin
1118 typ:= LinesEnds[i];
1119 if (typ<>AValue) and (typ<>cEndNone) then
1120 LinesEnds[i]:= AValue;
1121 end;
1122 end;
1123
1124 procedure TATStrings.SetLine(AIndex: integer; const AValue: atString);
1125 var
1126 Item: PATStringItem;
1127 begin
1128 //Assert(IsIndexValid(AIndex));
1129 if FReadOnly then Exit;
1130 Item:= FList.GetItem(AIndex);
1131
1132 UpdateModified;
1133 AddUndoItem(aeaChange, AIndex, Item^.Line, Item^.LineEnds, Item^.LineState, FCommandCode);
1134 DoEventLog(AIndex);
1135 DoEventChange(cLineChangeEdited, AIndex, 1);
1136
1137 Item^.Line:= AValue;
1138
1139 //fully unfold this line
1140 Item^.Ex.FoldFrom_0:= 0;
1141 Item^.Ex.FoldFrom_1:= 0;
1142
1143 Item^.LineStateToChanged;
1144
1145 Item^.Ex.Updated:= true;
1146 Item^.Ex.HasTab:= 0; //unknown
1147 end;
1148
1149 procedure TATStrings.SetLineSep(AIndex: integer; AValue: TATLineSeparator);
1150 var
1151 Item: PATStringItem;
1152 begin
1153 if IsIndexValid(AIndex) then
1154 begin
1155 Item:= FList.GetItem(AIndex);
1156 Item^.Ex.Sep:= TATBits2(AValue);
1157 end;
1158 end;
1159
1160
1161 procedure TATStrings.SetLineEnd(AIndex: integer; AValue: TATLineEnds);
1162 var
1163 Item: PATStringItem;
1164 begin
1165 //Assert(IsIndexValid(AIndex));
1166 if FReadOnly then Exit;
1167
1168 Item:= FList.GetItem(AIndex);
1169
1170 UpdateModified;
1171 AddUndoItem(aeaChangeEol, AIndex, '', Item^.LineEnds, Item^.LineState, FCommandCode);
1172
1173 Item^.Ex.Ends:= TATBits2(AValue);
1174 Item^.LineStateToChanged;
1175 Item^.Ex.Updated:= true;
1176 end;
1177
1178 procedure TATStrings.SetLineFoldFrom(AIndexLine, AIndexClient: integer; AValue: integer);
1179 const
1180 cMax = High(TATStringItem_FoldFrom);
1181 var
1182 Item: PATStringItem;
1183 begin
1184 //Assert(IsIndexValid(AIndexLine));
1185 if AValue<0 then AValue:= 0;
1186 if AValue>cMax then AValue:= cMax;
1187
1188 Item:= FList.GetItem(AIndexLine);
1189 case AIndexClient of
1190 0: Item^.Ex.FoldFrom_0:= AValue;
1191 1: Item^.Ex.FoldFrom_1:= AValue;
1192 end;
1193 end;
1194
1195 procedure TATStrings.SetLineHidden(AIndexLine, AIndexClient: integer; AValue: boolean);
1196 var
1197 Item: PATStringItem;
1198 begin
1199 //Assert(IsIndexValid(AIndexLine));
1200 Item:= FList.GetItem(AIndexLine);
1201 case AIndexClient of
1202 0: Item^.Ex.Hidden_0:= AValue;
1203 1: Item^.Ex.Hidden_1:= AValue;
1204 end;
1205 end;
1206
1207 procedure TATStrings.SetLineState(AIndex: integer; AValue: TATLineState);
1208 var
1209 Item: PATStringItem;
1210 begin
1211 //Assert(IsIndexValid(AIndex));
1212 Item:= FList.GetItem(AIndex);
1213 Item^.Ex.State:= TATBits2(AValue);
1214 end;
1215
1216 procedure TATStrings.SetLineUpdated(AIndex: integer; AValue: boolean);
1217 var
1218 Item: PATStringItem;
1219 begin
1220 //Assert(IsIndexValid(AIndex));
1221 Item:= FList.GetItem(AIndex);
1222 Item^.Ex.Updated:= AValue;
1223 end;
1224
1225
TATStrings.TextString_Unicodenull1226 function TATStrings.TextString_Unicode(AMaxLen: integer=0): UnicodeString;
1227 const
1228 LenEol = 1;
1229 CharEol = #10;
1230 var
1231 Len, LastIndex, i: integer;
1232 Item: PATStringItem;
1233 Ptr: pointer;
1234 bFinalEol: boolean;
1235 begin
1236 Result:= '';
1237 if Count=0 then Exit;
1238 LastIndex:= Count-1;
1239
1240 Len:= 0;
1241 for i:= 0 to LastIndex-1 do
1242 begin
1243 Item:= FList.GetItem(i);
1244 Inc(Len, Item^.CharLen+LenEol);
1245 end;
1246
1247 Item:= FList.GetItem(LastIndex);
1248 Inc(Len, Item^.CharLen);
1249
1250 bFinalEol:= LinesEnds[LastIndex]<>cEndNone;
1251 if bFinalEol then
1252 Inc(Len, LenEol);
1253
1254 if Len=0 then Exit;
1255
1256 SetLength(Result, Len);
1257 Ptr:= @Result[1];
1258
1259 for i:= 0 to LastIndex do
1260 begin
1261 Item:= FList.GetItem(i);
1262 Len:= Item^.CharLen;
1263 //copy string
1264 if Len>0 then
1265 begin
1266 if (AMaxLen>0) and (Len>AMaxLen) then
1267 FillChar(Ptr^, Len*2, $20) //fill item with spaces
1268 else
1269 Item^.LineToBuffer(Ptr);
1270 Inc(Ptr, Len*2);
1271 end;
1272 //copy eol
1273 if bFinalEol or (i<LastIndex) then
1274 begin
1275 PWideChar(Ptr)^:= CharEol;
1276 Inc(Ptr, LenEol*2);
1277 end;
1278 end;
1279 end;
1280
1281
1282 constructor TATStrings.Create(AUndoLimit: integer);
1283 begin
1284 FList:= TATStringItemList.Create;
1285 FListUpdates:= TATIntegerList.Create;
1286 FListUpdatesHard:= false;
1287 FUndoLimit:= AUndoLimit;
1288 FUndoList:= TATUndoList.Create(FUndoLimit);
1289 FRedoList:= TATUndoList.Create(FUndoLimit);
1290 FGaps:= TATGaps.Create;
1291 FBookmarks:= TATBookmarks.Create;
1292 FBookmarks2:= TATBookmarks.Create;
1293 FEnabledBookmarksUpdate:= true;
1294 FEnabledChangeEvents:= true;
1295
1296 FEncoding:= cEncUTF8;
1297 FEncodingDetect:= true;
1298 FEncodingDetectDefaultUtf8:= true;
1299 FEncodingCodepage:= EncConvGetANSI;
1300 FEndings:= cEndWin;
1301
1302 FModified:= false;
1303 FModifiedRecent:= false;
1304 FModifiedVersion:= 0;
1305 FChangeBlockActive:= false;
1306
1307 FSaveSignUtf8:= true;
1308 FSaveSignWide:= true;
1309 FUndoAfterSave:= true;
1310 FOneLine:= false;
1311 FProgressValue:= 0;
1312 FProgressKind:= cStringsProgressNone;
1313 SetLength(CaretsAfterLastEdition, 0);
1314
1315 ActionAddFakeLineIfNeeded;
1316 ClearUndo;
1317 end;
1318
1319 destructor TATStrings.Destroy;
1320 begin
1321 //disable events: so Clear won't call them
1322 FOnChangeEx:= nil;
1323 FOnChangeLog:= nil;
1324 FOnChangeBlock:= nil;
1325 FOnGetCaretsArray:= nil;
1326 FOnSetCaretsArray:= nil;
1327 FOnGetMarkersArray:= nil;
1328 FOnSetMarkersArray:= nil;
1329 FOnProgress:= nil;
1330
1331 GutterDecor1:= nil;
1332 GutterDecor2:= nil;
1333
1334 ClearUndo(true);
1335 FList.Clear; //Clear calls event, no need
1336
1337 FreeAndNil(FList);
1338 FreeAndNil(FBookmarks2);
1339 FreeAndNil(FBookmarks);
1340 FreeAndNil(FGaps);
1341 FreeAndNil(FListUpdates);
1342 FreeAndNil(FUndoList);
1343 FreeAndNil(FRedoList);
1344
1345 inherited;
1346 end;
1347
IsLastLineFakenull1348 function TATStrings.IsLastLineFake: boolean;
1349 begin
1350 Result:= (Count>0) and
1351 FList.GetItem(FList.Count-1)^.IsFake;
1352 end;
1353
IsLastFakeLineUnneedednull1354 function TATStrings.IsLastFakeLineUnneeded: boolean;
1355 begin
1356 Result:= (Count>1) and
1357 IsLastLineFake and
1358 (FList.GetItem(FList.Count-2)^.LineEnds=cEndNone);
1359 end;
1360
1361 procedure TATStrings.ActionDeleteFakeLine;
1362 begin
1363 if IsLastLineFake then
1364 LineDelete(Count-1, false{AForceLast}, false, false);
1365 end;
1366
1367 procedure TATStrings.ActionDeleteFakeLineAndFinalEol;
1368 begin
1369 ActionDeleteFakeLine;
1370 if Count>0 then
1371 if LinesEnds[Count-1]<>cEndNone then
1372 LinesEnds[Count-1]:= cEndNone;
1373 end;
1374
1375 procedure TATStrings.ActionAddFakeLineIfNeeded;
1376 begin
1377 if Count=0 then
1378 begin
1379 LineAddRaw('', cEndNone, false{AWithEvent});
1380 Exit
1381 end;
1382
1383 if IsLastLineFake then Exit;
1384
1385 if LinesEnds[Count-1]<>cEndNone then
1386 begin
1387 LineAddRaw('', cEndNone, false{AWithEvent});
1388 Exit
1389 end;
1390 end;
1391
1392 procedure TATStrings.LineAddRaw(const AString: atString; AEnd: TATLineEnds; AWithEvent: boolean);
1393 var
1394 Item: TATStringItem;
1395 begin
1396 if FReadOnly then Exit;
1397 if DoCheckFilled then Exit;
1398
1399 UpdateModified;
1400 AddUndoItem(aeaInsert, Count, '', cEndNone, cLineStateNone, FCommandCode);
1401 if AWithEvent then
1402 begin
1403 DoEventLog(Count);
1404 DoEventChange(cLineChangeAdded, Count, 1);
1405 end;
1406
1407 Item.Init(AString, AEnd);
1408 FList.Add(@Item);
1409 FillChar(Item, SizeOf(Item), 0);
1410 end;
1411
1412 procedure TATStrings.LineAddEx(const AString: atString; AEnd: TATLineEnds);
1413 var
1414 AEndInside: TATLineEnds;
1415 begin
1416 if FReadOnly then Exit;
1417
1418 AEndInside:= AEnd;
1419 if AEndInside=cEndNone then
1420 AEndInside:= FEndings;
1421
1422 if IsLastLineFake then
1423 LineInsertRaw(Count-1, AString, AEndInside)
1424 else
1425 begin
1426 LineAddRaw(AString, AEnd);
1427 if AEnd<>cEndNone then
1428 LineAddRaw('', cEndNone);
1429 end;
1430 end;
1431
1432 procedure TATStrings.LineAdd(const AString: atString);
1433 begin
1434 LineAddEx(AString, FEndings);
1435 end;
1436
1437
DoCheckFillednull1438 function TATStrings.DoCheckFilled: boolean;
1439 begin
1440 Result:= false;
1441 if FOneLine then
1442 begin
1443 Result:= Count>0;
1444 if Result then
1445 while Count>1 do
1446 LineDelete(Count-1);
1447 end;
1448 end;
1449
1450 procedure TATStrings.LineInsertRaw(ALineIndex: integer; const AString: atString;
1451 AEnd: TATLineEnds; AWithEvent: boolean=true);
1452 var
1453 Item: TATStringItem;
1454 begin
1455 if FReadOnly then Exit;
1456 if DoCheckFilled then Exit;
1457
1458 UpdateModified;
1459 AddUndoItem(aeaInsert, ALineIndex, '', cEndNone, cLineStateNone, FCommandCode);
1460
1461 if AWithEvent then
1462 begin
1463 DoEventLog(ALineIndex);
1464 DoEventChange(cLineChangeAdded, ALineIndex, 1);
1465 end;
1466
1467 Item.Init(AString, AEnd);
1468 FList.Insert(ALineIndex, @Item);
1469 FillChar(Item, SizeOf(Item), 0);
1470 end;
1471
1472 procedure TATStrings.LineInsertEx(ALineIndex: integer; const AString: atString; AEnd: TATLineEnds;
1473 AWithEvent: boolean=true);
1474 begin
1475 if FReadOnly then Exit;
1476
1477 if IsIndexValid(ALineIndex) then
1478 LineInsertRaw(ALineIndex, AString, AEnd, AWithEvent)
1479 else
1480 if ALineIndex=Count then
1481 LineAddEx(AString, AEnd);
1482 //else
1483 // raise Exception.Create('Incorrect Insert index: '+IntToStr(ALineIndex));
1484 end;
1485
1486 procedure TATStrings.LineInsert(ALineIndex: integer; const AString: atString;
1487 AWithEvent: boolean=true);
1488 begin
1489 LineInsertEx(ALineIndex, AString, FEndings, AWithEvent);
1490 end;
1491
1492 procedure TATStrings.LineInsertStrings(ALineIndex: integer; ABlock: TATStrings; AWithFinalEol: boolean);
1493 //AWithFinalEol:
1494 // True to insert whole lines;
1495 // False to insert whole lines except last + concat last item to existing line
1496 var
1497 Item: TATStringItem;
1498 Str: atString;
1499 NCount, i: integer;
1500 begin
1501 NCount:= ABlock.Count;
1502 if NCount=0 then exit;
1503 if not AWithFinalEol then Dec(NCount);
1504
1505 UpdateModified;
1506
1507 if NCount>0 then
1508 begin
1509 for i:= 0 to NCount-1 do
1510 begin
1511 AddUndoItem(aeaInsert, ALineIndex+i, '', cEndNone, cLineStateNone, FCommandCode);
1512
1513 Item.Init(
1514 ABlock.GetLine(i),
1515 Endings
1516 );
1517 FList.Insert(ALineIndex+i, @Item);
1518 FillChar(Item, SizeOf(Item), 0);
1519 end;
1520
1521 DoEventLog(ALineIndex);
1522 DoEventChange(cLineChangeAdded, ALineIndex, NCount);
1523 end;
1524
1525 //insert last item specially, if no eol
1526 if not AWithFinalEol then
1527 begin
1528 i:= ALineIndex+ABlock.Count-1;
1529 Str:= ABlock.Lines[ABlock.Count-1];
1530 if IsIndexValid(i) then
1531 Lines[i]:= Str+Lines[i]
1532 else
1533 LineAdd(Str);
1534 end;
1535 end;
1536
1537
IsIndexValidnull1538 function TATStrings.IsIndexValid(N: integer): boolean; inline;
1539 begin
1540 Result:= (N>=0) and (N<FList.Count);
1541 end;
1542
Countnull1543 function TATStrings.Count: integer; inline;
1544 begin
1545 Result:= FList.Count;
1546 end;
1547
1548 procedure TATStrings.LineDelete(ALineIndex: integer; AForceLast: boolean = true;
1549 AWithEvent: boolean=true; AWithUndo: boolean=true);
1550 var
1551 Item: PATStringItem;
1552 begin
1553 if FReadOnly then Exit;
1554
1555 if IsIndexValid(ALineIndex) then
1556 begin
1557 Item:= FList.GetItem(ALineIndex);
1558
1559 UpdateModified;
1560 if AWithUndo then
1561 AddUndoItem(aeaDelete, ALineIndex, Item^.Line, Item^.LineEnds, Item^.LineState, FCommandCode);
1562
1563 if AWithEvent then
1564 begin
1565 DoEventLog(ALineIndex);
1566 DoEventChange(cLineChangeDeleted, ALineIndex, 1);
1567 end;
1568
1569 FList.Delete(ALineIndex);
1570 end;
1571 //else
1572 // raise Exception.Create('Invalid Delete index: '+IntToStr(ALineIndex));
1573
1574 if AForceLast then
1575 ActionAddFakeLineIfNeeded;
1576 end;
1577
1578 procedure TATStrings.LineMove(AIndexFrom, AIndexTo: integer; AWithUndo: boolean=true);
1579 var
1580 ItemFrom, ItemTo: PATStringItem;
1581 NLineMin: integer;
1582 begin
1583 UpdateModified;
1584
1585 if AWithUndo then
1586 begin
1587 ItemFrom:= GetItemPtr(AIndexFrom);
1588 ItemTo:= GetItemPtr(AIndexTo);
1589
1590 AddUndoItem(aeaDelete, AIndexFrom, ItemFrom^.Line, ItemFrom^.LineEnds, ItemFrom^.LineState, FCommandCode);
1591 AddUndoItem(aeaInsert, AIndexTo, ItemTo^.Line, ItemTo^.LineEnds, ItemTo^.LineState, FCommandCode);
1592 end;
1593
1594 FList.Move(AIndexFrom, AIndexTo);
1595
1596 LinesState[AIndexFrom]:= cLineStateChanged;
1597 if LinesEnds[AIndexFrom]=cEndNone then
1598 LinesEnds[AIndexFrom]:= Endings;
1599 if LinesEnds[AIndexTo]=cEndNone then
1600 LinesEnds[AIndexTo]:= Endings;
1601
1602 ActionAddFakeLineIfNeeded;
1603 Modified:= true;
1604
1605 NLineMin:= Min(AIndexFrom, AIndexTo);
1606 DoEventLog(NLineMin);
1607 end;
1608
LineSubnull1609 function TATStrings.LineSub(ALineIndex, APosFrom, ALen: integer): atString;
1610 var
1611 Item: PATStringItem;
1612 begin
1613 if ALen=0 then exit('');
1614 Item:= GetItemPtr(ALineIndex);
1615 Result:= Item^.LineSub(APosFrom, ALen);
1616 end;
1617
LineCharAtnull1618 function TATStrings.LineCharAt(ALineIndex, ACharIndex: integer): WideChar;
1619 begin
1620 Result:= GetItemPtr(ALineIndex)^.CharAt(ACharIndex);
1621 end;
1622
1623 procedure TATStrings.GetIndentProp(ALineIndex: integer; out
1624 ACharCount: integer; out AKind: TATLineIndentKind);
1625 begin
1626 GetItemPtr(ALineIndex)^.GetIndentProp(ACharCount, AKind);
1627 end;
1628
LineLenWithoutSpacenull1629 function TATStrings.LineLenWithoutSpace(ALineIndex: integer): integer;
1630 begin
1631 Result:= GetItemPtr(ALineIndex)^.CharLenWithoutSpace;
1632 end;
1633
ColumnPosToCharPosnull1634 function TATStrings.ColumnPosToCharPos(AIndex: integer; AX: integer; ATabHelper: TATStringTabHelper): integer;
1635 var
1636 SLine: atString;
1637 begin
1638 if not LinesHasTab[AIndex] then exit(AX);
1639
1640 //optimized for huge lines
1641 SLine:= LineSub(AIndex, 1, AX+ATabHelper.TabSize);
1642 Result:= ATabHelper.ColumnPosToCharPos(AIndex, SLine, AX);
1643 end;
1644
CharPosToColumnPosnull1645 function TATStrings.CharPosToColumnPos(AIndex: integer; AX: integer; ATabHelper: TATStringTabHelper): integer;
1646 var
1647 SLine: atString;
1648 begin
1649 if not LinesHasTab[AIndex] then exit(AX);
1650
1651 //optimized for huge lines
1652 SLine:= LineSub(AIndex, 1, AX+ATabHelper.TabSize);
1653 Result:= ATabHelper.CharPosToColumnPos(AIndex, SLine, AX);
1654 end;
1655
GetItemPtrnull1656 function TATStrings.GetItemPtr(AIndex: integer): PATStringItem;
1657 begin
1658 Result:= FList.GetItem(AIndex);
1659 end;
1660
1661 procedure TATStrings.Clear(AWithEvent: boolean);
1662 begin
1663 ClearUndo(FUndoList.Locked);
1664
1665 if AWithEvent then
1666 begin
1667 DoEventLog(0);
1668 DoEventChange(cLineChangeDeletedAll, -1, 1);
1669 end;
1670
1671 FList.Clear;
1672 end;
1673
1674 procedure TATStrings.ClearLineStates(ASaved: boolean);
1675 var
1676 Item: PATStringItem;
1677 i: integer;
1678 begin
1679 for i:= 0 to Count-1 do
1680 begin
1681 Item:= FList.GetItem(i);
1682 if ASaved then
1683 Item^.LineStateToSaved
1684 else
1685 Item^.LineStateToNone;
1686 end;
1687 end;
1688
1689 procedure TATStrings.SetModified(AValue: boolean);
1690 begin
1691 FModified:= AValue;
1692 if FModified then
1693 begin
1694 end
1695 else
1696 FUndoList.AddUnmodifiedMark;
1697 end;
1698
1699 procedure TATStrings.SetRedoAsString(const AValue: string);
1700 begin
1701 FRedoList.AsString:= AValue;
1702 end;
1703
1704 procedure TATStrings.SetUndoAsString(const AValue: string);
1705 begin
1706 FUndoList.AsString:= AValue;
1707 end;
1708
1709 procedure TATStrings.SetUndoLimit(AValue: integer);
1710 begin
1711 if Assigned(FUndoList) then
1712 FUndoList.MaxCount:= AValue;
1713 end;
1714
1715 procedure TATStrings.DoDetectEndings;
1716 begin
1717 if not IsIndexValid(0) then Exit;
1718 FEndings:= LinesEnds[0]; //no range-chk
1719 if FEndings=cEndNone then
1720 FEndings:= cEndWin;
1721 end;
1722
TextSubstringnull1723 function TATStrings.TextSubstring(AX1, AY1, AX2, AY2: integer;
1724 const AEolString: UnicodeString = #10): atString;
1725 var
1726 i: integer;
1727 begin
1728 Result:= '';
1729 if AY1>AY2 then Exit;
1730 if not IsIndexValid(AY1) then Exit;
1731 if not IsIndexValid(AY2) then Exit;
1732
1733 if AY1=AY2 then
1734 Exit(LineSub(AY1, AX1+1, AX2-AX1));
1735
1736 //first line
1737 Result:= LineSub(AY1, AX1+1, MaxInt);
1738
1739 //middle
1740 for i:= AY1+1 to AY2-1 do
1741 Result+= AEolString+Lines[i];
1742
1743 //last line
1744 Result+= AEolString+LineSub(AY2, 1, AX2);
1745 end;
1746
TextSubstringLengthnull1747 function TATStrings.TextSubstringLength(AX1, AY1, AX2, AY2: integer;
1748 const AEolString: UnicodeString = #10): integer;
1749 var
1750 NLen, NLenEol, i: integer;
1751 begin
1752 Result:= 0;
1753 if AY1>AY2 then Exit;
1754 if not IsIndexValid(AY1) then Exit;
1755 if not IsIndexValid(AY2) then Exit;
1756
1757 NLenEol:= Length(AEolString);
1758
1759 if AY1=AY2 then
1760 begin
1761 NLen:= LinesLen[AY1];
1762 Exit(Max(0, Min(NLen, AX2)-AX1));
1763 end;
1764
1765 //first line
1766 NLen:= LinesLen[AY1];
1767 Result:= Max(0, NLen-AX1);
1768
1769 //middle
1770 for i:= AY1+1 to AY2-1 do
1771 begin
1772 NLen:= LinesLen[i];
1773 Result+= NLen+NLenEol;
1774 end;
1775
1776 //last line
1777 NLen:= LinesLen[AY2];
1778 Result+= Min(NLen, AX2)+NLenEol;
1779 end;
1780
1781 procedure TATStrings.SetGroupMark;
1782 begin
1783 if Assigned(FUndoList) then
1784 FUndoList.SoftMark:= true;
1785 end;
1786
1787 procedure TATStrings.BeginUndoGroup;
1788 begin
1789 Inc(FUndoGroupCounter);
1790 if Assigned(FUndoList) then
1791 begin
1792 if FUndoList.Locked then exit;
1793 //softmark if not-nested call
1794 if FUndoGroupCounter=1 then
1795 FUndoList.SoftMark:= true;
1796 //hardmark always
1797 FUndoList.HardMark:= true;
1798 end;
1799 end;
1800
1801 procedure TATStrings.EndUndoGroup;
1802 begin
1803 if FUndoGroupCounter>0 then
1804 Dec(FUndoGroupCounter)
1805 else
1806 FUndoGroupCounter:= 0;
1807
1808 if FUndoGroupCounter=0 then
1809 if Assigned(FUndoList) then
1810 begin
1811 if FUndoList.Locked then exit;
1812 FUndoList.HardMark:= false;
1813 end;
1814 end;
1815
1816 procedure TATStrings.UndoSingle(ACurList: TATUndoList;
1817 out ASoftMarked, AHardMarked, AHardMarkedNext, AUnmodifiedNext: boolean;
1818 out ACommandCode: integer; out ATickCount: QWord);
1819 var
1820 CurItem, PrevItem: TATUndoItem;
1821 CurAction: TATEditAction;
1822 CurText: atString;
1823 CurIndex: integer;
1824 CurLineEnd: TATLineEnds;
1825 CurLineState: TATLineState;
1826 CurCaretsArray: TATPointArray;
1827 CurMarkersArray: TATInt64Array;
1828 OtherList: TATUndoList;
1829 NCount: integer;
1830 NEventX, NEventY: integer;
1831 bWithoutPause: boolean;
1832 bEnableEventBefore,
1833 bEnableEventAfter: boolean;
1834 begin
1835 ASoftMarked:= true;
1836 AHardMarked:= false;
1837 AHardMarkedNext:= false;
1838 AUnmodifiedNext:= false;
1839 if FReadOnly then Exit;
1840 if ACurList=nil then Exit;
1841
1842 CurItem:= ACurList.Last;
1843 if CurItem=nil then Exit;
1844 CurAction:= CurItem.ItemAction;
1845 CurIndex:= CurItem.ItemIndex;
1846
1847 //CurIndex=Count is allowed, CudaText issue #3258
1848 if (CurIndex<0) or (CurIndex>Count) then exit;
1849
1850 CurText:= CurItem.ItemText;
1851 CurLineEnd:= CurItem.ItemEnd;
1852 CurLineState:= CurItem.ItemLineState;
1853 CurCaretsArray:= CurItem.ItemCarets;
1854 CurMarkersArray:= CurItem.ItemMarkers;
1855 ACommandCode:= CurItem.ItemCommandCode;
1856 ASoftMarked:= CurItem.ItemSoftMark;
1857 AHardMarked:= CurItem.ItemHardMark;
1858 ATickCount:= CurItem.ItemTickCount;
1859 NCount:= ACurList.Count;
1860 bWithoutPause:= IsCommandToUndoInOneStep(ACommandCode);
1861
1862 //note: do not break this issue https://github.com/Alexey-T/CudaText/issues/2677
1863 if NCount>=2 then
1864 begin
1865 PrevItem:= ACurList[NCount-2];
1866 AHardMarkedNext:= PrevItem.ItemHardMark;
1867 AUnmodifiedNext:= PrevItem.ItemAction=aeaClearModified;
1868 end;
1869
1870 //don't undo if one item left: unmodified-mark
1871 if ACurList.IsEmpty then exit;
1872
1873 CurItem:= nil;
1874 ACurList.DeleteLast;
1875 ACurList.Locked:= true;
1876
1877 if ACurList=FUndoList then
1878 OtherList:= FRedoList
1879 else
1880 OtherList:= FUndoList;
1881
1882 case CurAction of
1883 aeaChange,
1884 aeaDelete,
1885 aeaInsert:
1886 begin
1887 bEnableEventAfter:= ASoftMarked or AHardMarked;
1888 end;
1889 aeaCaretJump:
1890 begin
1891 bEnableEventAfter:= true;
1892 end;
1893 else
1894 begin
1895 bEnableEventAfter:= false;
1896 end;
1897 end;
1898
1899 if Length(CurCaretsArray)>0 then
1900 begin
1901 NEventX:= CurCaretsArray[0].X;
1902 NEventY:= CurCaretsArray[0].Y; //CurIndex is 0 for CaretJump
1903 end
1904 else
1905 begin
1906 NEventX:= -1;
1907 NEventY:= -1;
1908 end;
1909
1910 bEnableEventBefore:= (NEventY>=0) and (NEventY<>FLastUndoY);
1911 FLastUndoY:= NEventY;
1912
1913 //fixing issue #3427, flag nnnAfter must be false if nnnBefore=false
1914 if not bEnableEventBefore then
1915 bEnableEventAfter:= false;
1916
1917 if bWithoutPause then
1918 begin
1919 bEnableEventBefore:= false;
1920 bEnableEventAfter:= false;
1921 end;
1922
1923 if bEnableEventBefore then
1924 if Assigned(FOnUndoBefore) then
1925 FOnUndoBefore(Self, NEventX, NEventY);
1926
1927 try
1928 case CurAction of
1929 aeaChange:
1930 begin
1931 if IsIndexValid(CurIndex) then
1932 begin
1933 Lines[CurIndex]:= CurText;
1934 LinesState[CurIndex]:= CurLineState;
1935 end;
1936 end;
1937
1938 aeaChangeEol:
1939 begin
1940 if IsIndexValid(CurIndex) then
1941 begin
1942 LinesEnds[CurIndex]:= CurLineEnd;
1943 LinesState[CurIndex]:= CurLineState;
1944 end;
1945 end;
1946
1947 aeaInsert:
1948 begin
1949 if IsIndexValid(CurIndex) then
1950 LineDelete(CurIndex);
1951 end;
1952
1953 aeaDelete:
1954 begin
1955 if CurIndex>=Count then
1956 LineAddRaw(CurText, CurLineEnd)
1957 else
1958 LineInsertRaw(CurIndex, CurText, CurLineEnd);
1959 if IsIndexValid(CurIndex) then
1960 LinesState[CurIndex]:= CurLineState;
1961 end;
1962
1963 aeaClearModified:
1964 begin
1965 OtherList.AddUnmodifiedMark;
1966 exit;
1967 end;
1968
1969 aeaCaretJump:
1970 begin
1971 OtherList.Add(CurAction, 0, '', cEndNone, cLineStateNone, CurCaretsArray, CurMarkersArray, ACommandCode);
1972 end;
1973 end;
1974
1975 if Length(CurCaretsArray)>0 then
1976 SetCaretsArray(CurCaretsArray);
1977 SetMarkersArray(CurMarkersArray);
1978
1979 if bEnableEventAfter then
1980 if Assigned(FOnUndoAfter) then
1981 FOnUndoAfter(Self, NEventX, NEventY);
1982
1983 ActionDeleteDupFakeLines;
1984 finally
1985 ACurList.Locked:= false;
1986 end;
1987 end;
1988
DebugTextnull1989 function TATStrings.DebugText: string;
1990 var
1991 Item: PATStringItem;
1992 i: integer;
1993 begin
1994 Result:= '';
1995 for i:= 0 to Min(20, Count-1) do
1996 begin
1997 Item:= FList.GetItem(i);
1998 Result:= Result+Format('[%d] "%s" <%s>', [
1999 i,
2000 Item^.Line,
2001 cLineEndNiceNames[Item^.LineEnds]
2002 ])+#10;
2003 end;
2004 end;
2005
GetCaretsArraynull2006 function TATStrings.GetCaretsArray: TATPointArray;
2007 begin
2008 if Assigned(FOnGetCaretsArray) then
2009 Result:= FOnGetCaretsArray()
2010 else
2011 SetLength(Result, 0);
2012 end;
2013
TATStrings.GetMarkersArraynull2014 function TATStrings.GetMarkersArray: TATInt64Array;
2015 begin
2016 if Assigned(FOnGetMarkersArray) then
2017 Result:= FOnGetMarkersArray()
2018 else
2019 SetLength(Result, 0);
2020 end;
2021
2022 procedure TATStrings.SetCaretsArray(const L: TATPointArray);
2023 begin
2024 if Assigned(FOnSetCaretsArray) then
2025 FOnSetCaretsArray(L);
2026 end;
2027
2028 procedure TATStrings.SetMarkersArray(const L: TATInt64Array);
2029 begin
2030 if Assigned(FOnSetMarkersArray) then
2031 FOnSetMarkersArray(L);
2032 end;
2033
2034 procedure TATStrings.UpdateModified;
2035 begin
2036 FModified:= true;
2037 FModifiedRecent:= true;
2038 Inc(FModifiedVersion);
2039 end;
2040
2041 procedure TATStrings.AddUndoItem(AAction: TATEditAction; AIndex: integer;
2042 const AText: atString; AEnd: TATLineEnds; ALineState: TATLineState;
2043 ACommandCode: integer);
2044 var
2045 CurList: TATUndoList;
2046 begin
2047 if FUndoList=nil then exit;
2048 if FRedoList=nil then exit;
2049
2050 if not FUndoList.Locked then
2051 CurList:= FUndoList
2052 else
2053 if not FRedoList.Locked then
2054 CurList:= FRedoList
2055 else
2056 exit;
2057
2058 //handle CaretJump:
2059 //if last item was also CaretJump, delete the last item (don't make huge list on many clicks)
2060 if AAction=aeaCaretJump then
2061 begin
2062 if (CurList.Count>0) and (CurList.Last.ItemAction=AAction) then
2063 CurList.DeleteLast;
2064 end
2065 else
2066 begin
2067 if not FUndoList.Locked and not FRedoList.Locked then
2068 FRedoList.Clear;
2069 AddUpdatesAction(AIndex, AAction);
2070 end;
2071
2072 CurList.Add(AAction, AIndex, AText, AEnd, ALineState, GetCaretsArray, GetMarkersArray, ACommandCode);
2073 end;
2074
2075 procedure TATStrings.UndoOrRedo(AUndo: boolean; AGrouped: boolean);
2076 var
2077 List, ListOther: TATUndoList;
2078 LastItem: TATUndoItem;
2079 bSoftMarked,
2080 bHardMarked,
2081 bHardMarkedNext,
2082 bMarkedUnmodified: boolean;
2083 NCommandCode: integer;
2084 NTickCount: QWord;
2085 begin
2086 if not Assigned(FUndoList) then Exit;
2087 if not Assigned(FRedoList) then Exit;
2088
2089 if AUndo then
2090 begin
2091 List:= FUndoList;
2092 ListOther:= FRedoList;
2093 end
2094 else
2095 begin
2096 List:= FRedoList;
2097 ListOther:= FUndoList;
2098 end;
2099
2100 //ShowMessage('Undo list:'#10+FUndolist.DebugText);
2101
2102 {
2103 solve CudaText #3261:
2104 - Type something on line e.g. 100
2105 - Press Ctrl+F and find any text on line e.g. 25
2106 - Go to main window and try Undo/Redo
2107 it undoes/redoes editing on the line 100, but moves the caret to line 25.
2108 Usually first time it doesn't jump (as expected) but after repeating steps 2 and 3 it's starting to jump again.
2109 }
2110 if Length(CaretsAfterLastEdition)>0 then
2111 SetCaretsArray(CaretsAfterLastEdition);
2112
2113 {
2114 solve CudaText #3268
2115 - Type something in line 100
2116 - Scroll screen to line 1 (to hide line 100 from the screen)
2117 - Perform undo
2118 In my case I see blink but don't see the change itself on the line 100.
2119 }
2120 FLastUndoY:= -1;
2121
2122 repeat
2123 //better to have this, e.g. for Undo after Ctrl+A, Del
2124 //we can have not fixed 'chain reaction of pauses'
2125 if Application.Terminated then Break;
2126
2127 if List.Count=0 then Break;
2128 if List.IsEmpty then Break;
2129
2130 UndoSingle(List, bSoftMarked, bHardMarked, bHardMarkedNext, bMarkedUnmodified, NCommandCode, NTickCount);
2131
2132 //handle unmodified
2133 //don't clear FModified if List.IsEmpty! http://synwrite.sourceforge.net/forums/viewtopic.php?f=5&t=2504
2134 if bMarkedUnmodified then
2135 FModified:= false;
2136
2137 //apply Hardmark to ListOther
2138 if bHardMarked then
2139 if ListOther.Count>0 then
2140 begin
2141 ListOther.Last.ItemHardMark:= bHardMarked;
2142 //ListOther.Last.ItemSoftMark:= ?? //for redo needed Softmark too but don't know how
2143 end;
2144
2145 if bHardMarked and bHardMarkedNext and not bSoftMarked then
2146 Continue;
2147 if not AGrouped then
2148 Break;
2149
2150 //make commands with non-zero ItemCommandCode grouped (ie 'move lines up/down')
2151 if NCommandCode<>0 then
2152 if List.Count>0 then
2153 begin
2154 LastItem:= List.Last;
2155 if LastItem.ItemCommandCode=NCommandCode then
2156 if Abs(Int64(LastItem.ItemTickCount)-Int64(NTickCount))<List.PauseForMakingGroup then
2157 Continue;
2158 end;
2159
2160 if bSoftMarked then
2161 Break;
2162 until false;
2163
2164 //apply SoftMark to ListOther
2165 if bSoftMarked and AGrouped then
2166 ListOther.SoftMark:= true;
2167
2168 //to fix this:
2169 // - new tab, make 5 lines "dd'
2170 // - caret at end of 1st line
2171 // - Shift+Alt+Down to make 5 carets column
2172 // - do fast: 'd', Undo, 'dd', Undo, 'd', Undo...
2173 // -> it gave return to single caret, but must return to multi-carets
2174 // https://github.com/Alexey-T/CudaText/issues/3274#issuecomment-810522418
2175 if bSoftMarked then
2176 List.SoftMark:= true;
2177 end;
2178
2179 procedure TATStrings.ClearUndo(ALocked: boolean = false);
2180 begin
2181 if Assigned(FUndoList) then
2182 begin
2183 FUndoList.Clear;
2184 FUndoList.Locked:= ALocked;
2185 end;
2186
2187 if Assigned(FRedoList) then
2188 begin
2189 FRedoList.Clear;
2190 FRedoList.Locked:= ALocked;
2191 end;
2192
2193 if Assigned(FListUpdates) then
2194 begin
2195 FListUpdates.Clear;
2196 FListUpdatesHard:= false;
2197 end;
2198 end;
2199
2200 procedure TATStrings.ClearLineStatesUpdated;
2201 var
2202 i: integer;
2203 begin
2204 for i:= 0 to FList.Count-1 do
2205 FList.GetItem(i)^.Ex.Updated:= false;
2206 end;
2207
2208 procedure TATStrings.ActionSaveLastEditionPos(AX: integer; AY: integer);
2209 var
2210 Ar: TATPointArray;
2211 begin
2212 ModifiedRecent:= false;
2213
2214 if (AX>=0) and (AY>=0) then
2215 begin
2216 //2 items per caret
2217 SetLength(Ar, 2);
2218 Ar[0].X:= AX;
2219 Ar[0].Y:= AY;
2220 Ar[1].X:= -1;
2221 Ar[1].Y:= -1;
2222 end
2223 else
2224 Ar:= GetCaretsArray;
2225
2226 if Length(Ar)>0 then
2227 CaretsAfterLastEdition:= Ar;
2228 end;
2229
2230 procedure TATStrings.ActionGotoLastEditionPos;
2231 begin
2232 if Length(CaretsAfterLastEdition)>0 then
2233 SetCaretsArray(CaretsAfterLastEdition);
2234 end;
2235
2236 procedure TATStrings.ActionDeleteDupFakeLines;
2237 begin
2238 while IsLastFakeLineUnneeded do
2239 LineDelete(Count-1, false, false, false);
2240 end;
2241
2242 procedure TATStrings.ActionDeleteAllBlanks;
2243 var
2244 i: integer;
2245 begin
2246 ClearUndo;
2247 ClearLineStates(false);
2248
2249 for i:= Count-1 downto 0 do
2250 if LinesBlank[i] then
2251 FList.Delete(i);
2252
2253 ActionAddFakeLineIfNeeded;
2254 ClearLineStates(false);
2255
2256 DoEventChange(cLineChangeDeletedAll, -1, 1);
2257 DoEventLog(0);
2258 end;
2259
2260 procedure TATStrings.ActionDeleteAdjacentBlanks;
2261 var
2262 i: integer;
2263 begin
2264 ClearUndo;
2265 ClearLineStates(false);
2266
2267 for i:= Count-1 downto 1{!} do
2268 if LinesBlank[i] and LinesBlank[i-1] then
2269 FList.Delete(i);
2270
2271 ActionAddFakeLineIfNeeded;
2272 ClearLineStates(false);
2273
2274 DoEventChange(cLineChangeDeletedAll, -1, 1);
2275 DoEventLog(0);
2276 end;
2277
2278 procedure TATStrings.ActionDeleteAdjacentDups;
2279 var
2280 i: integer;
2281 begin
2282 ClearUndo;
2283 ClearLineStates(false);
2284
2285 for i:= Count-1 downto 1{!} do
2286 if (LinesLen[i]=LinesLen[i-1]) and (Lines[i]=Lines[i-1]) then
2287 FList.Delete(i);
2288
2289 ActionAddFakeLineIfNeeded;
2290 ClearLineStates(false);
2291
2292 DoEventChange(cLineChangeDeletedAll, -1, 1);
2293 DoEventLog(0);
2294 end;
2295
2296 procedure TATStrings.ActionDeleteAllDups(AKeepBlanks: boolean);
2297 var
2298 i, j, NLen: integer;
2299 S: UnicodeString;
2300 begin
2301 ClearUndo;
2302 ClearLineStates(false);
2303
2304 for i:= Count-1 downto 1{!} do
2305 begin
2306 if AKeepBlanks then
2307 if LinesBlank[i] then Continue;
2308 S:= Lines[i];
2309 NLen:= Length(S);
2310 for j:= 0 to i-1 do
2311 if (NLen=LinesLen[j]) and (S=Lines[j]) then
2312 begin
2313 FList.Delete(i);
2314 Break
2315 end;
2316 end;
2317
2318 ActionAddFakeLineIfNeeded;
2319 ClearLineStates(false);
2320
2321 DoEventChange(cLineChangeDeletedAll, -1, 1);
2322 DoEventLog(0);
2323 end;
2324
2325
2326 procedure TATStrings.ActionReverseLines;
2327 var
2328 Cnt, i, mid: integer;
2329 begin
2330 ActionEnsureFinalEol;
2331 ActionDeleteFakeLine;
2332
2333 Cnt:= Count;
2334 if Cnt<2 then
2335 begin
2336 ActionAddFakeLineIfNeeded;
2337 exit;
2338 end;
2339
2340 ClearUndo;
2341 ClearLineStates(false);
2342
2343 mid:= Cnt div 2;
2344 if Odd(Cnt) then
2345 Inc(mid);
2346
2347 for i:= Cnt-1 downto mid do
2348 FList.Exchange(i, Cnt-1-i);
2349
2350 ActionAddFakeLineIfNeeded;
2351 ClearLineStates(false);
2352
2353 DoEventChange(cLineChangeDeletedAll, -1, 1);
2354 DoEventLog(0);
2355 end;
2356
2357 procedure TATStrings.ActionShuffleLines;
2358 var
2359 Cnt, i: integer;
2360 begin
2361 UpdateModified;
2362 ActionEnsureFinalEol;
2363 ActionDeleteFakeLine;
2364
2365 Cnt:= Count;
2366 if Cnt<2 then
2367 begin
2368 ActionAddFakeLineIfNeeded;
2369 exit;
2370 end;
2371
2372 ClearUndo;
2373 ClearLineStates(false);
2374
2375 // https://stackoverflow.com/a/14006825/6792690
2376 for i:= Cnt-1 downto 1 do
2377 FList.Exchange(i, Random(i+1));
2378
2379 ActionAddFakeLineIfNeeded;
2380 ClearLineStates(false);
2381
2382 DoEventChange(cLineChangeDeletedAll, -1, 1);
2383 DoEventLog(0);
2384 end;
2385
2386 procedure TATStrings.ActionAddJumpToUndo(constref ACaretsArray: TATPointArray);
2387 var
2388 Item: TATUndoItem;
2389 begin
2390 if FUndoList.Locked then exit;
2391 AddUndoItem(aeaCaretJump, 0, '', cEndNone, cLineStateNone, FCommandCode);
2392 Item:= FUndoList.Last;
2393 if Assigned(Item) then
2394 if Length(ACaretsArray)>0 then
2395 Item.ItemCarets:= ACaretsArray;
2396 end;
2397
2398
2399 procedure TATStrings.AddUpdatesAction(N: integer; AAction: TATEditAction);
2400 begin
2401 if not Assigned(FListUpdates) then Exit;
2402
2403 if AAction in [aeaDelete, aeaInsert] then
2404 begin
2405 FListUpdatesHard:= true;
2406 Exit
2407 end;
2408
2409 if FListUpdates.Count>cMaxUpdatesCountEasy then
2410 begin
2411 FListUpdatesHard:= true;
2412 Exit
2413 end;
2414
2415 with FListUpdates do
2416 if IndexOf(N)<0 then
2417 Add(N);
2418 end;
2419
2420 procedure TATStrings.DoOnChangeBlock(AX1, AY1, AX2, AY2: integer;
2421 AChange: TATBlockChangeKind; ABlock: TStringList);
2422 begin
2423 if Assigned(FOnChangeBlock) then
2424 FOnChangeBlock(Self,
2425 Point(AX1, AY1),
2426 Point(AX2, AY2),
2427 AChange,
2428 ABlock);
2429 end;
2430
ActionEnsureFinalEolnull2431 function TATStrings.ActionEnsureFinalEol: boolean;
2432 begin
2433 Result:= false;
2434 if IsLastLineFake then Exit;
2435 if Count>0 then
2436 begin
2437 if LinesEnds[Count-1]=cEndNone then
2438 begin
2439 LinesEnds[Count-1]:= Endings;
2440 ActionAddFakeLineIfNeeded;
2441 Result:= true;
2442 end;
2443 end;
2444 end;
2445
ActionTrimFinalEmptyLinesnull2446 function TATStrings.ActionTrimFinalEmptyLines: boolean;
2447 begin
2448 Result:= false;
2449 while (Count>1) and (Lines[Count-1]='') and (Lines[Count-2]='') do
2450 begin
2451 LineDelete(Count-2);
2452 Result:= true;
2453 end;
2454 end;
2455
ActionTrimSpacesnull2456 function TATStrings.ActionTrimSpaces(AMode: TATTrimSpaces): boolean;
2457 var
2458 i: integer;
2459 S1, S2: atString;
2460 begin
2461 Result:= false;
2462 FLastCommandChangedLines:= 0;
2463
2464 for i:= 0 to Count-1 do
2465 begin
2466 S1:= Lines[i];
2467 if S1='' then Continue;
2468
2469 case AMode of
2470 cTrimLeft:
2471 begin
2472 if not IsCharSpace(S1[1]) then
2473 Continue;
2474 S2:= STrimLeft(S1);
2475 end;
2476 cTrimRight:
2477 begin
2478 if not IsCharSpace(S1[Length(S1)]) then
2479 Continue;
2480 S2:= STrimRight(S1);
2481 end;
2482 cTrimAll:
2483 begin
2484 if not IsCharSpace(S1[1]) and not IsCharSpace(S1[Length(S1)]) then
2485 Continue;
2486 S2:= STrimAll(S1);
2487 end;
2488 end;
2489
2490 if S2<>S1 then
2491 begin
2492 Inc(FLastCommandChangedLines);
2493 Lines[i]:= S2;
2494 Result:= true;
2495 end;
2496 end;
2497 end;
2498
IsPosFoldednull2499 function TATStrings.IsPosFolded(AX, AY, AIndexClient: integer): boolean;
2500 var
2501 ValueFoldFrom: integer;
2502 begin
2503 Result:= false;
2504 if not IsIndexValid(AY) then Exit;
2505
2506 if LinesHidden[AY, AIndexClient] then
2507 Exit(true);
2508
2509 ValueFoldFrom:= LinesFoldFrom[AY, AIndexClient];
2510 if (ValueFoldFrom>0) and (AX>=ValueFoldFrom) then
2511 Exit(true);
2512 end;
2513
2514 procedure TATStrings.LineAddRaw_NoUndo(const S: string; AEnd: TATLineEnds);
2515 var
2516 Item: TATStringItem;
2517 begin
2518 Item.Init(S, AEnd);
2519 Item.Ex.State:= TATBits2(cLineStateAdded);
2520 FList.Add(@Item);
2521 FillChar(Item, SizeOf(Item), 0);
2522 end;
2523
2524 procedure TATStrings.LineAddRaw_NoUndo(const S: UnicodeString; AEnd: TATLineEnds);
2525 var
2526 Item: TATStringItem;
2527 begin
2528 Item.Init(S, AEnd);
2529 Item.Ex.State:= TATBits2(cLineStateAdded);
2530 FList.Add(@Item);
2531 FillChar(Item, SizeOf(Item), 0);
2532 end;
2533
2534 procedure TATStrings.DoEventLog(ALine: integer);
2535 begin
2536 if not FEnabledChangeEvents then exit;
2537
2538 if (EditingTopLine<0) or (ALine<EditingTopLine) then
2539 EditingTopLine:= ALine;
2540
2541 if Assigned(FOnChangeLog) then
2542 FOnChangeLog(Self, ALine);
2543 end;
2544
2545 procedure TATStrings.DoEventChange(AChange: TATLineChangeKind; ALineIndex, AItemCount: integer);
2546 begin
2547 if not FEnabledChangeEvents then exit;
2548
2549 FGaps.Update(AChange, ALineIndex, AItemCount);
2550
2551 if FEnabledBookmarksUpdate then
2552 begin
2553 FBookmarks.Update(AChange, ALineIndex, AItemCount, Count);
2554 FBookmarks2.Update(AChange, ALineIndex, AItemCount, Count);
2555 end;
2556
2557 if Assigned(FGutterDecor1) then
2558 FGutterDecor1.Update(AChange, ALineIndex, AItemCount, Count);
2559 if Assigned(FGutterDecor2) then
2560 FGutterDecor2.Update(AChange, ALineIndex, AItemCount, Count);
2561
2562 if Assigned(FOnChangeEx) then
2563 FOnChangeEx(Self, AChange, ALineIndex, AItemCount);
2564 end;
2565
2566 procedure TATStrings.ClearSeparators;
2567 var
2568 Item: PATStringItem;
2569 i: integer;
2570 begin
2571 for i:= 0 to Count-1 do
2572 begin
2573 Item:= FList.GetItem(i);
2574 Item^.Ex.Sep:= TATBits2(cLineSepNone);
2575 end;
2576 end;
2577
Compare_Ascnull2578 function TATStrings.Compare_Asc(Key1, Key2: Pointer): Integer;
2579 var
2580 P1, P2: PATStringItem;
2581 begin
2582 P1:= PATStringItem(Key1);
2583 P2:= PATStringItem(Key2);
2584 if P1^.Ex.Wide or P2^.Ex.Wide then
2585 Result:= UnicodeCompareStr(P1^.Line, P2^.Line)
2586 else
2587 Result:= CompareStr(P1^.Buf, P2^.Buf);
2588 end;
2589
Compare_AscNoCasenull2590 function TATStrings.Compare_AscNoCase(Key1, Key2: Pointer): Integer;
2591 var
2592 P1, P2: PATStringItem;
2593 begin
2594 P1:= PATStringItem(Key1);
2595 P2:= PATStringItem(Key2);
2596 if P1^.Ex.Wide or P2^.Ex.Wide then
2597 Result:= UnicodeCompareText(P1^.Line, P2^.Line)
2598 else
2599 Result:= CompareText(P1^.Buf, P2^.Buf);
2600 end;
2601
Compare_Descnull2602 function TATStrings.Compare_Desc(Key1, Key2: Pointer): Integer;
2603 begin
2604 Result:= -Compare_Asc(Key1, Key2);
2605 end;
2606
Compare_DescNoCasenull2607 function TATStrings.Compare_DescNoCase(Key1, Key2: Pointer): Integer;
2608 begin
2609 Result:= -Compare_AscNoCase(Key1, Key2);
2610 end;
2611
2612
2613 procedure TATStrings.ActionSort(AAction: TATStringsSortAction; AFrom, ATo: integer);
2614 var
2615 Func: TFPSListCompareFunc;
2616 i: integer;
2617 begin
2618 ActionEnsureFinalEol;
2619 ActionDeleteFakeLine;
2620
2621 if Count<2 then
2622 begin
2623 ActionAddFakeLineIfNeeded;
2624 exit;
2625 end;
2626
2627 case AAction of
2628 cSortActionAsc:
2629 Func:= @Compare_Asc;
2630 cSortActionAscNoCase:
2631 Func:= @Compare_AscNoCase;
2632 cSortActionDesc:
2633 Func:= @Compare_Desc;
2634 cSortActionDescNoCase:
2635 Func:= @Compare_DescNoCase;
2636 end;
2637
2638 ClearUndo;
2639 ClearLineStates(false);
2640
2641 for i:= Count-1 downto 0 do
2642 if LinesLen[i]=0 then
2643 FList.Delete(i);
2644
2645 if AFrom<0 then
2646 FList.Sort(Func)
2647 else
2648 FList.SortRange(AFrom, ATo, Func);
2649
2650 ActionAddFakeLineIfNeeded;
2651 ClearLineStates(false);
2652
2653 //this clears all bookmarks, ranges, decors - it's ok
2654 DoEventChange(cLineChangeDeletedAll, -1, 1);
2655 DoEventLog(0);
2656 end;
2657
2658
2659 {$I atstrings_editing.inc}
2660 {$I atstrings_load.inc}
2661 {$I atstrings_save.inc}
2662
2663 end.
2664
2665