unit TestSynBeautifier; {$mode objfpc}{$H+} interface uses Classes, SysUtils, testregistry, TestBase, TestHighlightPas, SynEdit, SynEditTextTrimmer, SynEditKeyCmds, SynBeautifier, SynEditTypes, SynBeautifierPascal, LCLType, LCLProc; type TCallBackData = record LinePos: Integer; Indent: Integer; // Indent in spaces (Logical = Physical) RelativeToLinePos: Integer; // Intend specifies +/- offset from intend on RTLine // 0: for absolute indent IndentChars: String ; // use the following string to indent; maybe empty, single char, or string IndentCharsFromLinePos: Integer; // Line for tab/space mix; set to -1 if unknown end; { TTestSynBeautifier } TTestSynBeautifier = class(TTestBaseHighlighterPas) protected procedure TestRedoUndo(Name: String; Text: Array of String; X, Y: Integer; Data: Array of const; SelX: Integer = -1; SelY: Integer = -1); protected FGetIndentCallBackResult: Boolean; FCallBackData: Array of TCallBackData; FGotLogCaret, FGotOldLogCaret: TPoint; FGotFirstLinePos, FGotLastLinePos: Integer; FGotReason: TSynEditorCommand; function GetIndentCallBack( Sender: TObject; // the beautifier {%H-}Editor: TObject; // the synedit LogCaret, OldLogCaret: TPoint; FirstLinePos, LastLinePos: Integer; Reason: TSynEditorCommand; // what caused the evnt SetIndentProc: TSynBeautifierSetIndentProc ): boolean; published (* Test includes: - no indent if switched off - indent after VK_RETURN - indent after CTRL-VK_N (TODO) * handling split indent (return in the middle of the leading space; next line is already part indented) * VK_Return on pos=1, keeps indent. line without indent will *not* be indented * with/without trim spaces * undo/redo/group-undo * correct indent from tabs (include split indent) * correct indent from several lines above (skip empty lines to find indent) VK_RETURN only * indent modes: sbitSpace, sbitCopySpaceTab, sbitPositionCaret *) procedure DefaultIndent; (* - no un-indent if switched off - un-indent after VK_BACK (if at end of leading whitespace) - no un-indent after VK_BACK (if not at end of leading whitespace) - act on empty line (only leading whitespace; no chars follow) (with/without trim space) - unindent after empty line (find indent from lines above) - handle tabs, in leading whitespace (TODO) - no unindent, if selection (none persistent) Todo: decide on none-overwritable block - TODO: fix group undo *) procedure DefaultUnIndent; procedure IndentCallBack; procedure IndentPasComment; end; implementation { TTestSynBeautifier } var SkipGroupUndo: Boolean; procedure TTestSynBeautifier.TestRedoUndo(Name: String; Text: array of String; X, Y: Integer; Data: array of const; SelX: Integer; SelY: Integer); function data2txt(n: Integer): String; begin if n < 0 then exit(LinesToText(Text)); Result := ''; case data[n].VType of vtString: Result := data[n].VString^; vtAnsiString: Result := AnsiString(data[n].VAnsiString); vtChar: Result := data[n].VChar; end; end; var i: integer; st: TShiftState; begin PushBaseName(Name + '(no group-undo)'); SetLines(Text); if (SelX >= 0) then SetCaretAndSel(SelX, SelY, X, Y) else SetCaret(X, Y); SynEdit.Options := SynEdit.Options - [eoGroupUndo]; for i := 0 to length(data) div 4 - 1 do begin st := []; if data[i*4+0].vinteger in [VK_A..VK_Z] then st := [ssCtrl]; DoKeyPress(data[i*4+0].vinteger, st); TestIsCaret('Key #'+IntToStr(i), data[i*4+1].vinteger, data[i*4+2].vinteger); TestIsFullText('Key #'+IntToStr(i), data2txt(i*4+3)); end; for i := length(data) div 4 - 2 downto -1 do begin SynEdit.Undo; if i >= 0 then TestIsCaret('Undo #'+IntToStr(i+1), data[i*4+1].vinteger, data[i*4+2].vinteger) else TestIsCaret('Undo #'+IntToStr(i+1), x, y); TestIsFullText('Undo #'+IntToStr(i+1), data2txt(i*4+3)); end; for i := 0 to length(data) div 4 - 1 do begin SynEdit.Redo; TestIsCaret('Redo #'+IntToStr(i), data[i*4+1].vinteger, data[i*4+2].vinteger); TestIsFullText('Redo #'+IntToStr(i), data2txt(i*4+3)); end; for i := length(data) div 4 - 2 downto -1 do begin SynEdit.Undo; if i >= 0 then TestIsCaret('2nd Undo Key #'+IntToStr(i+1), data[i*4+1].vinteger, data[i*4+2].vinteger) else TestIsCaret('2nd Undo Key #'+IntToStr(i+1), x, y); TestIsFullText('2nd Undo #'+IntToStr(i+1), data2txt(i*4+3)); end; if SkipGroupUndo then begin PopBaseName; exit; end; PopPushBaseName(Name + '(group-undo)'); SetLines(Text); if (SelX >= 0) then SetCaretAndSel(SelX, SelY, X, Y) else SetCaret(X, Y); SynEdit.Options := SynEdit.Options + [eoGroupUndo]; for i := 0 to length(data) div 4 - 1 do begin st := []; if data[i*4+0].vinteger in [VK_A..VK_Z] then st := [ssCtrl]; DoKeyPress(data[i*4+0].vinteger, st); TestIsCaret('Key #'+IntToStr(i), data[i*4+1].vinteger, data[i*4+2].vinteger); TestIsFullText('Key #'+IntToStr(i), data2txt(i*4+3)); end; SynEdit.Undo; TestIsCaret('Undo', x, y); TestIsFullText('Undo', Text); SynEdit.Redo; i := length(data) div 4 - 1; TestIsCaret('Redo', data[i*4+1].vinteger, data[i*4+2].vinteger); TestIsFullText('Redo', data2txt(i*4+3)); SynEdit.Undo; TestIsCaret('2nd Undo', x, y); TestIsFullText('2nd Undo', Text); PopBaseName; end; procedure TTestSynBeautifier.DefaultIndent; function TestText: TStringArray; begin SetLength(Result, 11); Result[0] := ' a;'; Result[1] := ' b'; Result[2] := ''; Result[3] := ' c'; Result[5] := ''; Result[6] := ''; Result[7] := #9+'x'; Result[8] := #32#9#32#32#9+'x'; // 8 indent ( 5 logical) Result[9] := ' c'; Result[10] := ''; end; function ExpText(rpl: array of const): String; begin Result := LinesToText(LinesReplace(TestText, rpl)) end; function TestText2: TStringArray; begin SetLength(Result, 6); Result[0] := ' a;'; Result[1] := 'b'; // not indent Result[2] := ' a;'; Result[3] := ''; Result[4] := 'b'; // not indent, after empty line Result[5] := ''; end; function ExpText2(rpl: array of const): String; begin Result := LinesToText(LinesReplace(TestText2, rpl)) end; begin SkipGroupUndo := False; ReCreateEdit; SynEdit.TabWidth := 4; SynEdit.TrimSpaceType := settMoveCaret; {%region ***** Test WITHOUT indent (no trim) *****} BaseTestName := 'DefaultIndent (no indent, no trim)'; SynEdit.Options := SynEdit.Options - [eoTrimTrailingSpaces, eoScrollPastEol, eoAutoIndent]; TestRedoUndo('Return EOL', TestText, 5,1, [ VK_RETURN, 1, 2, ExpText([ -2, '']) ] ); TestRedoUndo('Return empty line', TestText, 1,3, [ VK_RETURN, 1, 4, ExpText([ -4, '']) ] ); TestRedoUndo('Return after space', TestText, 5,2, [ VK_RETURN, 1, 3, ExpText([ 2, ' ', 'b']) ] ); // NOT trimmed TestRedoUndo('Return before space', TestText, 1,2, [ VK_RETURN, 1, 3, ExpText([ -2, '']) ] ); TestRedoUndo('Return mid space', TestText, 4,2, [ VK_RETURN, 1, 3, ExpText([ 2, ' ', ' b']) ] ); // NOT trimmed // VK_N will be ctrl-n (special detection in TestRedoUndo TestRedoUndo('CTRL-N EOL', TestText, 5,1, [ VK_N, 5, 1, ExpText([ -2, '']) ] ); {%endregion} {%region ***** Test WITHOUT indent (trim) *****} BaseTestName := 'DefaultIndent (trim, no indent)'; SynEdit.Options := SynEdit.Options - [eoScrollPastEol, eoAutoIndent] + [eoTrimTrailingSpaces]; TestRedoUndo('Return EOL', TestText, 5,1, [ VK_RETURN, 1, 2, ExpText([ -2, '']) ] ); TestRedoUndo('Return empty line', TestText, 1,3, [ VK_RETURN, 1, 4, ExpText([ -4, '']) ] ); TestRedoUndo('Return after space', TestText, 5,2, [ VK_RETURN, 1, 3, ExpText([ 2, '', 'b']) ] ); // trimmed TestRedoUndo('Return before space', TestText, 1,2, [ VK_RETURN, 1, 3, ExpText([ -2, '']) ] ); TestRedoUndo('Return mid space', TestText, 4,2, [ VK_RETURN, 1, 3, ExpText([ 2, '', ' b']) ] ); // trimmed {%endregion} {%region ***** Test WITHOUT trim *****} {%region sbitSpaces} BaseTestName := 'DefaultIndent (no trim)'; TSynBeautifier(SynEdit.Beautifier).IndentType := sbitSpace; SynEdit.Options := SynEdit.Options - [eoTrimTrailingSpaces, eoScrollPastEol, eoTabsToSpaces] + [eoAutoIndent]; TSynBeautifier(SynEdit.Beautifier).IndentType := sbitSpace; TestRedoUndo('Return EOL', TestText, 5,1, [ VK_RETURN, 3, 2, ExpText([ -2, ' ']) ] ); TestRedoUndo('Return EOL+EOT', TestText, 5,10,[ VK_RETURN, 4,11, ExpText([ -11,' ']) ] ); TestRedoUndo('Return empty line', TestText, 1,3, [ VK_RETURN, 5, 4, ExpText([ -4, ' ']) ] ); TestRedoUndo('Return after space', TestText, 5,2, [ VK_RETURN, 5, 3, ExpText([ -2, ' ']) ] ); // NOT trimmed TestRedoUndo('Return before space', TestText, 1,2, [ VK_RETURN, 1, 3, ExpText([ -2, '']) ] ); TestRedoUndo('Return mid space', TestText, 4,2, [ VK_RETURN, 4, 3, ExpText([ -2, ' ']) ] ); // NOT trimmed TestRedoUndo('Many Return EOL', TestText, 5,1, [ VK_RETURN, 3, 2, ExpText([ -2, ' ']), VK_RETURN, 3, 3, ExpText([ -2, ' ', ' ']), VK_RETURN, 3, 4, ExpText([ -2, ' ', ' ', ' ']) ]); TestRedoUndo('Many Return EOL+EOT', TestText, 5,10, [ VK_RETURN, 4,11, ExpText([ -11, ' ']), VK_RETURN, 4,12, ExpText([ -11, ' ', ' ']), VK_RETURN, 4,13, ExpText([ -11, ' ', ' ', ' ']) ]); TestRedoUndo('Many Return empty line', TestText, 1,3, [ VK_RETURN, 5, 4, ExpText([ -4, ' ']), VK_RETURN, 5, 5, ExpText([ -4, ' ', ' ']), VK_RETURN, 5, 6, ExpText([ -4, ' ', ' ', ' ']) ]); TestRedoUndo('Many Return after space', TestText, 5,2, [ VK_RETURN, 5, 3, ExpText([ -2, ' ']), // NOT trimmed VK_RETURN, 5, 4, ExpText([ -2, ' ', ' ']), // NOT trimmed VK_RETURN, 5, 5, ExpText([ -2, ' ', ' ', ' ']) // NOT trimmed ]); TestRedoUndo('Many Return before space', TestText, 1,2, [ VK_RETURN, 1, 3, ExpText([ -2, '']), VK_RETURN, 1, 4, ExpText([ -2, '', '']), VK_RETURN, 1, 5, ExpText([ -2, '', '', '']) ]); TestRedoUndo('Many Return mid space', TestText, 4,2, [ VK_RETURN, 4, 3, ExpText([ -2, ' ']), // NOT trimmed VK_RETURN, 4, 4, ExpText([ -2, ' ', ' ']), // NOT trimmed VK_RETURN, 4, 5, ExpText([ -2, ' ', ' ', ' ']) // NOT trimmed ]); TestRedoUndo('Return multi empty', TestText, 1,7, [ VK_RETURN, 4, 8, ExpText([ -8, ' ']) ] ); TestRedoUndo('Return at pos1 of unindeted', TestText2, 1,2, [ VK_RETURN, 1, 3, ExpText2([ -2, '']) ] ); TestRedoUndo('Return at pos1 of unindeted 2', TestText2, 1,5, [ VK_RETURN, 1, 6, ExpText2([ -5, '']) ] ); // caret pos are logical TestRedoUndo('Return after tab', TestText, 6,9, [ VK_RETURN, 9,10, ExpText([ 9, #32#9#32#32#9, ' x']) ] ); TestRedoUndo('Return before tab', TestText, 1,9, [ VK_RETURN, 1,10, ExpText([-9, '']) ] ); TestRedoUndo('Return middle tab 1', TestText, 3,9, [ VK_RETURN, 5,10, ExpText([ 9, #32#9, ' '+#32#32#9+'x']) ] ); TestRedoUndo('Return middle tab 2', TestText, 4,9, [ VK_RETURN, 6,10, ExpText([ 9, #32#9#32, ' '+#32#9+'x']) ] ); TestRedoUndo('Return tab EOL', TestText, 7,9, [ VK_RETURN, 9,10, ExpText([-10, ' ']) ] ); {%endregion} (* *** *) {%region sbitCopySpaceTab} PushBaseName('CopySpaceTab'); TSynBeautifier(SynEdit.Beautifier).IndentType := sbitCopySpaceTab; TestRedoUndo('Return after space', TestText, 5,2, [ VK_RETURN, 5, 3, ExpText([ -2, ' ']) ] ); // NOT trimmed TestRedoUndo('Return before space', TestText, 1,2, [ VK_RETURN, 1, 3, ExpText([ -2, '']) ] ); TestRedoUndo('Return mid space', TestText, 4,2, [ VK_RETURN, 4, 3, ExpText([ -2, ' ']) ] ); // NOT trimmed TestRedoUndo('Return after tab', TestText, 6,9, [ VK_RETURN, 6,10, ExpText([ 9, #32#9#32#32#9, #32#9#32#32#9+'x']) ] ); TestRedoUndo('Return before tab', TestText, 1,9, [ VK_RETURN, 1,10, ExpText([-9, '']) ] ); TestRedoUndo('Return middle tab 1', TestText, 3,9, [ VK_RETURN, 3,10, ExpText([ 9, #32#9, #32#9#32#32#9+'x']) ] ); TestRedoUndo('Return middle tab 2', TestText, 4,9, [ VK_RETURN, 4,10, ExpText([ 9, #32#9#32, #32#9#32#32#9+'x']) ] ); TestRedoUndo('Return tab EOL', TestText, 7,9, [ VK_RETURN, 6,10, ExpText([-10, #32#9#32#32#9]) ] ); TestRedoUndo('Many Return tab EOL', TestText, 7,9, [ VK_RETURN, 6,10, ExpText([-10, #32#9#32#32#9]), VK_RETURN, 6,11, ExpText([-10, #32#9#32#32#9, #32#9#32#32#9]), VK_RETURN, 6,12, ExpText([-10, #32#9#32#32#9, #32#9#32#32#9, #32#9#32#32#9]) ]); {%endregion} (* *** *) {%region sbitPosCaret} PopPushBaseName('PosCaret'); TSynBeautifier(SynEdit.Beautifier).IndentType := sbitPositionCaret; TestRedoUndo('Return after space', TestText, 5,2, [ VK_RETURN, 5, 3, ExpText([ -2, ' ']) ] ); // NOT trimmed TestRedoUndo('Return before space', TestText, 1,2, [ VK_RETURN, 1, 3, ExpText([ -2, '']) ] ); TestRedoUndo('Return mid space', TestText, 4,2, [ VK_RETURN, 4, 3, ExpText([ -2, ' ']) ] ); // NOT trimmed TestRedoUndo('Return eol', TestText, 6,2, [ VK_RETURN, 5, 3, ExpText([ -3, '']) ] ); // position only TestRedoUndo('Many Return eol', TestText, 6,2, [ // position only VK_RETURN, 5, 3, ExpText([ -3, '']), VK_RETURN, 5, 4, ExpText([ -3, '', '']), VK_RETURN, 5, 5, ExpText([ -3, '', '', '']) ]); TestRedoUndo('Return after tab', TestText, 6,9, [ VK_RETURN, 9,10, ExpText([ 9, #32#9#32#32#9, ' '+'x']) ] ); TestRedoUndo('Return before tab', TestText, 1,9, [ VK_RETURN, 1,10, ExpText([-9, '']) ] ); TestRedoUndo('Return middle tab 1', TestText, 3,9, [ VK_RETURN, 5,10, ExpText([ 9, #32#9, ' '+#32#32#9+'x']) ] ); TestRedoUndo('Return middle tab 2', TestText, 4,9, [ VK_RETURN, 6,10, ExpText([ 9, #32#9#32, ' '+#32#9+'x']) ] ); TestRedoUndo('Return tab EOL', TestText, 7,9, [ VK_RETURN, 9,10, ExpText([-10, '']) ] ); // pos only TestRedoUndo('Many Return tab EOL', TestText, 7,9, [ VK_RETURN, 9,10, ExpText([-10, '']), // pos only VK_RETURN, 9,11, ExpText([-10, '', '']), VK_RETURN, 9,12, ExpText([-10, '', '', '']) ]); {%endregion} {%region CTRL-N} PopPushBaseName('SpaceOnly'); // VK_N will be ctrl-n (special detection in TestRedoUndo TSynBeautifier(SynEdit.Beautifier).IndentType := sbitSpace; TestRedoUndo('CTRL-N EOL', TestText, 5,1, [ VK_N, 5, 1, ExpText([ -2, ' ']) ] ); TestRedoUndo('Many CTRL-N EOL', TestText, 5,1, [ VK_N, 5, 1, ExpText([ -2, ' ']), VK_N, 5, 1, ExpText([ -2, ' ', ' ']), VK_N, 5, 1, ExpText([ -2, ' ', ' ', ' ']) ]); TestRedoUndo('CTRL-N empty line', TestText, 1,3, [ VK_N, 1, 3, ExpText([ -4, ' ']) ] ); // TODO: should it? TestRedoUndo('CTRL-N pos1 unindeted',TestText2, 1,2, [ VK_N, 1, 2, ExpText2([ -2, '']) ] ); // TODO: must not TestRedoUndo('CTRL-N after space', TestText, 5,2, [ VK_N, 5, 2, ExpText([ -2, ' ']) ] ); // NOT trimmed TestRedoUndo('CTRL-N before space', TestText, 1,2, [ VK_N, 1, 2, ExpText([ -2, '']) ] ); // TODO: indents too much TestRedoUndo('CTRL-N mid space', TestText, 4,2, [ VK_N, 4, 2, ExpText([ -2, ' ']) ] ); // NOT trimmed PopPushBaseName('CopySpacetab'); TSynBeautifier(SynEdit.Beautifier).IndentType := sbitCopySpaceTab; TestRedoUndo('CTRL-N EOL', TestText, 5,1, [ VK_N, 5, 1, ExpText([ -2, ' ']) ] ); PopPushBaseName('PosOnly'); TSynBeautifier(SynEdit.Beautifier).IndentType := sbitPositionCaret; TestRedoUndo('CTRL-N EOL', TestText, 5,1, [ VK_N, 5, 1, ExpText([ -2, '']) ] ); {%endregion} {%endregion} {%region ***** Test WITH trim *****} {%region sbitSpaces} BaseTestName := 'DefaultIndent (trim)'; TSynBeautifier(SynEdit.Beautifier).IndentType := sbitSpace; SynEdit.Options := SynEdit.Options - [eoScrollPastEol] + [eoTrimTrailingSpaces, eoAutoIndent]; TSynBeautifier(SynEdit.Beautifier).IndentType := sbitSpace; //, sbitCopySpaceTab, sbitPositionCaret TestRedoUndo('Return EOL', TestText, 5,1, [ VK_RETURN, 3, 2, ExpText([ -2, ' ']) ] ); TestRedoUndo('Return EOL+EOT', TestText, 5,10,[ VK_RETURN, 4,11, ExpText([ -11,' ']) ] ); TestRedoUndo('Return empty line', TestText, 1,3, [ VK_RETURN, 5, 4, ExpText([ -4, ' ']) ] ); TestRedoUndo('Return after space', TestText, 5,2, [ VK_RETURN, 5, 3, ExpText([ -2, '']) ] ); // trimmed TestRedoUndo('Return before space', TestText, 1,2, [ VK_RETURN, 1, 3, ExpText([ -2, '']) ] ); TestRedoUndo('Return mid space', TestText, 4,2, [ VK_RETURN, 4, 3, ExpText([ -2, '']) ] ); // trimmed TestRedoUndo('Many Return EOL', TestText, 5,1, [ VK_RETURN, 3, 2, ExpText([ -2, ' ']), VK_RETURN, 3, 3, ExpText([ -2, '', ' ']), // trimmed VK_RETURN, 3, 4, ExpText([ -2, '', '', ' ']) ]); TestRedoUndo('Many Return EOL+EOT', TestText, 5,10, [ VK_RETURN, 4,11, ExpText([ -11, ' ']), VK_RETURN, 4,12, ExpText([ -11, '', ' ']), // trimmed VK_RETURN, 4,13, ExpText([ -11, '', '', ' ']) ]); TestRedoUndo('Many Return empty line', TestText, 1,3, [ VK_RETURN, 5, 4, ExpText([ -4, ' ']), VK_RETURN, 5, 5, ExpText([ -4, '', ' ']), // trimmed VK_RETURN, 5, 6, ExpText([ -4, '', '', ' ']) ]); TestRedoUndo('Many Return after space', TestText, 5,2, [ VK_RETURN, 5, 3, ExpText([ -2, '']), // trimmed VK_RETURN, 5, 4, ExpText([ -2, '', '']), // trimmed VK_RETURN, 5, 5, ExpText([ -2, '', '', '']) // trimmed ]); TestRedoUndo('Many Return before space', TestText, 1,2, [ VK_RETURN, 1, 3, ExpText([ -2, '']), VK_RETURN, 1, 4, ExpText([ -2, '', '']), VK_RETURN, 1, 5, ExpText([ -2, '', '', '']) ]); TestRedoUndo('Many Return mid space', TestText, 4,2, [ VK_RETURN, 4, 3, ExpText([ -2, '']), // trimmed VK_RETURN, 4, 4, ExpText([ -2, '', '']), // trimmed VK_RETURN, 4, 5, ExpText([ -2, '', '', '']) // trimmed ]); TestRedoUndo('Return multi empty', TestText, 1,7, [ VK_RETURN, 4, 8, ExpText([ -8, ' ']) ] ); TestRedoUndo('Return at pos1 of unindeted', TestText2, 1,2, [ VK_RETURN, 1, 3, ExpText2([ -2, '']) ] ); TestRedoUndo('Return at pos1 of unindeted 2', TestText2, 1,5, [ VK_RETURN, 1, 6, ExpText2([ -5, '']) ] ); // caret pos are logical TestRedoUndo('Return after tab', TestText, 6,9, [ VK_RETURN, 9,10, ExpText([ 9, '', ' x']) ] ); TestRedoUndo('Return before tab', TestText, 1,9, [ VK_RETURN, 1,10, ExpText([-9, '']) ] ); TestRedoUndo('Return middle tab 1', TestText, 3,9, [ VK_RETURN, 5,10, ExpText([ 9, '', ' '+#32#32#9+'x']) ] ); TestRedoUndo('Return middle tab 2', TestText, 4,9, [ VK_RETURN, 6,10, ExpText([ 9, '', ' '+#32#9+'x']) ] ); TestRedoUndo('Return tab EOL', TestText, 7,9, [ VK_RETURN, 9,10, ExpText([-10, ' ']) ] ); {%endregion} (* *** *) {%region sbitCopySpaceTab} PushBaseName('CopySpaceTab'); TSynBeautifier(SynEdit.Beautifier).IndentType := sbitCopySpaceTab; TestRedoUndo('Return after space', TestText, 5,2, [ VK_RETURN, 5, 3, ExpText([ -2, '']) ] ); // trimmed TestRedoUndo('Return before space', TestText, 1,2, [ VK_RETURN, 1, 3, ExpText([ -2, '']) ] ); TestRedoUndo('Return mid space', TestText, 4,2, [ VK_RETURN, 4, 3, ExpText([ -2, '']) ] ); // trimmed TestRedoUndo('Return after tab', TestText, 6,9, [ VK_RETURN, 6,10, ExpText([ 9, '', #32#9#32#32#9+'x']) ] ); TestRedoUndo('Return before tab', TestText, 1,9, [ VK_RETURN, 1,10, ExpText([-9, '']) ] ); TestRedoUndo('Return middle tab 1', TestText, 3,9, [ VK_RETURN, 3,10, ExpText([ 9, '', #32#9#32#32#9+'x']) ] ); TestRedoUndo('Return middle tab 2', TestText, 4,9, [ VK_RETURN, 4,10, ExpText([ 9, '', #32#9#32#32#9+'x']) ] ); TestRedoUndo('Return tab EOL', TestText, 7,9, [ VK_RETURN, 6,10, ExpText([-10, #32#9#32#32#9]) ] ); TestRedoUndo('Many Return tab EOL', TestText, 7,9, [ VK_RETURN, 6,10, ExpText([-10, #32#9#32#32#9]), VK_RETURN, 6,11, ExpText([-10, '', #32#9#32#32#9]), VK_RETURN, 6,12, ExpText([-10, '', '', #32#9#32#32#9]) ]); {%endregion} (* *** *) {%region sbitPosCaret} PopPushBaseName('PosCaret'); TSynBeautifier(SynEdit.Beautifier).IndentType := sbitPositionCaret; TestRedoUndo('Return after space', TestText, 5,2, [ VK_RETURN, 5, 3, ExpText([ -2, '']) ] ); // trimmed TestRedoUndo('Return before space', TestText, 1,2, [ VK_RETURN, 1, 3, ExpText([ -2, '']) ] ); TestRedoUndo('Return mid space', TestText, 4,2, [ VK_RETURN, 4, 3, ExpText([ -2, '']) ] ); //trimmed TestRedoUndo('Return eol', TestText, 6,2, [ VK_RETURN, 5, 3, ExpText([ -3, '']) ] ); // position only TestRedoUndo('Many Return eol', TestText, 6,2, [ // position only VK_RETURN, 5, 3, ExpText([ -3, '']), VK_RETURN, 5, 4, ExpText([ -3, '', '']), VK_RETURN, 5, 5, ExpText([ -3, '', '', '']) ]); TestRedoUndo('Return after tab', TestText, 6,9, [ VK_RETURN, 9,10, ExpText([ 9, '', ' '+'x']) ] ); TestRedoUndo('Return before tab', TestText, 1,9, [ VK_RETURN, 1,10, ExpText([-9, '']) ] ); TestRedoUndo('Return middle tab 1', TestText, 3,9, [ VK_RETURN, 5,10, ExpText([ 9, '', ' '+#32#32#9+'x']) ] ); TestRedoUndo('Return middle tab 2', TestText, 4,9, [ VK_RETURN, 6,10, ExpText([ 9, '', ' '+#32#9+'x']) ] ); TestRedoUndo('Return tab EOL', TestText, 7,9, [ VK_RETURN, 9,10, ExpText([-10, '']) ] ); // pos only TestRedoUndo('Many Return tab EOL', TestText, 7,9, [ VK_RETURN, 9,10, ExpText([-10, '']), // pos only VK_RETURN, 9,11, ExpText([-10, '', '']), VK_RETURN, 9,12, ExpText([-10, '', '', '']) ]); {%endregion} {%region CTRL-N} PopPushBaseName('SpaceOnly'); // VK_N will be ctrl-n (special detection in TestRedoUndo TSynBeautifier(SynEdit.Beautifier).IndentType := sbitSpace; TestRedoUndo('CTRL-N EOL', TestText, 5,1, [ VK_N, 5, 1, ExpText([ -2, '']) ] ); // trimmed TestRedoUndo('CTRL-N pos1 unindeted',TestText2, 1,2, [ VK_N, 1, 2, ExpText2([ -2, '']) ] ); // TODO: must not TestRedoUndo('CTRL-N after space', TestText, 5,2, [ VK_N, 5, 2, ExpText([ -2, ' ']) ] ); TestRedoUndo('CTRL-N before space', TestText, 1,2, [ VK_N, 1, 2, ExpText([ -2, '']) ] ); // TODO: indents too much TestRedoUndo('CTRL-N mid space', TestText, 4,2, [ VK_N, 4, 2, ExpText([ -2, ' ']) ] ); {%endregion} {%endregion} end; procedure TTestSynBeautifier.DefaultUnIndent; function TestText: TStringArray; begin SetLength(Result, 11); Result[0] := ' a;'; Result[1] := ' b'; Result[2] := ' c'; Result[3] := ''; Result[4] := ' d'; Result[5] := ''; Result[6] := ' x'; Result[7] := ''; Result[8] := ' y'; Result[9] := ' z'; Result[10]:= ''; end; function ExpText(rpl: array of const): String; begin Result := LinesToText(LinesReplace(TestText, rpl)) end; function TestText2: TStringArray; begin SetLength(Result, 4); Result[0] := ' a;'; Result[1] := ' b'; Result[2] := ' '; Result[3] := ''; end; function ExpText2(rpl: array of const): String; begin Result := LinesToText(LinesReplace(TestText2, rpl)) end; begin SkipGroupUndo := False; ReCreateEdit; SynEdit.TabWidth := 4; SynEdit.TrimSpaceType := settMoveCaret; BaseTestName := 'UnIndent pff'; SynEdit.Options := SynEdit.Options - [eoTrimTrailingSpaces, eoScrollPastEol, eoAutoIndent]; SynEdit.Options2 := SynEdit.Options2 - [eoPersistentBlock]; TestRedoUndo('simple', TestText, 5,2, [ VK_BACK, 4,2, ExpText([ 2, ' b']) ] ); TestRedoUndo('not at indent-end', TestText, 4,2, [ VK_BACK, 3,2, ExpText([ 2, ' b']) ] ); BaseTestName := 'UnIndent'; SynEdit.Options := SynEdit.Options + [eoAutoIndent] - [eoTrimTrailingSpaces, eoScrollPastEol]; TestRedoUndo('simple', TestText, 5,2, [ VK_BACK, 3,2, ExpText([ 2, ' b']) ] ); TestRedoUndo('simple twice', TestText, 5,2, [ VK_BACK, 3,2, ExpText([ 2, ' b']), VK_BACK, 1,2, ExpText([ 2, 'b']) ]); TestRedoUndo('not at indent-end', TestText, 4,2, [ VK_BACK, 3,2, ExpText([ 2, ' b']) ] ); TestRedoUndo('2 level', TestText, 7,3, [ VK_BACK, 5,3, ExpText([ 3, ' c']), VK_BACK, 3,3, ExpText([ 3, ' c']), VK_BACK, 1,3, ExpText([ 3, 'c']) ]); TestRedoUndo('2 lvl, not indent-end', TestText, 6,3, [ VK_BACK, 5,3, ExpText([ 3, ' c']) ] ); TestRedoUndo('2 lvl, not indent-end 2', TestText, 5,3, [ VK_BACK, 4,3, ExpText([ 3, ' c']) ] ); TestRedoUndo('below empty line', TestText, 7,5, [ VK_BACK, 5,5, ExpText([ 5, ' d']) ] ); TestRedoUndo('below empty, not I-End', TestText, 6,5, [ VK_BACK, 5,5, ExpText([ 5, ' d']) ] ); TestRedoUndo('below empty, not I-End 2',TestText, 5,5, [ VK_BACK, 4,5, ExpText([ 5, ' d']) ] ); TestRedoUndo('below empty line, many', TestText, 7,5, [ VK_BACK, 5,5, ExpText([ 5, ' d']), VK_BACK, 3,5, ExpText([ 5, ' d']), VK_BACK, 1,5, ExpText([ 5, 'd']) ]); TestRedoUndo('unindent single space', TestText, 6,10, [ VK_BACK, 5,10, ExpText([ 10, ' z']) ] ); TestRedoUndo('empty line in many', TestText, 6,10, [ VK_BACK, 5,10, ExpText([ 10, ' z']), VK_BACK, 3,10, ExpText([ 10, ' z']), VK_BACK, 1,10, ExpText([ 10, 'z']) ]); SkipGroupUndo := True; // The first VK_BACK is deletet, not unindent, therefore it's 2 different undo SynEdit.Options := SynEdit.Options - [eoTrimTrailingSpaces]; TestRedoUndo('only indent, no text', TestText, 8,3, [ VK_BACK, 7,3, ExpText([ 3, ' ']), VK_BACK, 5,3, ExpText([ 3, ' ']), VK_BACK, 3,3, ExpText([ 3, ' ']), VK_BACK, 1,3, ExpText([ 3, '']) ]); SynEdit.Options := SynEdit.Options + [eoTrimTrailingSpaces]; TestRedoUndo('only indent, no text (trim)', TestText, 8,3, [ VK_BACK, 7,3, ExpText([ 3, ' ']), VK_BACK, 5,3, ExpText([ 3, ' ']), VK_BACK, 3,3, ExpText([ 3, ' ']), VK_BACK, 1,3, ExpText([ 3, '']) ]); SynEdit.Options := SynEdit.Options - [eoTrimTrailingSpaces]; SkipGroupUndo := False; TestRedoUndo('no unindent (selection)', TestText, 5,2, [ VK_BACK, 4,2, ExpText([ 2, ' b']) ], 4,2); TestRedoUndo('no unindent (selection)', TestText, 5,2, [ VK_BACK, 1,2, ExpText([ 2, 'b']) ], 1,2); SynEdit.Options2 := SynEdit.Options2 + [eoPersistentBlock]; TestRedoUndo('unindent (persist selection)', TestText, 5,2, [ VK_BACK, 3,2, ExpText([ 2, ' b']) ], 4,2); TestRedoUndo('unindent (persist selection)', TestText, 5,2, [ VK_BACK, 3,2, ExpText([ 2, ' b']) ], 1,2); SynEdit.Options2 := SynEdit.Options2 - [eoPersistentBlock]; BaseTestName := 'UnIndent, with 1st line indended most'; SynEdit.Options := SynEdit.Options - [eoTrimTrailingSpaces{, eoScrollPastEol}]; SynEdit.Options2 := SynEdit.Options2 - [eoPersistentBlock]; TestRedoUndo('extra indent on first', TestText2, 7,3, [ VK_BACK, 5,3, ExpText2([ 3, ' ']), VK_BACK, 1,3, ExpText2([ 3, '']) ] ); end; function TTestSynBeautifier.GetIndentCallBack(Sender: TObject; Editor: TObject; LogCaret, OldLogCaret: TPoint; FirstLinePos, LastLinePos: Integer; Reason: TSynEditorCommand; SetIndentProc: TSynBeautifierSetIndentProc): boolean; var i: integer; begin FGotLogCaret := LogCaret; FGotOldLogCaret := OldLogCaret; FGotFirstLinePos := FirstLinePos; FGotLastLinePos := LastLinePos; FGotReason := Reason; for i := 0 to high(FCallBackData) do SetIndentProc( FCallBackData[i].LinePos, FCallBackData[i].Indent, FCallBackData[i].RelativeToLinePos, FCallBackData[i].IndentChars, FCallBackData[i].IndentCharsFromLinePos ); Result := FGetIndentCallBackResult; end; procedure TTestSynBeautifier.IndentCallBack; function TestText: TStringArray; begin SetLength(Result, 13); Result[0] := ' a;'; Result[1] := #9+'b'; Result[2] := ' c'; Result[3] := ''; Result[4] := #32#9+' d'; Result[5] := ''; Result[6] := ' x'; Result[7] := ''; Result[8] := ' y'; Result[9] := ' z'; Result[10]:= ' mn'; Result[11]:= ' op'; Result[12]:= ''; end; function ExpText(rpl: array of const): String; begin Result := LinesToText(LinesReplace(TestText, rpl)) end; procedure SetCB(Res: Boolean; Action: array of const); var i: integer; begin FGetIndentCallBackResult := Res; FGotLogCaret := Point(-3, -33); FGotOldLogCaret := Point(-3, -33); FGotFirstLinePos := -99; FGotLastLinePos := -99; FGotReason := 27997; SetLength(FCallBackData, length(Action) div 5); for i := 0 to length(Action) div 5 - 1 do begin FCallBackData[i].LinePos := Action[i*5 + 0].VInteger; FCallBackData[i].Indent := Action[i*5 + 1].VInteger; FCallBackData[i].RelativeToLinePos := Action[i*5 + 2].VInteger; case Action[i*5 + 3].VType of vtString: FCallBackData[i].IndentChars := AnsiString(Action[i*5 + 3].VString); vtAnsiString: FCallBackData[i].IndentChars := AnsiString(Action[i*5 + 3].VAnsiString); vtChar: FCallBackData[i].IndentChars := Action[i*5 + 3].VChar; end; FCallBackData[i].IndentCharsFromLinePos := Action[i*5 + 4].VInteger; end; end; procedure TestCbArgs(Name: String; OldX, OldY, NewX, NewY, FirstLine, LastLine, Reason: integer); begin AssertEquals(Name + 'CB-Args: Got Caret.x Before', OldX, FGotOldLogCaret.x); AssertEquals(Name + 'CB-Args: Got Caret.y Before', OldY, FGotOldLogCaret.y); AssertEquals(Name + 'CB-Args: Got Caret.x After', NewX, FGotLogCaret.x); // x=1 => before auto indent AssertEquals(Name + 'CB-Args: Got Caret.y After', NewY, FGotLogCaret.y); AssertEquals(Name + 'CB-Args: FirstLine', FirstLine, FGotFirstLinePos); AssertEquals(Name + 'CB-Args: LastLine', LastLine, FGotLastLinePos); AssertEquals(Name + 'CB-Args: Reason', Reason, FGotReason); end; begin ReCreateEdit; try SynEdit.Beautifier.OnGetDesiredIndent := @GetIndentCallBack; SynEdit.TabWidth := 4; SynEdit.TrimSpaceType := settMoveCaret; BaseTestName := 'Callback (no trim)'; SynEdit.Options := SynEdit.Options + [eoAutoIndent] - [eoTrimTrailingSpaces, eoScrollPastEol]; TSynBeautifier(SynEdit.Beautifier).IndentType := sbitSpace; // sbitCopySpaceTab; SetCB(False, []); TestRedoUndo('cb result = false', TestText, 3,2, [ VK_RETURN, 5,3, ExpText([ -3, ' ']) ] ); TestCbArgs('cb result = false', 3,2, 1,3, 3,3, ecLineBreak); SetCB(True, []); TestRedoUndo('cb result = true ', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ -3, '']) ] ); TestCbArgs('cb result = true', 3,2, 1,3, 3,3, ecLineBreak); // LinPos, Indend, RelativeLinePos=0, Char(s), indentFromLine=-1, SetCB(True, [2, 2, 0, '', -1, 3, 3, 0, '', -1 ]); TestRedoUndo('cb absolute', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, ' b', ' ']) ] ); // caret is NOT adjusted TestCbArgs('cb absolute', 3,2, 1,3, 3,3, ecLineBreak); SetCB(True, [2, 3, 0, '', -1, 3, 3, 0, '', 2, 4, 2, 0, '', -1 ]); TestRedoUndo('cb absolute 2', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, 2, ' b', ' ', ' c']) ] ); TestCbArgs('cb absolute 2', 3,2, 1,3, 3,3, ecLineBreak); SetCB(True, [2, 7, 0, '', 2, 3, 6, 0, '', 2 ]); TestRedoUndo('cb absolute, keep-cur', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, #9+' b', #9+' ']) ] ); TestCbArgs('cb absolute, keep-cur', 3,2, 1,3, 3,3, ecLineBreak); SetCB(True, [2, -2, 2, '', -1, 3, 3, 2, '', -1 ]); TestRedoUndo('cb relative ', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, ' b', ' ']) ] ); TestCbArgs('cb relative ', 3,2, 1,3, 3,3, ecLineBreak); SetCB(True, [2, 3, 2, '', -1, 3, -1, 2, '', -1 ]); TestRedoUndo('cb relative 2', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, ' b', ' ']) ] ); TestCbArgs('cb relative 2', 3,2, 1,3, 3,3, ecLineBreak); SetCB(True, [2, 3, 2, '', -1, 3, 1, 3, '', -1 ]); TestRedoUndo('cb relative 3', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, ' b', ' ']) ] ); TestCbArgs('cb relative 3', 3,2, 1,3, 3,3, ecLineBreak); SetCB(True, [2, 3, 2, '', 2, 3, 0, 2, '', 2 ]); TestRedoUndo('cb relative, keep', TestText, 3,2, [ VK_RETURN, 1,3, ExpText([ 2, #9+' b', #9+' ']) ] ); TestCbArgs('cb relative, keep', 3,2, 1,3, 3,3, ecLineBreak); SetCB(True, [1, 9, 0, #9, -1]); TestRedoUndo('cb abs; char=tab', TestText, 5,1, [ VK_RETURN, 1,2, ExpText([ 1, #9#9+' a;', '']) ] ); TestCbArgs('cb abs; char=tab', 5,1, 1,2, 2,2, ecLineBreak); (* Paste *) ClipBoardText := '1'; SetCB(True, []); TestRedoUndo('paste 1 lines, noaction', TestText, 5,11, [ VK_V, 6,11, ExpText([ 11, ' mn1']) ] ); TestCbArgs('paste 1 lines, noaction', 5,11, 6,11, 11,11, ecPaste); ClipBoardText := '1'+LineEnding+'2'; SetCB(True, []); TestRedoUndo('paste 2 lines, noaction', TestText, 5,11, [ VK_V, 2,12, ExpText([ 11, ' mn1', '2']) ] ); TestCbArgs('paste 2 lines, noaction', 5,11, 2,12, 11,12, ecPaste); ClipBoardText := '1'+LineEnding+'2'+LineEnding+'3'; SetCB(True, []); TestRedoUndo('paste 3 lines, noaction', TestText, 5,11, [ VK_V, 2,13, ExpText([ 11, ' mn1', '2', '3']) ] ); TestCbArgs('paste 3 lines, noaction', 5,11, 2,13, 11,13, ecPaste); ClipBoardText := '1'+LineEnding+'2'+LineEnding+'3'+LineEnding+'4'; SetCB(True, []); TestRedoUndo('paste 4 lines, noaction', TestText, 5,11, [ VK_V, 2,14, ExpText([ 11, ' mn1', '2', '3', '4']) ] ); TestCbArgs('paste 4 lines, noaction', 5,11, 2,14, 11,14, ecPaste); ClipBoardText := '1'+LineEnding+'2'+LineEnding+'3'+LineEnding+'4'+LineEnding; SetCB(True, []); TestRedoUndo('paste 4+ lines, noaction', TestText, 5,11, [ VK_V, 1,15, ExpText([ 11, ' mn1', '2', '3', '4', '']) ] ); TestCbArgs('paste 4+ lines, noaction', 5,11, 1,15, 11,15, ecPaste); ClipBoardText := '1'+LineEnding+'2'+LineEnding+'3'+LineEnding+'4'; SetCB(True, []); TestRedoUndo('paste 4 lines on empty, noaction', TestText, 1,8, [ VK_V, 2,11, ExpText([ 8, '1', '2', '3', '4']) ] ); TestCbArgs('paste 4 lines on empty, noaction', 1,8, 2,11, 8,11, ecPaste); ClipBoardText := '1'+LineEnding+'2'+LineEnding+'3'+LineEnding+'4'+LineEnding +'5'; SetCB(True, []); TestRedoUndo('paste 5 lines, noaction', TestText, 5,11, [ VK_V, 2,15, ExpText([ 11, ' mn1', '2', '3', '4', '5']) ] ); TestCbArgs('paste 5 lines, noaction', 5,11, 2,15, 11,15, ecPaste); ClipBoardText := '1'+LineEnding+'2'; SetCB(True, []); TestRedoUndo('paste 2 lines middle, noaction', TestText, 4,11, [ VK_V, 2,12, ExpText([ 11, ' m1', '2n']) ] ); TestCbArgs('paste 2 lines middle, noaction', 4,11, 2,12, 11,12, ecPaste); ClipBoardText := '1'+LineEnding+'2'+LineEnding+'3'; SetCB(True, []); TestRedoUndo('paste 3 lines middle, noaction', TestText, 4,11, [ VK_V, 2,13, ExpText([ 11, ' m1', '2', '3n']) ] ); TestCbArgs('paste 3 lines middle, noaction', 4,11, 2,13, 11,13, ecPaste); BaseTestName := 'Callback (trim)'; SynEdit.Options := SynEdit.Options + [eoTrimTrailingSpaces, eoAutoIndent] - [eoScrollPastEol]; (* Paste *) ClipBoardText := '1'; SetCB(True, []); TestRedoUndo('paste 1 lines, noaction', TestText, 5,11, [ VK_V, 6,11, ExpText([ 11, ' mn1']) ] ); TestCbArgs('paste 1 lines, noaction', 5,11, 6,11, 11,11, ecPaste); ClipBoardText := '1'+LineEnding+'2'; SetCB(True, []); TestRedoUndo('paste 2 lines, noaction', TestText, 5,11, [ VK_V, 2,12, ExpText([ 11, ' mn1', '2']) ] ); TestCbArgs('paste 2 lines, noaction', 5,11, 2,12, 11,12, ecPaste); ClipBoardText := '1'+LineEnding+'2'+LineEnding+'3'; SetCB(True, []); TestRedoUndo('paste 3 lines, noaction', TestText, 5,11, [ VK_V, 2,13, ExpText([ 11, ' mn1', '2', '3']) ] ); TestCbArgs('paste 3 lines, noaction', 5,11, 2,13, 11,13, ecPaste); ClipBoardText := '1'+LineEnding+'2'+LineEnding+'3'+LineEnding+'4'; SetCB(True, []); TestRedoUndo('paste 4 lines, noaction', TestText, 5,11, [ VK_V, 2,14, ExpText([ 11, ' mn1', '2', '3', '4']) ] ); TestCbArgs('paste 4 lines, noaction', 5,11, 2,14, 11,14, ecPaste); ClipBoardText := '1'+LineEnding+'2'+LineEnding+'3'+LineEnding+'4'+LineEnding; SetCB(True, []); TestRedoUndo('paste 4+ lines, noaction', TestText, 5,11, [ VK_V, 1,15, ExpText([ 11, ' mn1', '2', '3', '4', '']) ] ); TestCbArgs('paste 4+ lines, noaction', 5,11, 1,15, 11,15, ecPaste); ClipBoardText := '1'+LineEnding+'2'+LineEnding+'3'+LineEnding+'4'; SetCB(True, []); TestRedoUndo('paste 4 lines on empty, noaction', TestText, 1,8, [ VK_V, 2,11, ExpText([ 8, '1', '2', '3', '4']) ] ); TestCbArgs('paste 4 lines on empty, noaction', 1,8, 2,11, 8,11, ecPaste); ClipBoardText := '1'+LineEnding+'2'+LineEnding+'3'+LineEnding+'4'+LineEnding +'5'; SetCB(True, []); TestRedoUndo('paste 5 lines, noaction', TestText, 5,11, [ VK_V, 2,15, ExpText([ 11, ' mn1', '2', '3', '4', '5']) ] ); TestCbArgs('paste 5 lines, noaction', 5,11, 2,15, 11,15, ecPaste); ClipBoardText := '1'+LineEnding+'2'; SetCB(True, []); TestRedoUndo('paste 2 lines middle, noaction', TestText, 4,11, [ VK_V, 2,12, ExpText([ 11, ' m1', '2n']) ] ); TestCbArgs('paste 2 lines middle, noaction', 4,11, 2,12, 11,12, ecPaste); ClipBoardText := '1'+LineEnding+'2'+LineEnding+'3'; SetCB(True, []); TestRedoUndo('paste 3 lines middle, noaction', TestText, 4,11, [ VK_V, 2,13, ExpText([ 11, ' m1', '2', '3n']) ] ); TestCbArgs('paste 3 lines middle, noaction', 4,11, 2,13, 11,13, ecPaste); finally SynEdit.Beautifier.OnGetDesiredIndent := nil; end; end; procedure TTestSynBeautifier.IndentPasComment; var Beautifier: TSynBeautifierPascal; BaseName, BaseConf: String; BaseText: TStringArray; BaseStep: Integer; CurrentText: TStringArray; ParentIndentType: TSynBeautifierIndentType; PIName: string; LoopMatchFirst, LoopIndentApplyNoMatch, LoopIndentAdds, LoopIndentMax: Integer; ExtraIndentFlags: TSynCommentIndentFlags; MatchLine: TSynCommentMatchLine; type TTestFlags = set of (tfExpLineDiffUseBase); procedure ConfigBeautifier(AType: TSynCommentType; AIndentMode: TSynCommentIndentFlags; AIndentFirstLineMax: Integer; AIndentFirstLineExtra: String; ACommentMode: TSynCommentContineMode; AMatchMode: TSynCommentMatchMode; AMatchLine: TSynCommentMatchLine; ACommentIndent: TSynBeautifierIndentType; AMatch: String; APrefix: String; AExtenbSlash: TSynCommentExtendMode = sceNever ); begin writestr(BaseConf, AType,':', ' IMode=', dbgs(AIndentMode), ' IMax=', AIndentFirstLineMax, ' IExtra=', AIndentFirstLineExtra, ' CMode=', ACommentMode, ' CMatch=', AMatchMode, ' CLine=', AMatchLine, ' M=''', AMatch, ''' R=''', APrefix, ''' CIndent=', ACommentIndent ); case AType of sctAnsi: with Beautifier do begin AnsiIndentMode := AIndentMode; AnsiIndentFirstLineMax := AIndentFirstLineMax; AnsiIndentFirstLineExtra := AIndentFirstLineExtra; AnsiCommentMode := ACommentMode; AnsiMatchMode := AMatchMode; AnsiMatchLine := AMatchLine; AnsiCommentIndent := ACommentIndent; AnsiMatch := AMatch; AnsiPrefix := APrefix; end; sctBor: with Beautifier do begin BorIndentMode := AIndentMode; BorIndentFirstLineMax := AIndentFirstLineMax; BorIndentFirstLineExtra := AIndentFirstLineExtra; BorCommentMode := ACommentMode; BorMatchMode := AMatchMode; BorMatchLine := AMatchLine; BorCommentIndent := ACommentIndent; BorMatch := AMatch; BorPrefix := APrefix; end; sctSlash: with Beautifier do begin ExtendSlashCommentMode := AExtenbSlash; SlashIndentMode := AIndentMode; SlashIndentFirstLineMax := AIndentFirstLineMax; SlashIndentFirstLineExtra := AIndentFirstLineExtra; SlashCommentMode := ACommentMode; SlashMatchMode := AMatchMode; SlashMatchLine := AMatchLine; SlashCommentIndent := ACommentIndent; SlashMatch := AMatch; SlashPrefix := APrefix; end; end; end; function BaseLinesToText: String; var i, j, k: Integer; begin i := 0; j := 30; k := length(BaseText); Result := ' '; while k > 0 do if BaseText[k - 1] = '' then dec(k) else break; while i < k do if BaseText[i] = '' then inc(i) else break; Result := Result + ' '+ IntToStr(i)+':'; while (i < k) and (j>0) do begin Result := Result + copy(BaseText[i], 1, j); if Length(BaseText[i]) > j then Result := Result + '...' else Result := Result + '#13'; j := j - Length(BaseText[i]); inc(i); end; end; Procedure DoSetText(ABaseName: String; ALines: TStringArray); begin BaseName := ABaseName; BaseStep := 0; BaseText := ALines; CurrentText := BaseText; SetLines(ALines); BaseName := BaseName + BaseLinesToText; DebugLn(BaseConf); end; Procedure DoSetText(ABaseName: String; ALines: array of const); begin BaseName := ABaseName; BaseStep := 0; BaseText := nil; SetLength(BaseText, 10); BaseText := LinesReplace(BaseText, ALines); CurrentText := BaseText; SetLines(BaseText); BaseName := BaseName + BaseLinesToText; DebugLn(BaseConf); end; // Caret is logical Procedure DoNewLine(AName: string; AStartX, AStartY: Integer; ExpX, ExpY: Integer; ExpLineDiff: array of const; AFlags: TTestFlags = []); begin SynEdit.TestTypeText(AStartX, AStartY, #13); if tfExpLineDiffUseBase in AFlags then CurrentText := LinesReplace(BaseText, ExpLineDiff) else CurrentText := LinesReplace(CurrentText, ExpLineDiff); inc(BaseStep); debugln([Format('[%s (%d)] %s -- Enter at (%d, %d)', [BaseName, BaseStep, AName, AStartX, AStartY])]); debugln([dbgs(SynEdit.LogicalCaretXY), ': ', DbgStr(SynEdit.TestFullText)]); TestIsCaret(Format('[%s (%d)] %s -- Enter at (%d, %d) -- Exp Caret (%d, %d) :: %s', [BaseName, BaseStep, AName, AStartX, AStartY, ExpX, ExpY, BaseConf]), ExpX, ExpY); TestIsText(Format('[%s (%d)] %s -- Enter at (%d, %d) -> (%d, %d) - Exp Text (Changes: %d) :: %s', [BaseName, BaseStep, AName, AStartX, AStartY, ExpX, ExpY, length(ExpLineDiff), BaseConf]), CurrentText); end; begin Beautifier := TSynBeautifierPascal.Create(nil); try ReCreateEdit; SynEdit.Beautifier := Beautifier; SynEdit.Options := SynEdit.Options - [eoTrimTrailingSpaces];// + [eoScrollPastEol]; SynEdit.TabWidth := 4; UseFullText := True; Beautifier.IndentType := sbitCopySpaceTab; {%region Bor (Curly) } ConfigBeautifier(sctBor, [sciAddTokenLen, sciAddPastTokenIndent], 0, '', sccPrefixMatch, scmMatchAfterOpening, sclMatchPrev, sbitSpace, '^ ?([\*#])', '$1'); DoSetText('Curly simple 1', [2, ' {* abc']); DoNewLine('', 7, 2, 6, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('', 7, 3, 6, 4, [3, ' * b', ' * c' ]); // 3:" * b|c" => 4:" * |c" DoNewLine('', 7, 4, 6, 5, [4, ' * c', ' * ' ]); // 4:" * c|" DoNewLine('', 5, 3, 5, 4, [3, ' *', ' * b' ]); // 3:" *| b" DoSetText('Curly simple 2', [2, ' {* abc', ' * ']); DoNewLine('', 5, 3, 5, 4, [3, ' * ', ' * ']); DoSetText('Curly simple 3', [2, ' {*', ' *']); DoNewLine('', 4, 3, 4, 4, [3, ' *', ' *']); DoSetText('Curly, not matching', [2, ' {+ abc']); DoNewLine('', 7, 2, 3, 3, [2, ' {+ a', ' bc']); // 2:" {* a|bc" DoSetText('Curly Nested 1', [2, ' {* abc {', ' # def']); DoNewLine('', 9, 3, 8, 4, [3, ' # d', ' # ef']); // 2:" {# d|ef" // Todo: Nested that matches the "{", and uses smartSpace. ConfigBeautifier(sctBor, [sciAddTokenLen, sciAddPastTokenIndent], 0, '', sccPrefixMatch, scmMatchOpening, sclMatchPrev, sbitSpace, '^\{ ?([\*#])', '$1'); DoSetText('Bor Nested 1', [2, ' { * abc ', ' { # def']); // Todo: smart, must detect, that prevline = nest open // DoNewLine('', 10, 3, 9, 4, [3, ' { # d', ' # ef']); // 2:" {# d|ef" {%region Bor (Curly) --- TSynCommentIndentFlag (Pre-PerfixIndent) } PushBaseName('Curly - TSynCommentIndentFlag'); // sbitSpace, sbitCopySpaceTab, sbitPositionCaret for ParentIndentType := low(TSynBeautifierIndentType) to sbitPositionCaret // high(TSynBeautifierIndentType) do begin Beautifier.IndentType := ParentIndentType; WriteStr(PIName, ParentIndentType); PushBaseName(PIName); // sclMatchFirst; for LoopMatchFirst := 0 to 1 do begin if LoopMatchFirst = 0 then MatchLine := sclMatchPrev else MatchLine := sclMatchFirst; PushBaseName(dbgs(MatchLine)); // sciApplyIndentForNoMatch for LoopIndentApplyNoMatch := 0 to 1 do begin // sciAddTokenLen, sciAddPastTokenIndent; for LoopIndentAdds := 0 to 3 do begin ExtraIndentFlags := []; if LoopIndentApplyNoMatch = 1 then begin ExtraIndentFlags := ExtraIndentFlags + [sciApplyIndentForNoMatch]; PushBaseName('ApplyIndNoMatch'); end else PushBaseName(''); if (LoopIndentAdds and 1) = 1 then begin ExtraIndentFlags := ExtraIndentFlags + [sciAddTokenLen]; PushBaseName('AddTokLen'); end else PushBaseName(''); if (LoopIndentAdds and 2) = 2 then begin ExtraIndentFlags := ExtraIndentFlags + [sciAddPastTokenIndent]; PushBaseName('AddPastTokInd'); end else PushBaseName(''); {%region [] use default indent} PushBaseName('IndType=[]'); // AnsiIndentFirstLineMax, indent is >= 2, so it is not affeceted by 1 or 2 for LoopIndentMax := 0 to 2 do begin if LoopIndentMax = 1 then Continue; // TODO: add tests // MaxIndent was extended to act on default indent to (first line only) PushBaseName('Max='+IntToStr(LoopIndentMax)); ConfigBeautifier(sctBor, [] + ExtraIndentFlags, LoopIndentMax, '', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 5, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 6, 3, 5, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent in comment "{ *" / matching DoSetText('"{ *" matching', [2, ' { * abc']); if not (sciAddPastTokenIndent in ExtraIndentFlags) then begin DoNewLine('after 1st', 9, 2, 5, 3, [2, ' { * a', ' * bc']); // 2:" { * a|bc" DoNewLine('any line', 6, 3, 5, 4, [3, ' * b', ' * c']); // 3:" * b|c" end else begin DoNewLine('after 1st', 9, 2, 7, 3, [2, ' { * a', ' * bc']); // 2:" { * a|bc" DoNewLine('any line', 8, 3, 7, 4, [3, ' * b', ' * c']); // 3:" * b|c" end; // Indent tabs / matching if (ParentIndentType in [sbitCopySpaceTab]) and (LoopIndentMax = 0) then begin DoSetText('tabs matching', [2, #9' {*'#9' abc']); DoNewLine('after 1st', 8, 2, 6, 3, [2, #9' {*'#9' a', #9' *'#9' bc']); // 2:"_ {*_ a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, #9' *'#9' b', #9' *'#9' c']); // 3:" * b|c" end; // Indent, not BOL / matching DoSetText('not BOL matching', [2, ' ;{* abc']); DoNewLine('after 1st', 8, 2, 5, 3, [2, ' ;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 6, 3, 5, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent / NOT matching DoSetText('not matching', [2, ' {+ abc']); DoNewLine('after 1st', 7, 2, 3, 3, [2, ' {+ a', ' bc']); // 2:" {+ a|bc" DoNewLine('any line', 4, 3, 3, 4, [3, ' b', ' c']); // 3:" b|c" end else begin // [sciAddTokenLen] // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 6, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent in comment "{ *" / matching DoSetText('"{ *" matching', [2, ' { * abc']); if not (sciAddPastTokenIndent in ExtraIndentFlags) then begin DoNewLine('after 1st', 9, 2, 6, 3, [2, ' { * a', ' * bc']); // 2:" { * a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" end else begin DoNewLine('after 1st', 9, 2, 8, 3, [2, ' { * a', ' * bc']); // 2:" { * a|bc" DoNewLine('any line', 9, 3, 8, 4, [3, ' * b', ' * c']); // 3:" * b|c" end; // Indent tabs / matching if (ParentIndentType in [sbitCopySpaceTab]) and (LoopIndentMax = 0) then begin DoSetText('tabs matching', [2, #9' {*'#9' abc']); DoNewLine('after 1st', 8, 2, 7, 3, [2, #9' {*'#9' a', #9' *'#9' bc']); // 2:"_ {*_ a|bc" DoNewLine('any line', 8, 3, 7, 4, [3, #9' *'#9' b', #9' *'#9' c']); // 3:" * b|c" end; // Indent, not BOL / matching DoSetText('not BOL matching', [2, ' ;{* abc']); DoNewLine('after 1st', 8, 2, 6, 3, [2, ' ;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent / NOT matching DoSetText('not matching', [2, ' {+ abc']); if sciApplyIndentForNoMatch in ExtraIndentFlags then begin DoNewLine('after 1st', 7, 2, 4, 3, [2, ' {+ a', ' bc']); // 2:" {+ a|bc" DoNewLine('any line', 5, 3, 4, 4, [3, ' b', ' c']); // 3:" b|c" end else begin DoNewLine('after 1st', 7, 2, 3, 3, [2, ' {+ a', ' bc']); // 2:" {+ a|bc" DoNewLine('any line', 4, 3, 3, 4, [3, ' b', ' c']); // 3:" b|c" end; end; PopBaseName; end; // LoopIndentMax; PushBaseName('Max='+IntToStr(10)); ConfigBeautifier(sctBor, [sciAlignOpenOnce, sciAlignOpenSkipBOL] + ExtraIndentFlags, 0, '', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 5, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 6, 3, 5, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // sciAlignOpenOnce applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 8, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 9, 3, 8, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Check_that_Indent_is_NOT_restored // SEE Check_that_Indent_is_restored // Indent, not BOL / matching // sciAlignOpenOnce applied DoSetText('NOT restore Align', [2, ' ;;;{* abc', ' *']); if ParentIndentType = sbitPositionCaret then DoNewLine('after 2nd', 4, 3, 5, 4, [3, ' *', ' *']) // NOT added post indent of 1 else DoNewLine('after 2nd', 4, 3, 5, 4, [3, ' *', ' * ']); // added post indent of 1 end else begin // [sciAddTokenLen] // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 6, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // sciAlignOpenOnce applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 9, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 10, 3, 9, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Check_that_Indent_is_NOT_restored // SEE Check_that_Indent_is_restored // Indent, not BOL / matching // sciAlignOpenOnce applied DoSetText('NOT restore Align', [2, ' ;;;{* abc', ' *']); if ParentIndentType = sbitPositionCaret then DoNewLine('after 2nd', 4, 3, 5, 4, [3, ' *', ' *']) else DoNewLine('after 2nd', 4, 3, 5, 4, [3, ' *', ' * ']); end; PopBaseName; PushBaseName('Etra=" " + Max=10'); ConfigBeautifier(sctBor, [sciAlignOpenOnce, sciAlignOpenSkipBOL] + ExtraIndentFlags, 0, ' ', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 5, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 6, 3, 5, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // sciAlignOpenOnce applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 8, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 9, 3, 8, 4, [3, ' * b', ' * c']); // 3:" * b|c" end else begin // [sciAddTokenLen] // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 6, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // sciAlignOpenOnce applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 9, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 10, 3, 9, 4, [3, ' * b', ' * c']); // 3:" * b|c" end; PopBaseName; PushBaseName('Etra=" " + Max=0'); ConfigBeautifier(sctBor, [] + ExtraIndentFlags, 0, ' ', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 5, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 6, 3, 5, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 10, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 11, 3, 10, 4, [3, ' * b', ' * c']); // 3:" * b|c" end else begin // [sciAddTokenLen] // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 6, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 11, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 12, 3, 11, 4, [3, ' * b', ' * c']); // 3:" * b|c" end; PopBaseName; PopBaseName; {%endregion [] use default indent} {%region [sciNone] } PushBaseName('IndType=[]'); // AnsiIndentFirstLineMax, indent is >= 2, so it is not affeceted by 1 or 2 for LoopIndentMax := 0 to 2 do begin PushBaseName('Max='+IntToStr(LoopIndentMax)); ConfigBeautifier(sctBor, [sciNone] + ExtraIndentFlags, LoopIndentMax, '', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 3, 3, [2, ' {* a', '* bc']); // 2:" {* a|bc" DoNewLine('any line', 4, 3, 3, 4, [3, '* b', '* c']); // 3:"* b|c" // Indent in comment "{ *" / matching DoSetText('"{ *" matching', [2, ' { * abc']); if not (sciAddPastTokenIndent in ExtraIndentFlags) then begin DoNewLine('after 1st', 9, 2, 3, 3, [2, ' { * a', '* bc']); // 2:" { * a|bc" DoNewLine('any line', 4, 3, 3, 4, [3, '* b', '* c']); // 3:"* b|c" end else begin DoNewLine('after 1st', 9, 2, 5, 3, [2, ' { * a', ' * bc']); // 2:" { * a|bc" DoNewLine('any line', 6, 3, 3, 4, [3, ' * b', '* c']); // 3:" * b|c" end; // Indent tabs / matching if ParentIndentType in [sbitCopySpaceTab] then begin DoSetText('tabs matching', [2, #9' {*'#9' abc']); DoNewLine('after 1st', 8, 2, 4, 3, [2, #9' {*'#9' a', '*'#9' bc']); // 2:"_ {*_ a|bc" DoNewLine('any line', 5, 3, 4, 4, [3, '*'#9' b', '*'#9' c']); // 3:" * b|c" end; // Indent, not BOL / matching DoSetText('not BOL matching', [2, ' ;{* abc']); DoNewLine('after 1st', 8, 2, 3, 3, [2, ' ;{* a', '* bc']); // 2:" ;{* a|bc" DoNewLine('any line', 4, 3, 3, 4, [3, '* b', '* c']); // 3:" * b|c" // Indent / NOT matching DoSetText('not matching', [2, ' {+ abc']); if sciApplyIndentForNoMatch in ExtraIndentFlags then begin DoNewLine('after 1st', 7, 2, 1, 3, [2, ' {+ a', 'bc']); // 2:" {+ a|bc" DoNewLine('any line', 2, 3, 1, 4, [3, 'b', 'c']); // 3:" b|c" end else begin DoNewLine('after 1st', 7, 2, 3, 3, [2, ' {+ a', ' bc']); // 2:" {+ a|bc" DoNewLine('any line', 4, 3, 3, 4, [3, ' b', ' c']); // 3:" b|c" end; end else begin // [sciAddTokenLen] // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 4, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 5, 3, 3, 4, [3, ' * b', '* c']); // 3:" * b|c" // Indent in comment "{ *" / matching DoSetText('"{ *" matching', [2, ' { * abc']); if not (sciAddPastTokenIndent in ExtraIndentFlags) then begin DoNewLine('after 1st', 9, 2, 4, 3, [2, ' { * a', ' * bc']); // 2:" { * a|bc" DoNewLine('any line', 5, 3, 3, 4, [3, ' * b', '* c']); // 3:" * b|c" end else begin DoNewLine('after 1st', 9, 2, 6, 3, [2, ' { * a', ' * bc']); // 2:" { * a|bc" DoNewLine('any line', 7, 3, 3, 4, [3, ' * b', '* c']); // 3:" * b|c" end; // Indent tabs / matching if ParentIndentType in [sbitCopySpaceTab] then begin DoSetText('tabs matching', [2, #9' {*'#9' abc']); DoNewLine('after 1st', 8, 2, 5, 3, [2, #9' {*'#9' a', ' *'#9' bc']); // 2:"_ {*_ a|bc" DoNewLine('any line', 6, 3, 4, 4, [3, ' *'#9' b', '*'#9' c']); // 3:" * b|c" end; // Indent, not BOL / matching DoSetText('not BOL matching', [2, ' ;{* abc']); DoNewLine('after 1st', 8, 2, 4, 3, [2, ' ;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 5, 3, 3, 4, [3, ' * b', '* c']); // 3:" * b|c" // Indent / NOT matching DoSetText('not matching', [2, ' {+ abc']); if sciApplyIndentForNoMatch in ExtraIndentFlags then begin DoNewLine('after 1st', 7, 2, 2, 3, [2, ' {+ a', ' bc']); // 2:" {+ a|bc" DoNewLine('any line', 3, 3, 1, 4, [3, ' b', 'c']); // 3:" b|c" end else begin DoNewLine('after 1st', 7, 2, 3, 3, [2, ' {+ a', ' bc']); // 2:" {+ a|bc" DoNewLine('any line', 4, 3, 3, 4, [3, ' b', ' c']); // 3:" b|c" end; end; PopBaseName; end; // LoopIndentMax; PushBaseName('Max='+IntToStr(10)); ConfigBeautifier(sctBor, [sciNone, sciAlignOpenOnce, sciAlignOpenSkipBOL] + ExtraIndentFlags, 0, '', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 3, 3, [2, ' {* a', '* bc']); // 2:" {* a|bc" DoNewLine('any line', 4, 3, 3, 4, [3, '* b', '* c']); // 3:"* b|c" // Indent, not BOL / matching // sciAlignOpenOnce applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 8, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 9, 3, 3, 4, [3, ' * b', '* c']); // 3:" * b|c" end else begin // [sciAddTokenLen] // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 4, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 5, 3, 3, 4, [3, ' * b', '* c']); // 3:" * b|c" // Indent, not BOL / matching // sciAlignOpenOnce applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 9, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 10, 3, 3, 4, [3, ' * b', '* c']); // 3:" * b|c" end; PopBaseName; (* **** PushBaseName('Etra=" " + Max=10'); ConfigBeautifier(sctBor, [sciNone] + ExtraIndentFlags, 10, ' ', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 5, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 6, 3, 5, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 8, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 9, 3, 8, 4, [3, ' * b', ' * c']); // 3:" * b|c" end else begin // [sciAddTokenLen] // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 6, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 9, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 10, 3, 9, 4, [3, ' * b', ' * c']); // 3:" * b|c" end; PopBaseName; PushBaseName('Etra=" " + Max=0'); ConfigBeautifier(sctBor, [sciNone] + ExtraIndentFlags, 0, ' ', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 5, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 6, 3, 5, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 10, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 11, 3, 10, 4, [3, ' * b', ' * c']); // 3:" * b|c" end else begin // [sciAddTokenLen] // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 6, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 11, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 12, 3, 11, 4, [3, ' * b', ' * c']); // 3:" * b|c" end; PopBaseName; **** *) PopBaseName; {%endregion [sciNone] } {%region [sciAlignOpen] } PushBaseName('IndType=[sciAlignOpen]'); PushBaseName('Max='+IntToStr(LoopIndentMax)); ConfigBeautifier(sctBor, [sciAlignOpen] + ExtraIndentFlags, 0, '', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); // most are default, because the opening is at start if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 5, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 6, 3, 5, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent in comment "{ *" / matching DoSetText('"{ *" matching', [2, ' { * abc']); if not (sciAddPastTokenIndent in ExtraIndentFlags) then begin DoNewLine('after 1st', 9, 2, 5, 3, [2, ' { * a', ' * bc']); // 2:" { * a|bc" DoNewLine('any line', 6, 3, 5, 4, [3, ' * b', ' * c']); // 3:" * b|c" end else begin DoNewLine('after 1st', 9, 2, 7, 3, [2, ' { * a', ' * bc']); // 2:" { * a|bc" DoNewLine('any line', 8, 3, 7, 4, [3, ' * b', ' * c']); // 3:" * b|c" end; // Indent tabs / matching if ParentIndentType in [sbitCopySpaceTab] then begin DoSetText('tabs matching', [2, #9' {*'#9' abc']); DoNewLine('after 1st', 8, 2, 6, 3, [2, #9' {*'#9' a', #9' *'#9' bc']); // 2:"_ {*_ a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, #9' *'#9' b', #9' *'#9' c']); // 3:" * b|c" end; // Indent, not BOL / matching DoSetText('not BOL matching', [2, ' ;{* abc']); DoNewLine('after 1st', 8, 2, 6, 3, [2, ' ;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent / NOT matching DoSetText('not matching', [2, ' {+ abc']); DoNewLine('after 1st', 7, 2, 3, 3, [2, ' {+ a', ' bc']); // 2:" {+ a|bc" DoNewLine('any line', 4, 3, 3, 4, [3, ' b', ' c']); // 3:" b|c" end else begin // [sciAddTokenLen] // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 6, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent in comment "{ *" / matching DoSetText('"{ *" matching', [2, ' { * abc']); if not (sciAddPastTokenIndent in ExtraIndentFlags) then begin DoNewLine('after 1st', 9, 2, 6, 3, [2, ' { * a', ' * bc']); // 2:" { * a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" end else begin DoNewLine('after 1st', 9, 2, 8, 3, [2, ' { * a', ' * bc']); // 2:" { * a|bc" DoNewLine('any line', 9, 3, 8, 4, [3, ' * b', ' * c']); // 3:" * b|c" end; // Indent tabs / matching if ParentIndentType in [sbitCopySpaceTab] then begin DoSetText('tabs matching', [2, #9' {*'#9' abc']); DoNewLine('after 1st', 8, 2, 7, 3, [2, #9' {*'#9' a', #9' *'#9' bc']); // 2:"_ {*_ a|bc" DoNewLine('any line', 8, 3, 7, 4, [3, #9' *'#9' b', #9' *'#9' c']); // 3:" * b|c" end; // Indent, not BOL / matching DoSetText('not BOL matching', [2, ' ;{* abc']); DoNewLine('after 1st', 8, 2, 7, 3, [2, ' ;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 8, 3, 7, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent / NOT matching DoSetText('not matching', [2, ' {+ abc']); if sciApplyIndentForNoMatch in ExtraIndentFlags then begin DoNewLine('after 1st', 7, 2, 4, 3, [2, ' {+ a', ' bc']); // 2:" {+ a|bc" DoNewLine('any line', 5, 3, 4, 4, [3, ' b', ' c']); // 3:" b|c" end else begin DoNewLine('after 1st', 7, 2, 3, 3, [2, ' {+ a', ' bc']); // 2:" {+ a|bc" DoNewLine('any line', 4, 3, 3, 4, [3, ' b', ' c']); // 3:" b|c" end; end; PopBaseName; PushBaseName('Max='+IntToStr(10)); ConfigBeautifier(sctBor, [sciAlignOpen] + ExtraIndentFlags, 10, '', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 5, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 6, 3, 5, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 8, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 9, 3, 8, 4, [3, ' * b', ' * c']); // 3:" * b|c" end else begin // [sciAddTokenLen] // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 6, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 9, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 10, 3, 9, 4, [3, ' * b', ' * c']); // 3:" * b|c" end; PopBaseName; PushBaseName('Max='+IntToStr(4)); // actually cut off at 4 ConfigBeautifier(sctBor, [sciAlignOpen] + ExtraIndentFlags, 4, '', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 7, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 8, 3, 7, 4, [3, ' * b', ' * c']); // 3:" * b|c" end else begin // [sciAddTokenLen] // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 8, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 9, 3, 8, 4, [3, ' * b', ' * c']); // 3:" * b|c" end; PopBaseName; (* **** PushBaseName('Etra=" " + Max=10'); ConfigBeautifier(sctBor, [sciAlignOpen] + ExtraIndentFlags, 10, ' ', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 5, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 6, 3, 5, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 8, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 9, 3, 8, 4, [3, ' * b', ' * c']); // 3:" * b|c" end else begin // [sciAddTokenLen] // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 6, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 9, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 10, 3, 9, 4, [3, ' * b', ' * c']); // 3:" * b|c" end; PopBaseName; PushBaseName('Etra=" " + Max=0'); ConfigBeautifier(sctBor, [sciAlignOpen] + ExtraIndentFlags, 0, ' ', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 5, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 6, 3, 5, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 10, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 11, 3, 10, 4, [3, ' * b', ' * c']); // 3:" * b|c" end else begin // [sciAddTokenLen] // Indent / matching DoSetText('matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 6, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoNewLine('any line', 7, 3, 6, 4, [3, ' * b', ' * c']); // 3:" * b|c" // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('not BOL matching', [2, ' ;;;{* abc']); DoNewLine('after 1st', 10, 2, 11, 3, [2, ' ;;;{* a', ' * bc']); // 2:" ;{* a|bc" DoNewLine('any line', 12, 3, 11, 4, [3, ' * b', ' * c']); // 3:" * b|c" end; PopBaseName; **** *) // Check_that_Indent_is_restored SEE Check_that_Indent_is_NOT_restored PushBaseName('Max='+IntToStr(10)); ConfigBeautifier(sctBor, [sciAlignOpen] + ExtraIndentFlags, 0, '', sccPrefixMatch, scmMatchAfterOpening, MatchLine, sbitSpace, '^\s*\*', '*'); if not (sciAddTokenLen in ExtraIndentFlags) then begin // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('restore Align', [2, ' ;;;{* abc', ' *']); if ParentIndentType = sbitPositionCaret then DoNewLine('after 2nd', 4, 3, 8, 4, [3, ' *', ' *']) else DoNewLine('after 2nd', 4, 3, 8, 4, [3, ' *', ' * ']); DoSetText('restore Align', [2, ' ;;;{* abc', ' *', ' *']); if ParentIndentType = sbitPositionCaret then DoNewLine('any line', 4, 4, 8, 5, [4, ' *', ' *']) else DoNewLine('any line', 4, 4, 8, 5, [4, ' *', ' * ']); end else begin // [sciAddTokenLen] // Indent, not BOL / matching // AnsiIndentFirstLineMax applied DoSetText('restore Align', [2, ' ;;;{* abc', ' *']); if ParentIndentType = sbitPositionCaret then DoNewLine('after 2nd', 4, 3, 9, 4, [3, ' *', ' *']) else DoNewLine('after 2nd', 4, 3, 9, 4, [3, ' *', ' * ']); DoSetText('restore Align', [2, ' ;;;{* abc', ' *', ' *']); if ParentIndentType = sbitPositionCaret then DoNewLine('any line', 4, 4, 9, 5, [4, ' *', ' *']) else DoNewLine('any line', 4, 4, 9, 5, [4, ' *', ' * ']); end; PopBaseName; PopBaseName; {%endregion [sciAlignOpen] } PopBaseName; end; //LoopIndentAdds PopBaseName; PopBaseName; end; // LoopIndentApplyNoMatch PopBaseName; end; // LoopMatchFirst PopBaseName; end; Beautifier.IndentType := sbitCopySpaceTab; PopBaseName; {%endregion Bor (Curly) --- Pre-PerfixIndent } // sccNoPrefix; ConfigBeautifier(sctBor, [sciAddTokenLen], 0, '', sccNoPrefix, scmMatchAfterOpening, sclMatchPrev, sbitSpace, '^\s*\*', '*'); DoSetText('sccNoPrefix; matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 4, 3, [2, ' {* a', ' bc']); // 2:" {* a|bc" DoSetText('sccNoPrefix; NOT matching', [2, ' {+ abc']); DoNewLine('after 1st', 7, 2, 3, 3, [2, ' {+ a', ' bc']); // 2:" {* a|bc" Beautifier.BorIndentMode := [sciAddTokenLen, sciApplyIndentForNoMatch]; DoSetText('sccNoPrefix; NOT matching, apply', [2, ' {+ abc']); DoNewLine('after 1st', 7, 2, 4, 3, [2, ' {+ a', ' bc']); // 2:" {* a|bc" // sccPrefixMatch; ConfigBeautifier(sctBor, [sciAddTokenLen], 0, '', sccPrefixMatch, scmMatchAfterOpening, sclMatchPrev, sbitSpace, '^\s*\*', '*'); DoSetText('sccPrefixMatch; matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 6, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoSetText('sccPrefixMatch; NOT matching', [2, ' {+ abc']); DoNewLine('after 1st', 7, 2, 3, 3, [2, ' {+ a', ' bc']); // 2:" {* a|bc" Beautifier.BorIndentMode := [sciAddTokenLen, sciApplyIndentForNoMatch]; DoSetText('sccPrefixMatch; NOT matching, apply', [2, ' {+ abc']); DoNewLine('after 1st', 7, 2, 4, 3, [2, ' {+ a', ' bc']); // 2:" {* a|bc" // sccPrefixAlways; ConfigBeautifier(sctBor, [sciAddTokenLen], 0, '', sccPrefixAlways, scmMatchAfterOpening, sclMatchPrev, sbitSpace, '^\s*\*', '*'); DoSetText('sccPrefixAlways; matching', [2, ' {* abc']); DoNewLine('after 1st', 7, 2, 6, 3, [2, ' {* a', ' * bc']); // 2:" {* a|bc" DoSetText('sccPrefixAlways; NOT matching', [2, ' {+ abc']); DoNewLine('after 1st', 7, 2, 5, 3, [2, ' {+ a', ' *bc']); // 2:" {* a|bc" Beautifier.BorIndentMode := [sciAddTokenLen, sciApplyIndentForNoMatch]; DoSetText('sccPrefixAlways; NOT matching, apply', [2, ' {+ abc']); DoNewLine('after 1st', 7, 2, 5, 3, [2, ' {+ a', ' *bc']); // 2:" {* a|bc" {%endregion Bor (Curly) } {%region Ansi ( * } ConfigBeautifier(sctAnsi, [sciAddTokenLen, sciAddPastTokenIndent], 0, '', sccPrefixMatch, scmMatchAfterOpening, sclMatchPrev, sbitSpace, '^ ?\*', '*'); DoSetText('Ansi 2', [2, ' (* * ']); DoNewLine('', 8, 2, 8, 3, [2, ' (* * ', ' * ']); // 2:" (* * |" DoSetText('Ansi 3', [2, ' (* *']); DoNewLine('', 7, 2, 7, 3, [2, ' (* *', ' *']); // 2:" (* *|" ConfigBeautifier(sctAnsi, [sciAddTokenLen], 0, '', sccPrefixMatch, scmMatchAtAsterisk, sclMatchPrev, sbitSpace, '^\*', '*'); DoSetText('Ansi 1', [2, ' (* abc']); DoNewLine('', 7, 2, 6, 3, [2, ' (* a', ' * bc']); // 2:" (* a|bc" // scmMatchAfterOpening ConfigBeautifier(sctAnsi, [sciAddTokenLen, sciAddPastTokenIndent], 0, '', sccPrefixMatch, scmMatchAfterOpening, sclMatchPrev, sbitSpace, '^\+', '+'); DoSetText('scmMatchAfterOpening match Ansi', [2, ' (*+ ab']); DoNewLine('', 8, 2, 7, 3, [2, ' (*+ a', ' + b']); // 2:" (*+ a|b" DoSetText('scmMatchAfterOpening NO match Ansi', [2, ' (*- ab']); DoNewLine('', 8, 2, 3, 3, [2, ' (*- a', ' b']); // 2:" (*+ a|b" // scmMatchOpening ConfigBeautifier(sctAnsi, [sciAddTokenLen, sciAddPastTokenIndent], 0, '', sccPrefixMatch, scmMatchOpening, sclMatchPrev, sbitSpace, '^\(\*\+', '+'); DoSetText('scmMatchOpening match Ansi', [2, ' (*+ ab']); DoNewLine('', 8, 2, 7, 3, [2, ' (*+ a', ' + b']); // 2:" (*+ a|b" DoSetText('scmMatchOpening NO match Ansi', [2, ' (*- ab']); DoNewLine('', 8, 2, 3, 3, [2, ' (*- a', ' b']); // 2:" (*+ a|b" // scmMatchWholeLine; ConfigBeautifier(sctAnsi, [sciAddTokenLen, sciAddPastTokenIndent], 0, '', sccPrefixMatch, scmMatchWholeLine, sclMatchPrev, sbitSpace, '^ \(\*\+', '+'); DoSetText('scmMatchWholeLine; match Ansi', [2, ' (*+ ab']); DoNewLine('', 8, 2, 7, 3, [2, ' (*+ a', ' + b']); // 2:" (*+ a|b" DoSetText('scmMatchWholeLine; NO match Ansi', [2, ' (*- ab']); DoNewLine('', 8, 2, 3, 3, [2, ' (*- a', ' b']); // 2:" (*+ a|b" // scmMatchAtAsterisk; ConfigBeautifier(sctAnsi, [sciAddTokenLen, sciAddPastTokenIndent], 0, '', sccPrefixMatch, scmMatchAtAsterisk, sclMatchPrev, sbitSpace, '^\*\+', '*+'); DoSetText('scmMatchAtAsterisk; match Ansi', [2, ' (*+ ab']); DoNewLine('', 8, 2, 7, 3, [2, ' (*+ a', ' *+ b']); // 2:" (*+ a|b" DoSetText('scmMatchAtAsterisk; NO match Ansi', [2, ' (*- ab']); DoNewLine('', 8, 2, 3, 3, [2, ' (*- a', ' b']); // 2:" (*+ a|b" {%endregion Ansi ( * } {%region Slash // } ConfigBeautifier(sctSlash, [sciAddTokenLen, sciAddPastTokenIndent], 0, '', sccPrefixMatch, scmMatchAfterOpening, sclMatchPrev, sbitSpace, '^ ?\*', '*', sceMatching); DoSetText('Slash No match', [2, ' // abc']); DoNewLine('', 7, 2, 3, 3, [2, ' // a', ' bc']); // 2:" // a|bc" DoSetText('Slash ', [2, ' // * abc']); DoNewLine('', 9, 2, 8, 3, [2, ' // * a', ' // * bc']); // 2:" // * a|bc" ConfigBeautifier(sctSlash, [sciAddTokenLen, sciAddPastTokenIndent], 0, '', sccPrefixMatch, scmMatchAfterOpening, sclMatchPrev, sbitSpace, '^ ?\*', '*', sceSplitLine); DoSetText('Slash No match, EOL', [2, ' // abc']); DoNewLine('', 9, 2, 3, 3, [2, ' // abc', ' ']); // 2:" // abc|" DoSetText('Slash No match, past EOL', [2, ' // abc']); DoNewLine('', 10, 2, 3, 3, [2, ' // abc', ' ']); // 2:" // abc |" DoSetText('Slash No match, split', [2, ' // abc']); DoNewLine('', 7, 2, 6, 3, [2, ' // a', ' // bc']); // 2:" // a|bc" // aligOpen (single and multiline) ConfigBeautifier(sctSlash, [sciAlignOpen, sciAddTokenLen, sciAddPastTokenIndent], 0, '', sccPrefixAlways, scmMatchAfterOpening, sclMatchPrev, sbitSpace, '^.?', '', sceAlways); DoSetText('Slash sciAlignOpen', [2, ' ;;; // abc']); DoNewLine('first', 11, 2, 10, 3, [2, ' ;;; // a', ' // bc']); // 2:" // a|bc" DoNewLine('any', 11, 3, 10, 4, [3, ' // b', ' // c']); // 2:" // b|c" //DoSetText('Slash sciAlignOpen realign', [2, ' ;;; // abc', ' // de']); //DoNewLine('2nd', 9, 3, 10, 4, [3, ' // d', ' // e']); // 3:" // d|e" // //DoSetText('Slash sciAlignOpen realign', [2, ' ;;; // abc', ' //', ' // de']); //DoNewLine('3rd', 9, 4, 10, 5, [4, ' // d', ' // e']); // 3:" // d|e" {%endregion Slash // } finally SynEdit.Beautifier := nil; FreeAndNil(Beautifier); end; // TODO: extra/smart indent only allowed, if MATCHED end; initialization RegisterTest(TTestSynBeautifier); end.