mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 21:38:27 +02:00
IDE, SynEdit: Add indent for column mode selection
This commit is contained in:
parent
3c2da22f52
commit
c07ae4c10e
@ -645,8 +645,10 @@ type
|
||||
procedure SetVisibleSpecialChars(AValue: TSynVisibleSpecialChars);
|
||||
procedure SurrenderPrimarySelection;
|
||||
procedure ComputeCaret(X, Y: Integer);
|
||||
procedure DoBlockIndent;
|
||||
procedure DoBlockUnindent;
|
||||
procedure DoBlockIndent(AColumnIndentOutside: Boolean = False);
|
||||
procedure DoBlockIndentColSel(AnIndentOutside: Boolean = False);
|
||||
procedure DoBlockUnindent(AColumnIndentOutside: Boolean = False);
|
||||
procedure DoBlockUnindentColSel(AnIndentOutside: Boolean = False);
|
||||
procedure DoHomeKey(aMode: TSynHomeMode = synhmDefault);
|
||||
procedure DoEndKey;
|
||||
procedure DoTabKey;
|
||||
@ -7718,10 +7720,10 @@ begin
|
||||
FCaret.LineBytePos := FBlockSelection.LastLineBytePos;
|
||||
end;
|
||||
|
||||
ecBlockIndent:
|
||||
if not ReadOnly then DoBlockIndent;
|
||||
ecBlockUnindent:
|
||||
if not ReadOnly then DoBlockUnindent;
|
||||
ecBlockIndent, ecBlockIndentMove:
|
||||
if not ReadOnly then DoBlockIndent(Command = ecBlockIndentMove);
|
||||
ecBlockUnindent, ecBlockUnindentMove:
|
||||
if not ReadOnly then DoBlockUnindent(Command = ecBlockUnindentMove);
|
||||
ecNormalSelect,
|
||||
ecColumnSelect,
|
||||
ecLineSelect:
|
||||
@ -9086,13 +9088,18 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCustomSynEdit.DoBlockIndent;
|
||||
procedure TCustomSynEdit.DoBlockIndent(AColumnIndentOutside: Boolean);
|
||||
var
|
||||
BB,BE : TPoint;
|
||||
Line : PChar;
|
||||
Len, e, y: integer;
|
||||
Spaces, Tabs: String;
|
||||
begin
|
||||
if SelAvail and (SelectionMode = smColumn) then begin
|
||||
DoBlockIndentColSel(AColumnIndentOutside);
|
||||
exit;
|
||||
end;
|
||||
|
||||
IncPaintLock;
|
||||
FBlockSelection.IncPersistentLock;
|
||||
try
|
||||
@ -9135,7 +9142,88 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCustomSynEdit.DoBlockUnindent;
|
||||
procedure TCustomSynEdit.DoBlockIndentColSel(AnIndentOutside: Boolean);
|
||||
var
|
||||
BB,BE, BB2, BE2: TPoint;
|
||||
Len, y, LeftBytePos, RightBytePos: integer;
|
||||
LineStr, TabStr, SpaceStr: String;
|
||||
Bounds: array of record
|
||||
LeftByte, RightByte: integer;
|
||||
end;
|
||||
begin
|
||||
if not (SelAvail and (SelectionMode = smColumn)) then
|
||||
exit;
|
||||
if (FBlockIndent <= 0) and (FBlockTabIndent <= 0) then
|
||||
exit;
|
||||
|
||||
IncPaintLock;
|
||||
try
|
||||
// build text to insert
|
||||
BB := BlockBegin;
|
||||
BE := BlockEnd;
|
||||
BB2 := FBlockSelection.StartLineBytePos;
|
||||
BE2 := FBlockSelection.EndLineBytePos;
|
||||
|
||||
SetLength(Bounds, BE.Y - BB.Y + 1);
|
||||
for y := BB.Y to BE.y do begin
|
||||
Bounds[y-BB.y].LeftByte := FBlockSelection.ColumnStartBytePos[y];
|
||||
Bounds[y-BB.y].RightByte := FBlockSelection.ColumnEndBytePos[y];
|
||||
end;
|
||||
FBlockSelection.Clear;
|
||||
|
||||
SpaceStr := StringOfChar(#32, FBlockIndent);
|
||||
TabStr := StringOfChar( #9, FBlockTabIndent);
|
||||
for y := BB.Y to BE.y do begin
|
||||
LineStr := FTheLinesView[y - 1];
|
||||
LeftBytePos := Bounds[y-BB.y].LeftByte;
|
||||
if AnIndentOutside then begin
|
||||
Len := CountBackwardWhiteSpace(PChar(LineStr), LeftBytePos-1);
|
||||
RightBytePos := LeftBytePos;
|
||||
LeftBytePos := LeftBytePos - Len;
|
||||
if FBlockIndent = 0 then
|
||||
Len := 0;
|
||||
end
|
||||
else begin
|
||||
if FBlockIndent > 0
|
||||
then Len := CountLeadWhiteSpace(PChar(LineStr)+LeftBytePos-1)
|
||||
else Len := 0;
|
||||
if (Len > 0) then
|
||||
RightBytePos := Bounds[y-BB.y].RightByte;
|
||||
if (Len > 0) and (LeftBytePos + Len > RightBytePos) then
|
||||
Len := Max(0, RightBytePos - LeftBytePos);
|
||||
end;
|
||||
|
||||
if Len = 0 then begin
|
||||
FTheLinesView.EditInsert(LeftBytePos, y, TabStr+SpaceStr);
|
||||
end
|
||||
else begin
|
||||
FTheLinesView.EditInsert(LeftBytePos + Len, y, SpaceStr);
|
||||
if TabStr <> '' then
|
||||
FTheLinesView.EditInsert(LeftBytePos, y, TabStr);
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
FTrimmedLinesView.ForceTrim; // Otherwise it may reset the block
|
||||
|
||||
if AnIndentOutside then begin
|
||||
BB2.X := BB2.X + Length(TabStr) + Length(SpaceStr);
|
||||
BE2.X := BE2.X + Length(TabStr) + Length(SpaceStr);
|
||||
end
|
||||
else begin
|
||||
if BB2.X > BE2.X then
|
||||
BB2.X := BB2.X + Length(TabStr) + Length(SpaceStr)
|
||||
else
|
||||
BE2.X := BE2.X + Length(TabStr) + Length(SpaceStr);
|
||||
end;
|
||||
FBlockSelection.StartLineBytePos := BB2;
|
||||
FBlockSelection.EndLineBytePos := BE2;
|
||||
FBlockSelection.ActiveSelectionMode := smColumn;
|
||||
FCaret.LineBytePos := BE2;
|
||||
DecPaintLock;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCustomSynEdit.DoBlockUnindent(AColumnIndentOutside: Boolean);
|
||||
const
|
||||
LineEnd = #10;
|
||||
var
|
||||
@ -9147,6 +9235,11 @@ var
|
||||
SomethingDeleted : Boolean;
|
||||
HasTab: Boolean;
|
||||
begin
|
||||
if SelAvail and (SelectionMode = smColumn) then begin
|
||||
DoBlockUnindentColSel(AColumnIndentOutside);
|
||||
exit;
|
||||
end;
|
||||
|
||||
if not SelAvail then begin
|
||||
BB := CaretXY;
|
||||
BE := CaretXY;
|
||||
@ -9257,6 +9350,165 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCustomSynEdit.DoBlockUnindentColSel(AnIndentOutside: Boolean);
|
||||
var
|
||||
BB,BE, BB2, BE2: TPoint;
|
||||
Len, y, LeftBytePos, TabW, TabDel, CurTabDel, CurTabSpaceAdd,
|
||||
SpaceStartCharPos, CurSpaceSpaceAdd, CurSpaceDel, CurSpaceDelPos: integer;
|
||||
LineStr: String;
|
||||
LeftCharPos, RightBytePos, TabEndBytePos: LongInt;
|
||||
Bounds: array of record
|
||||
LeftByte, RightByte: integer;
|
||||
// LeftChar, RightchByte: integer;
|
||||
end;
|
||||
LPC: TSynLogicalPhysicalConvertor;
|
||||
BbIsRight: Boolean;
|
||||
|
||||
function LogToPhys(X: Integer): integer; inline;
|
||||
begin
|
||||
Result := LPC.LogicalToPhysical(ToIdx(y), X);
|
||||
end;
|
||||
function PhysToLog(X: Integer): integer; inline;
|
||||
begin
|
||||
Result := LPC.PhysicalToLogical(ToIdx(y), X);
|
||||
end;
|
||||
function PhysToLog(X: Integer; out Offs: integer): integer; inline;
|
||||
begin
|
||||
Result := LPC.PhysicalToLogical(ToIdx(y), X, Offs, cspDefault, []);
|
||||
end;
|
||||
|
||||
begin
|
||||
if not (SelAvail and (SelectionMode = smColumn)) then
|
||||
exit;
|
||||
if (FBlockIndent <= 0) and (FBlockTabIndent <= 0) then
|
||||
exit;
|
||||
|
||||
IncPaintLock;
|
||||
try
|
||||
TabW := TabWidth;
|
||||
TabDel := TabW * FBlockTabIndent;
|
||||
BB := BlockBegin;
|
||||
BE := BlockEnd;
|
||||
BB2 := FBlockSelection.StartLineBytePos;
|
||||
BE2 := FBlockSelection.EndLineBytePos;
|
||||
BbIsRight := BB2.X > BE2.X;
|
||||
|
||||
SetLength(Bounds, BE.Y - BB.Y + 1);
|
||||
for y := BB.Y to BE.y do begin
|
||||
Bounds[y-BB.y].LeftByte := FBlockSelection.ColumnStartBytePos[y];
|
||||
Bounds[y-BB.y].RightByte := FBlockSelection.ColumnEndBytePos[y];
|
||||
end;
|
||||
FBlockSelection.Clear;
|
||||
|
||||
|
||||
LPC := FTheLinesView.LogPhysConvertor;
|
||||
for y := BB.Y to BE.y do begin
|
||||
LineStr := FTheLinesView[y - 1];
|
||||
LeftBytePos := Bounds[y-BB.y].LeftByte;
|
||||
RightBytePos := Bounds[y-BB.y].RightByte;
|
||||
|
||||
if AnIndentOutside then begin
|
||||
Len := CountBackwardWhiteSpace(PChar(LineStr), LeftBytePos-1);
|
||||
RightBytePos := LeftBytePos;
|
||||
LeftBytePos := LeftBytePos - Len;
|
||||
//if FBlockIndent = 0 then
|
||||
// Len := 0;
|
||||
end
|
||||
else begin
|
||||
Len := Min(CountLeadWhiteSpace(PChar(LineStr)+LeftBytePos-1), RightBytePos - LeftBytePos);
|
||||
if Len > Length(LineStr) - LeftBytePos then
|
||||
Len := Length(LineStr) - LeftBytePos;
|
||||
end;
|
||||
|
||||
if Len = 0 then
|
||||
Continue;
|
||||
|
||||
CurSpaceDel := 0;
|
||||
CurSpaceDelPos := LeftBytePos + Len; // used as end for tab // pretend zero spaces
|
||||
CurSpaceSpaceAdd := 0;
|
||||
CurTabDel := 0;
|
||||
CurTabSpaceAdd := 0;
|
||||
|
||||
if FBlockIndent > 0 then begin
|
||||
//SpaceStartCharPos := LogToPhys(Max(LeftBytePos, CurSpaceDelPos - FBlockIndent));
|
||||
SpaceStartCharPos := Max( LogToPhys(CurSpaceDelPos) - FBlockIndent,
|
||||
LogToPhys(LeftBytePos)
|
||||
);
|
||||
CurSpaceDelPos := PhysToLog(SpaceStartCharPos, CurSpaceSpaceAdd);
|
||||
CurSpaceDel := LeftBytePos + Len - CurSpaceDelPos;
|
||||
end;
|
||||
|
||||
if (CurSpaceDel > Len) or
|
||||
( (CurSpaceDel = Len) and (CurSpaceSpaceAdd = 0) )
|
||||
then begin
|
||||
CurSpaceDelPos := LeftBytePos;
|
||||
CurSpaceDel := Len;
|
||||
CurSpaceSpaceAdd := 0;
|
||||
end
|
||||
|
||||
else
|
||||
begin
|
||||
Len := CurSpaceDelPos - LeftBytePos;
|
||||
if TabDel > 0 then begin
|
||||
LeftCharPos := LogToPhys(LeftBytePos);
|
||||
CurTabDel := TabDel - (LeftCharPos-1) mod TabW;
|
||||
if CurTabDel > 0 then begin
|
||||
TabEndBytePos := PhysToLog(LeftCharPos+CurTabDel, CurTabSpaceAdd);
|
||||
CurTabDel := TabEndBytePos - LeftBytePos;
|
||||
if CurTabSpaceAdd > 0 then
|
||||
inc(CurTabDel);
|
||||
|
||||
if (CurTabDel > Len) or
|
||||
( (CurTabDel=Len) and (CurTabSpaceAdd >= CurSpaceSpaceAdd) )
|
||||
then begin
|
||||
CurTabDel := 0;
|
||||
CurTabSpaceAdd := 0;
|
||||
CurSpaceDelPos := LeftBytePos;
|
||||
CurSpaceDel := Len;
|
||||
CurSpaceSpaceAdd := 0;
|
||||
end;
|
||||
end
|
||||
else
|
||||
CurTabDel := 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
if CurSpaceDel > 0 then begin
|
||||
FTheLinesView.EditDelete(CurSpaceDelPos, y, CurSpaceDel);
|
||||
if CurSpaceSpaceAdd > 0 then
|
||||
FTheLinesView.EditInsert(CurSpaceDelPos, y, StringOfChar(' ', CurSpaceSpaceAdd));
|
||||
end;
|
||||
if CurTabDel > 0 then begin
|
||||
FTheLinesView.EditDelete(LeftBytePos, y, CurTabDel);
|
||||
if CurTabSpaceAdd > 0 then
|
||||
FTheLinesView.EditInsert(LeftBytePos, y, StringOfChar(' ', CurTabSpaceAdd));
|
||||
end;
|
||||
|
||||
if AnIndentOutside then begin
|
||||
if (y = BB2.y) then
|
||||
BB2.X := BB2.X + CurSpaceSpaceAdd + CurTabSpaceAdd - CurSpaceDel - CurTabDel;
|
||||
if (y = BE2.y) then
|
||||
BE2.X := BE2.X + CurSpaceSpaceAdd + CurTabSpaceAdd - CurSpaceDel - CurTabDel;
|
||||
end
|
||||
else begin
|
||||
if (y = BB2.y) and (BbIsRight) then
|
||||
BB2.X := BB2.X + CurSpaceSpaceAdd + CurTabSpaceAdd - CurSpaceDel - CurTabDel;
|
||||
if (y = BE2.y) and (not BbIsRight) then
|
||||
BE2.X := BE2.X + CurSpaceSpaceAdd + CurTabSpaceAdd - CurSpaceDel - CurTabDel;
|
||||
end;
|
||||
|
||||
end;
|
||||
finally
|
||||
FTrimmedLinesView.ForceTrim; // Otherwise it may reset the block
|
||||
|
||||
FBlockSelection.StartLineBytePos := BB2;
|
||||
FBlockSelection.EndLineBytePos := BE2;
|
||||
FBlockSelection.ActiveSelectionMode := smColumn;
|
||||
FCaret.LineBytePos := BE2;
|
||||
DecPaintLock;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCustomSynEdit.DoHomeKey(aMode: TSynHomeMode = synhmDefault);
|
||||
// jump to start of line (x=1),
|
||||
// or if already there, jump to first non blank char
|
||||
|
@ -294,6 +294,8 @@ const
|
||||
ecString = 640; //Insert a whole string
|
||||
|
||||
ecAutoCompletion = 650;
|
||||
ecBlockIndentMove = 651; // Indent selection (indent before column-sel, therefore moving the column-sel)
|
||||
ecBlockUnindentMove = 652; // Unindent selection (indent before column-sel, therefore moving the column-sel)
|
||||
|
||||
ecGotFocus = 700;
|
||||
ecLostFocus = 701;
|
||||
@ -514,7 +516,7 @@ end;
|
||||
{ Command mapping routines }
|
||||
|
||||
const
|
||||
EditorCommandStrs: array[0..172] of TIdentMapEntry = (
|
||||
EditorCommandStrs: array[0..174] of TIdentMapEntry = (
|
||||
(Value: ecNone; Name: 'ecNone'),
|
||||
(Value: ecLeft; Name: 'ecLeft'),
|
||||
(Value: ecRight; Name: 'ecRight'),
|
||||
@ -627,6 +629,8 @@ const
|
||||
(Value: ecToggleMode; Name: 'ecToggleMode'),
|
||||
(Value: ecBlockIndent; Name: 'ecBlockIndent'),
|
||||
(Value: ecBlockUnindent; Name: 'ecBlockUnindent'),
|
||||
(Value: ecBlockIndentMove; Name: 'ecBlockIndentMove'),
|
||||
(Value: ecBlockUnindentMove; Name: 'ecBlockUnindentMove'),
|
||||
(Value: ecTab; Name: 'ecTab'),
|
||||
(Value: ecShiftTab; Name: 'ecShiftTab'),
|
||||
(Value: ecMatchBracket; Name: 'ecMatchBracket'),
|
||||
|
@ -83,6 +83,7 @@ function YToIdx(APointWithYPos: TPoint): TPoint; inline;
|
||||
function YToPos(APointWithYIdx: TPoint): TPoint; inline;
|
||||
|
||||
function CountLeadWhiteSpace(AText: PChar): integer; inline;
|
||||
function CountBackwardWhiteSpace(AText: PChar; AStart: Integer): integer; inline;
|
||||
function CountLeadWhiteSpace(AText: PChar; out AnHasTab: boolean): integer; inline;
|
||||
|
||||
|
||||
@ -120,6 +121,16 @@ begin
|
||||
Result := Run - AText;
|
||||
end;
|
||||
|
||||
function CountBackwardWhiteSpace(AText: PChar; AStart: Integer): integer;
|
||||
var
|
||||
Run : PChar;
|
||||
begin
|
||||
Run := AText+AStart-1;
|
||||
while (Run >=AText) and (Run^ in [' ', #9]) do
|
||||
Dec(Run);
|
||||
Result := AText + AStart - 1 - Run;
|
||||
end;
|
||||
|
||||
function CountLeadWhiteSpace(AText: PChar; out AnHasTab: boolean): integer;
|
||||
var
|
||||
Run : PChar;
|
||||
|
@ -165,6 +165,7 @@ type
|
||||
property MainCaretIndex: Integer read GetMainCaretIndex;
|
||||
|
||||
private
|
||||
FAdjustOnlyAfterInsertColumn: Boolean;
|
||||
FCurrenCaret, FBeforeNextCaret: PCaretData;
|
||||
FIterationDoneCount: Integer;
|
||||
FLowCaret, FHighCaret: PCaretData; // used in AdjustAfterChange
|
||||
@ -186,6 +187,7 @@ type
|
||||
function PeekCaretY(AIndexOffset: Integer): Integer; inline;
|
||||
function PeekCaretFull(AIndexOffset: Integer): TLogCaretPoint; inline;
|
||||
//procedure AbortIterator;
|
||||
property AdjustOnlyAfterInsertColumn: Boolean read FAdjustOnlyAfterInsertColumn write FAdjustOnlyAfterInsertColumn;
|
||||
|
||||
property CurrentCaretFull: TLogCaretPoint read GetCurrentCaretFull write SetCurrentCaretFull;
|
||||
property CurrentCaretKeepX: Integer read GetCurrentCaretKeepX write SetCurrentCaretKeepX;
|
||||
@ -1028,7 +1030,9 @@ begin
|
||||
end
|
||||
else begin // aCount >= 0
|
||||
for i := lowest to FHighIndex do begin
|
||||
if (FCarets[i].y = aLinePos) and (FCarets[i].x >= aBytePos) then
|
||||
if (FCarets[i].y = aLinePos) and (FCarets[i].x >= aBytePos) and
|
||||
((not FAdjustOnlyAfterInsertColumn) or (FCarets[i].x > aBytePos))
|
||||
then
|
||||
FCarets[i].x := FCarets[i].x + aCount
|
||||
else
|
||||
break;
|
||||
@ -2572,6 +2576,18 @@ var
|
||||
begin
|
||||
// hcfFinish
|
||||
if AfterProcessing then begin
|
||||
case Command of
|
||||
ecTab..ecShiftTab:
|
||||
if (not Editor.ReadOnly) and Editor.SelAvail and (eoTabIndent in Editor.Options) and
|
||||
(SelectionObj.ActiveSelectionMode = smColumn)
|
||||
then
|
||||
ClearCarets;
|
||||
ecBlockIndent, ecBlockUnindent, ecBlockIndentMove, ecBlockUnindentMove:
|
||||
if (not Editor.ReadOnly) and Editor.SelAvail and (SelectionObj.ActiveSelectionMode = smColumn)
|
||||
then
|
||||
ClearCarets;
|
||||
end;
|
||||
|
||||
if (FNestedCommandProcessor > 0) then begin
|
||||
dec(FNestedCommandProcessor);
|
||||
exit;
|
||||
@ -2682,25 +2698,21 @@ begin
|
||||
StartEditing;
|
||||
if Editor.ReadOnly then exit;
|
||||
if (eoTabIndent in Editor.Options) and Editor.SelAvail then begin
|
||||
if (SelectionObj.ActiveSelectionMode = smColumn) then begin
|
||||
// no indent for column mode, when multicaret
|
||||
Editor.BeginUpdate(True);
|
||||
try
|
||||
AddCaret(Editor.LogicalCaretXY.x, Editor.CaretY, CaretObj.BytePosOffset, [cfMainCaret, cfNoneVisual, cfAddDuplicate]);
|
||||
Editor.SelText := '';
|
||||
if Carets.MainCaretIndex >= 0 then begin
|
||||
Editor.LogicalCaretXY := Carets.Caret[Carets.MainCaretIndex];
|
||||
RemoveCaret(Carets.MainCaretIndex);
|
||||
end
|
||||
else
|
||||
assert(False, 'TSynCustomPluginMultiCaret.ProcessAllSynCommand: Maincaret index not found');
|
||||
ExecCommandRepeated;
|
||||
finally
|
||||
Editor.EndUpdate;
|
||||
end;
|
||||
end
|
||||
else // exec once and adjust
|
||||
exit;
|
||||
if (SelectionObj.ActiveSelectionMode = smColumn) then
|
||||
ClearCarets;
|
||||
exit;
|
||||
end
|
||||
else
|
||||
ExecCommandRepeated;
|
||||
end;
|
||||
ecBlockIndent, ecBlockUnindent, ecBlockIndentMove, ecBlockUnindentMove:
|
||||
begin
|
||||
StartEditing;
|
||||
if Editor.ReadOnly then exit;
|
||||
if Editor.SelAvail then begin
|
||||
if (SelectionObj.ActiveSelectionMode = smColumn) then
|
||||
ClearCarets;
|
||||
exit;
|
||||
end
|
||||
else
|
||||
ExecCommandRepeated;
|
||||
|
@ -149,15 +149,17 @@ type
|
||||
procedure TestIsCaretLogAndFullText(Name: String; X, Y, Offs: Integer; Lines: Array of String; Repl: Array of const); // logical caret
|
||||
end;
|
||||
|
||||
function MyDbg(t: String): String;
|
||||
function MyDbg(t: String; AnEsc: Boolean = false): String;
|
||||
|
||||
implementation
|
||||
|
||||
function MyDbg(t: String): String;
|
||||
function MyDbg(t: String; AnEsc: Boolean): String;
|
||||
begin
|
||||
Result := '';
|
||||
while(pos(LineEnding, t) > 0) do begin
|
||||
Result := Result + '"' + copy(t, 1, pos(LineEnding, t)-1) + '" Len='+IntTostr(pos(LineEnding, t)-1) + DbgStr(copy(t, 1, pos(LineEnding, t)-1)) + LineEnding;
|
||||
if AnEsc
|
||||
then Result := Result + DbgStr(copy(t, 1, pos(LineEnding, t)-1)) + ' // Len='+IntTostr(pos(LineEnding, t)-1) + DbgStr(copy(t, 1, pos(LineEnding, t)-1)) + LineEnding
|
||||
else Result := Result + '"' + copy(t, 1, pos(LineEnding, t)-1) + '" Len='+IntTostr(pos(LineEnding, t)-1) + DbgStr(copy(t, 1, pos(LineEnding, t)-1)) + LineEnding;
|
||||
system.Delete(t, 1, pos(LineEnding, t)-1+length(LineEnding));
|
||||
end;
|
||||
Result := Result + '"' + t + '" Len='+IntTostr(length(t)) + DbgStr(t);
|
||||
|
@ -5,7 +5,7 @@ unit TestBlockIndent;
|
||||
interface
|
||||
|
||||
uses
|
||||
SysUtils, testregistry, TestBase, math,
|
||||
SysUtils, testregistry, TestBase, math, Types,
|
||||
SynEdit, SynEditKeyCmds, SynEditTypes;
|
||||
|
||||
type
|
||||
@ -16,13 +16,22 @@ type
|
||||
protected
|
||||
function TestTextSpace: TStringArray;
|
||||
function TestTextTabs: TStringArray;
|
||||
function TestTextColumn: TStringArray;
|
||||
function TestTextColumn2: TStringArray;
|
||||
|
||||
function ReplaceIndent(AText: TStringArray; AFirstLine: Integer;
|
||||
ANewIndents: Array of String): TStringArray;
|
||||
function AddIndent(AText: TStringArray; AFirstLine, ALastLine, ASpaceCount: Integer): TStringArray;
|
||||
function DelIndent(AText: TStringArray; AFirstLine, ALastLine, ASpaceCount: Integer): TStringArray;
|
||||
function InsertIntoText(AText: TStringArray; AnInsText: String; AnXPos: array of integer): TStringArray;
|
||||
function DelFromText(AText: TStringArray; AnXPosAndLen: array of integer): TStringArray;
|
||||
|
||||
procedure TestSelAndText(Name: String; LogX1, LogY1, LogX2, LogY2: Integer;
|
||||
ExpLines: Array of String;
|
||||
SelIsForward: Boolean = True);
|
||||
procedure TestColSelAndText(Name: String; LogX1, LogY1, LogX2, LogY2: Integer;
|
||||
ExpLines: Array of String;
|
||||
SelIsForward: Boolean = True);
|
||||
//procedure DoTest(X1,Y1, X2,Y2: Boolean; CountIndent: Integer;
|
||||
// ExpX1, ExpY1, ExpX2, ExpY2: Integer;
|
||||
// ExpText: A
|
||||
@ -31,6 +40,8 @@ type
|
||||
procedure TestUnIndent;
|
||||
procedure TestIndentWithTab;
|
||||
procedure TestUnIndentWithTab;
|
||||
procedure TestIndentColSel;
|
||||
procedure TestUnindentColSel;
|
||||
end;
|
||||
|
||||
|
||||
@ -70,6 +81,29 @@ begin
|
||||
Result[12] := '';
|
||||
end;
|
||||
|
||||
function TTestBlockIndent.TestTextColumn: TStringArray;
|
||||
begin
|
||||
SetLength(Result, 8);
|
||||
Result[0] := 'abc def ghi mno pqr';
|
||||
Result[1] := 'abc de'#9#9'123456789';
|
||||
Result[2] := 'äbc ÄÖÜ 123 456 789'; // ä/Ä takes 2 bytes (logical pos)
|
||||
Result[3] := 'ab mef ghi mno pqr'; // m takes 3 bytes (logical pos), but also takes 1 extra phys pos
|
||||
Result[4] := 'ABCDEFGHIJKLMNOPQRSTUABCDEFGHIJKLMNOPQRSTU';
|
||||
Result[5] := ' abd '#9#9' xyz';
|
||||
Result[6] := 'ABCDEFGHIJKLMNOPQRSTUABCDEFGHIJKLMNOPQRSTU';
|
||||
Result[7] := '';
|
||||
end;
|
||||
|
||||
function TTestBlockIndent.TestTextColumn2: TStringArray;
|
||||
begin
|
||||
SetLength(Result, 5);
|
||||
Result[0] := 'ABCDEFGHIJKLMNOPQRSTUABCDEFGHIJKLMNOPQRSTU';
|
||||
Result[1] := 'abc def';
|
||||
Result[2] := 'abc '#9#9' def';
|
||||
Result[3] := 'ABCDEFGHIJKLMNOPQRSTUABCDEFGHIJKLMNOPQRSTU';
|
||||
Result[4] := '';
|
||||
end;
|
||||
|
||||
function GetLeadWSLen(s: String): integer;
|
||||
var
|
||||
Run : PChar;
|
||||
@ -81,7 +115,7 @@ begin
|
||||
end;
|
||||
|
||||
function TTestBlockIndent.ReplaceIndent(AText: TStringArray; AFirstLine: Integer;
|
||||
ANewIndents: Array of String): TStringArray;
|
||||
ANewIndents: array of String): TStringArray;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
@ -123,6 +157,35 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TTestBlockIndent.InsertIntoText(AText: TStringArray; AnInsText: String;
|
||||
AnXPos: array of integer): TStringArray;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
SetLength(Result, Length(AText));
|
||||
for i := 0 to Length(AText) - 1 do begin
|
||||
if (i < Length(AnXPos)) and (AnXPos[i] > 0) then
|
||||
Result[i] := copy(AText[i], 1, AnXPos[i]-1) + AnInsText + copy(AText[i], AnXPos[i], Length(AText[i]))
|
||||
else
|
||||
Result[i] := AText[i];
|
||||
end;
|
||||
end;
|
||||
|
||||
function TTestBlockIndent.DelFromText(AText: TStringArray; AnXPosAndLen: array of integer
|
||||
): TStringArray;
|
||||
var
|
||||
i, x: Integer;
|
||||
begin
|
||||
SetLength(Result, Length(AText));
|
||||
for i := 0 to Length(AText) - 1 do begin
|
||||
x := i * 2;
|
||||
if (x < Length(AnXPosAndLen)) and (AnXPosAndLen[x] > 0) then
|
||||
Result[i] := copy(AText[i], 1, AnXPosAndLen[x]-1) + copy(AText[i], AnXPosAndLen[x] + AnXPosAndLen[x+1], Length(AText[i]))
|
||||
else
|
||||
Result[i] := AText[i];
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestBlockIndent.TestSelAndText(Name: String; LogX1, LogY1, LogX2, LogY2: Integer;
|
||||
ExpLines: array of String; SelIsForward: Boolean);
|
||||
begin
|
||||
@ -133,6 +196,33 @@ begin
|
||||
TestIsFullText(Name, ExpLines);
|
||||
end;
|
||||
|
||||
procedure TTestBlockIndent.TestColSelAndText(Name: String; LogX1, LogY1, LogX2, LogY2: Integer;
|
||||
ExpLines: array of String; SelIsForward: Boolean);
|
||||
var
|
||||
BB, BE: TPoint;
|
||||
begin
|
||||
TestIsCaret(Name, LogX2, LogY2);
|
||||
if SynEdit.IsBackwardSel then begin
|
||||
BB := SynEdit.BlockEnd;
|
||||
BE := SynEdit.BlockBegin;
|
||||
end
|
||||
else begin
|
||||
BB := SynEdit.BlockBegin;
|
||||
BE := SynEdit.BlockEnd;
|
||||
end;
|
||||
|
||||
if (BB.X <> LogX1) or (BB.Y <> LogY1) then
|
||||
TestFail(Name, 'IsBlockBegin(Log)',
|
||||
Format('X/Y=(%d, %d)', [LogX1, LogY1]),
|
||||
Format('X/Y=(%d, %d)', [BB.X, BB.Y]));
|
||||
if (BE.X <> LogX2) or (BE.Y <> LogY2) then
|
||||
TestFail(Name, 'IsBlockEnd(Log)',
|
||||
Format('X/Y=(%d, %d)', [LogX1, LogY1]),
|
||||
Format('X/Y=(%d, %d)', [BE.X, BE.Y]));
|
||||
|
||||
TestIsFullText(Name, ExpLines);
|
||||
end;
|
||||
|
||||
procedure TTestBlockIndent.TestIndent;
|
||||
var
|
||||
i, j: Integer;
|
||||
@ -566,6 +656,403 @@ begin
|
||||
|
||||
end;
|
||||
|
||||
procedure TTestBlockIndent.TestIndentColSel;
|
||||
var
|
||||
TheCommand: integer;
|
||||
|
||||
procedure SetSelect(x1,y1, x2,y2: integer);
|
||||
begin
|
||||
SynEdit.LogicalCaretXY := Point(x2, y2);
|
||||
SynEdit.BlockBegin := Point(x1, y1);
|
||||
SynEdit.BlockEnd := Point(x2, y2);
|
||||
SynEdit.SelectionMode := smColumn;
|
||||
end;
|
||||
|
||||
procedure DoTest(AName: String; x1,y1, x2,y2: integer;
|
||||
ExpectSelStartX, ExpCaretX: integer;
|
||||
AnInsText: String; AnXPos: array of integer;
|
||||
ATestUndoRedo: Integer = 2
|
||||
);
|
||||
var
|
||||
TestTextSub: TStringArray;
|
||||
begin
|
||||
TestTextSub := InsertIntoText(TestTextColumn, AnInsText, AnXPos);
|
||||
SetLines(TestTextColumn);
|
||||
PushBaseName('');
|
||||
|
||||
SetSelect(x1,y1, x2,y2);
|
||||
SynEdit.CommandProcessor(TheCommand, '', nil);
|
||||
TestColSelAndText('indend', ExpectSelStartX,y1, ExpCaretX,y2, TestTextSub );
|
||||
if ATestUndoRedo = 0 then exit;
|
||||
|
||||
SynEdit.CommandProcessor(ecUndo, '', nil);
|
||||
TestColSelAndText('undo 1', x1,y1, x2,y2, TestTextColumn );
|
||||
SynEdit.CommandProcessor(ecRedo, '', nil);
|
||||
TestColSelAndText('redo 1', ExpectSelStartX,y1, ExpCaretX,y2, TestTextSub );
|
||||
if ATestUndoRedo = 1 then exit;
|
||||
|
||||
SynEdit.CommandProcessor(ecUndo, '', nil);
|
||||
TestColSelAndText('undo 2', x1,y1, x2,y2, TestTextColumn );
|
||||
SynEdit.CommandProcessor(ecRedo, '', nil);
|
||||
TestColSelAndText('redo 2', ExpectSelStartX,y1, ExpCaretX,y2, TestTextSub );
|
||||
|
||||
PopBaseName;
|
||||
end;
|
||||
|
||||
|
||||
procedure DoTest2(AName: String; x1,y1, x2,y2: integer;
|
||||
ExpectSelStartX1, ExpCaretX1, ExpectSelStartX2, ExpCaretX2: integer;
|
||||
AnInsText: String; AnXPos1, AnXPos2: array of integer;
|
||||
ExpGroupUndo: boolean
|
||||
);
|
||||
var
|
||||
TestTextSub1, TestTextSub2: TStringArray;
|
||||
undo: Integer;
|
||||
begin
|
||||
TestTextSub1 := InsertIntoText(TestTextColumn, AnInsText, AnXPos1);
|
||||
TestTextSub2 := InsertIntoText(TestTextSub1, AnInsText, AnXPos2);
|
||||
for undo := 0 to 2 do begin
|
||||
DoTest(AName, x1, y1, x2, y2, ExpectSelStartX1, ExpCaretX1, AnInsText, AnXPos1, undo);
|
||||
|
||||
SynEdit.CommandProcessor(TheCommand, '', nil);
|
||||
TestColSelAndText('indend 2', ExpectSelStartX2,y1, ExpCaretX2,y2, TestTextSub2 );
|
||||
|
||||
SynEdit.CommandProcessor(ecUndo, '', nil);
|
||||
if ExpGroupUndo then
|
||||
TestColSelAndText('undo 2-1', x1,y1, x2,y2, TestTextColumn )
|
||||
else
|
||||
TestColSelAndText('undo 2-1', ExpectSelStartX1,y1, ExpCaretX1,y2, TestTextSub1 );
|
||||
//
|
||||
SynEdit.CommandProcessor(ecRedo, '', nil);
|
||||
TestColSelAndText('redo 2-1', ExpectSelStartX2,y1, ExpCaretX2,y2, TestTextSub2 );
|
||||
|
||||
|
||||
SynEdit.CommandProcessor(ecUndo, '', nil);
|
||||
if not ExpGroupUndo then begin
|
||||
TestColSelAndText('undo 2-2a', ExpectSelStartX1,y1, ExpCaretX1,y2, TestTextSub1 );
|
||||
SynEdit.CommandProcessor(ecUndo, '', nil);
|
||||
end;
|
||||
TestColSelAndText('undo 2-2b', x1,y1, x2,y2, TestTextColumn );
|
||||
//
|
||||
SynEdit.CommandProcessor(ecRedo, '', nil);
|
||||
if not ExpGroupUndo then begin
|
||||
TestColSelAndText('redo 2-2b', ExpectSelStartX1,y1, ExpCaretX1,y2, TestTextSub1 );
|
||||
SynEdit.CommandProcessor(ecRedo, '', nil);
|
||||
end;
|
||||
TestColSelAndText('redo 2-2a', ExpectSelStartX2,y1, ExpCaretX2,y2, TestTextSub2 );
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
begin
|
||||
ReCreateEdit;
|
||||
SetLines(TestTextColumn);
|
||||
PushBaseName('');
|
||||
|
||||
SynEdit.Options := SynEdit.Options - [eoGroupUndo] + [eoShowSpecialChars];
|
||||
SynEdit.TabWidth := 5;
|
||||
SynEdit.BlockIndent := 2;
|
||||
SynEdit.BlockTabIndent := 0;
|
||||
TheCommand := ecBlockIndent;
|
||||
|
||||
|
||||
(* Line 1 Log = Phys
|
||||
Line 2 Tabs
|
||||
Line 3 Log = Phys // +1 for each äÄÖÜ
|
||||
Line 4 Log = Phys // +1 after m
|
||||
1 5 9 12
|
||||
abc def ghi mno pqr'
|
||||
1 5 7 8 9
|
||||
abc de'#9 #9 '123456789'
|
||||
abc de 123456789'
|
||||
1 6 13 17
|
||||
äbc ÄÖÜ 123 456 789'; // ä/Ä takes 2 bytes (logical pos)
|
||||
1 4 7 10 14
|
||||
ab mef ghi mno pqr'; // m takes 3 bytes (logical pos), but also takes 1 extra phys pos
|
||||
1 5 9 12
|
||||
ABCDEFGHIJKLMNOPQRSTUABCDEFGHIJKLMNOPQRSTU';
|
||||
1 5 8 9 11
|
||||
abd '#9#9 ' xyz';
|
||||
ABCDEFGHIJKLMNOPQRSTUABCDEFGHIJKLMNOPQRSTU';
|
||||
|
||||
*)
|
||||
|
||||
DoTest2('', 5,1, 12,4, 5, 14, 5, 16, ' ', [5,5,6,4], [5,5,6,4], False);
|
||||
DoTest2('', 6,3, 12,1, 6, 14, 6, 16, ' ', [5,5,6], [5,5,6], False); // only 3 lines, can't select half m
|
||||
DoTest2('', 12,1, 6,3, 14, 6, 16, 6, ' ', [5,5,6], [5,5,6], False); // only 3 lines, can't select half m
|
||||
DoTest2('', 12,4, 5,1, 14, 5, 16, 5, ' ', [5,5,6,4], [5,5,6,4], False);
|
||||
|
||||
//abc def ghi mno pqr
|
||||
//abc de___>____>123456
|
||||
// check the inserted spaces are AFTER the tab(s), but in the selection (incl at the end of)
|
||||
DoTest2('half tab - only 1 tab', 8,1, 11,4, 8, 13, 8, 15, ' ', [8,7,12,9], [8,10,12,9], False);
|
||||
DoTest2('half tab - only 1 tab', 8,1, 12,4, 8, 14, 8, 16, ' ', [8,8,12,9], [8,8,12,9], False);
|
||||
|
||||
DoTest2('half tab - only 1 tab', 9,1, 12,4, 9, 14, 9, 16, ' ', [9,8,13,10], [9,8,13,10], False);
|
||||
// NEXT: the 2nd indent goes after the 2nd tab, as the selection grew
|
||||
DoTest2('half tab - both tab', 9,1, 16,4, 9, 18, 9, 20, ' ', [9,8,13,10], [9,11,13,10], False);
|
||||
DoTest2('half tab - both tab', 9,1, 17,4, 9, 19, 9, 21, ' ', [9,9,13,10], [9,9,13,10], False);
|
||||
|
||||
DoTest2('start tab - only 1 tab', 7,1, 12,4, 7, 14, 7, 16, ' ', [7,8,10,8], [7,8,10,8], False);
|
||||
DoTest2('start tab - both tab', 7,1, 17,4, 7, 19, 7, 21, ' ', [7,9,10,8], [7,9,10,8], False);
|
||||
|
||||
DoTest2('before tab - only 1 tab', 6,1, 12,4, 6, 14, 6, 16, ' ', [6,6,8,7], [6,6,8,7], False);
|
||||
|
||||
// space go after tabs
|
||||
// tabs go at start
|
||||
SynEdit.BlockIndent := 2;
|
||||
SynEdit.BlockTabIndent := 1;
|
||||
|
||||
// sel to short insert space before tabs
|
||||
SetLines(TestTextColumn);
|
||||
SetSelect(6,5, 10,7);
|
||||
SynEdit.CommandProcessor(TheCommand, '', nil);
|
||||
TestColSelAndText('indend', 6,5, 13,7,
|
||||
InsertIntoText(InsertIntoText(TestTextColumn, ' ', [0,0,0,0, 6,6,6]), #9, [0,0,0,0, 6,6,6])
|
||||
);
|
||||
|
||||
SetLines(TestTextColumn);
|
||||
SetSelect(6,5, 11,7);
|
||||
SynEdit.CommandProcessor(TheCommand, '', nil);
|
||||
TestColSelAndText('indend', 6,5, 14,7,
|
||||
InsertIntoText(InsertIntoText(TestTextColumn, ' ', [0,0,0,0, 6,9,6]), #9, [0,0,0,0, 6,6,6])
|
||||
);
|
||||
|
||||
SetLines(TestTextColumn);
|
||||
SetSelect(6,5, 20,7);
|
||||
SynEdit.CommandProcessor(TheCommand, '', nil);
|
||||
TestColSelAndText('indend', 6,5, 23,7,
|
||||
InsertIntoText(InsertIntoText(TestTextColumn, ' ', [0,0,0,0, 6,10,6]), #9, [0,0,0,0, 6,6,6])
|
||||
);
|
||||
|
||||
|
||||
SynEdit.Options := SynEdit.Options + [eoGroupUndo] + [eoShowSpecialChars];
|
||||
SynEdit.BlockIndent := 2;
|
||||
SynEdit.BlockTabIndent := 0;
|
||||
|
||||
DoTest2('', 5,1, 12,4, 5, 14, 5, 16, ' ', [5,5,6,4], [5,5,6,4], True);
|
||||
DoTest2('', 6,3, 12,1, 6, 14, 6, 16, ' ', [5,5,6], [5,5,6], True); // only 3 lines, can't select half m
|
||||
DoTest2('', 12,1, 6,3, 14, 6, 16, 6, ' ', [5,5,6], [5,5,6], True); // only 3 lines, can't select half m
|
||||
DoTest2('', 12,4, 5,1, 14, 5, 16, 5, ' ', [5,5,6,4], [5,5,6,4], True);
|
||||
|
||||
(* **************************************************************************** *)
|
||||
|
||||
SynEdit.Options := SynEdit.Options - [eoGroupUndo];
|
||||
SynEdit.TabWidth := 5;
|
||||
SynEdit.BlockIndent := 2;
|
||||
SynEdit.BlockTabIndent := 0;
|
||||
TheCommand := ecBlockIndentMove;
|
||||
|
||||
|
||||
DoTest2('', 5,1, 12,4, 7, 14, 9, 16, ' ', [4,4,5,3], [4,4,5,3], False);
|
||||
DoTest2('', 6,3, 12,1, 8, 14, 10, 16, ' ', [4,4,5], [4,4,5], False); // only 3 lines, can't select half m
|
||||
DoTest2('', 12,1, 6,3, 14, 8, 16, 10, ' ', [4,4,5], [4,4,5], False); // only 3 lines, can't select half m
|
||||
DoTest2('', 12,4, 5,1, 14, 7, 16, 9, ' ', [4,4,5,3], [4,4,5,3], False);
|
||||
|
||||
// check the inserted spaces are AFTER the tab(s), but in the selection (incl at the end of)
|
||||
DoTest2('half tab - only 1 tab', 8,1, 10,4, 10, 12, 12, 14, ' ', [8,7,12,9], [8,7,12,9], False);
|
||||
// NEXT: the 2nd indent goes after the 2nd tab, as the selection moves
|
||||
DoTest2('half tab - only 1 tab', 9,1, 12,4, 11, 14, 13, 16, ' ', [8,7,12,9], [8,10,12,9], False);
|
||||
DoTest2('half tab - both tab', 9,1, 16,4, 11, 18, 13, 20, ' ', [8,7,12,9], [8,10,12,10], False);
|
||||
DoTest2('half tab - both tab', 9,1, 17,4, 11, 19, 13, 21, ' ', [8,7,12,9], [8,10,12,10], False);
|
||||
|
||||
DoTest2('start tab - only 1 tab', 7,1, 12,4, 9, 14, 11, 16, ' ', [7,7,10,8], [7,7,10,8], False);
|
||||
DoTest2('start tab - both tab', 7,1, 17,4, 9, 19, 11, 21, ' ', [7,7,10,8], [7,7,10,8], False);
|
||||
|
||||
// space go after tabs
|
||||
// tabs go at start
|
||||
SynEdit.BlockIndent := 2;
|
||||
SynEdit.BlockTabIndent := 1;
|
||||
|
||||
// sel to short insert space before tabs
|
||||
SetLines(TestTextColumn);
|
||||
SetSelect(6,5, 10,7);
|
||||
SynEdit.CommandProcessor(TheCommand, '', nil);
|
||||
TestColSelAndText('indend', 9,5, 13,7,
|
||||
InsertIntoText(InsertIntoText(TestTextColumn, ' ', [0,0,0,0, 6,6,6]), #9, [0,0,0,0, 6,5,6])
|
||||
);
|
||||
|
||||
SetLines(TestTextColumn);
|
||||
SetSelect(6,5, 11,7);
|
||||
SynEdit.CommandProcessor(TheCommand, '', nil);
|
||||
TestColSelAndText('indend', 9,5, 14,7,
|
||||
InsertIntoText(InsertIntoText(TestTextColumn, ' ', [0,0,0,0, 6,6,6]), #9, [0,0,0,0, 6,5,6])
|
||||
);
|
||||
|
||||
SetLines(TestTextColumn);
|
||||
SetSelect(11,5, 20,7);
|
||||
SynEdit.CommandProcessor(TheCommand, '', nil);
|
||||
TestColSelAndText('indend', 14,5, 23,7,
|
||||
InsertIntoText(InsertIntoText(TestTextColumn, ' ', [0,0,0,0, 11,9,11]), #9, [0,0,0,0, 11,5,11])
|
||||
);
|
||||
|
||||
|
||||
SynEdit.Options := SynEdit.Options + [eoGroupUndo];
|
||||
SynEdit.BlockIndent := 2;
|
||||
SynEdit.BlockTabIndent := 0;
|
||||
|
||||
DoTest2('', 5,1, 12,4, 7, 14, 9, 16, ' ', [4,4,5,3], [4,4,5,3], True);
|
||||
DoTest2('', 5,3, 12,1, 7, 14, 9, 16, ' ', [4,4,5], [4,4,5], True); // only 3 lines, can't select half m
|
||||
DoTest2('', 12,1, 5,3, 14, 7, 16, 9, ' ', [4,4,5], [4,4,5], True); // only 3 lines, can't select half m
|
||||
DoTest2('', 12,4, 5,1, 14, 7, 16, 9, ' ', [4,4,5,3], [4,4,5,3], True);
|
||||
|
||||
|
||||
|
||||
end;
|
||||
|
||||
procedure TTestBlockIndent.TestUnindentColSel;
|
||||
var
|
||||
TheCommand: integer;
|
||||
|
||||
procedure SetSelect(x1,y1, x2,y2: integer);
|
||||
begin
|
||||
SynEdit.LogicalCaretXY := Point(x2, y2);
|
||||
SynEdit.BlockBegin := Point(x1, y1);
|
||||
SynEdit.BlockEnd := Point(x2, y2);
|
||||
SynEdit.SelectionMode := smColumn;
|
||||
end;
|
||||
|
||||
procedure DoTest(AName: String; x1,y1, x2,y2: integer;
|
||||
ExpectSelStartX, ExpCaretX: integer;
|
||||
AnXDelPos: array of integer;
|
||||
AnInsText: String; AnXPos: array of integer;
|
||||
ATestUndoRedo: Integer = 2
|
||||
);
|
||||
var
|
||||
TestTextSub: TStringArray;
|
||||
begin
|
||||
TestTextSub := DelFromText(TestTextColumn2, AnXDelPos);
|
||||
TestTextSub := InsertIntoText(TestTextSub, AnInsText, AnXPos);
|
||||
|
||||
SetLines(TestTextColumn2);
|
||||
PushBaseName('');
|
||||
|
||||
SetSelect(x1,y1, x2,y2);
|
||||
SynEdit.CommandProcessor(TheCommand, '', nil);
|
||||
TestColSelAndText('unindend', ExpectSelStartX,y1, ExpCaretX,y2, TestTextSub );
|
||||
if ATestUndoRedo = 0 then exit;
|
||||
|
||||
SynEdit.CommandProcessor(ecUndo, '', nil);
|
||||
TestColSelAndText('undo 1', x1,y1, x2,y2, TestTextColumn2 );
|
||||
SynEdit.CommandProcessor(ecRedo, '', nil);
|
||||
TestColSelAndText('redo 1', ExpectSelStartX,y1, ExpCaretX,y2, TestTextSub );
|
||||
if ATestUndoRedo = 1 then exit;
|
||||
|
||||
SynEdit.CommandProcessor(ecUndo, '', nil);
|
||||
TestColSelAndText('undo 2', x1,y1, x2,y2, TestTextColumn2 );
|
||||
SynEdit.CommandProcessor(ecRedo, '', nil);
|
||||
TestColSelAndText('redo 2', ExpectSelStartX,y1, ExpCaretX,y2, TestTextSub );
|
||||
|
||||
PopBaseName;
|
||||
end;
|
||||
|
||||
|
||||
procedure DoTest2(AName: String; x1,y1, x2,y2: integer;
|
||||
ExpectSelStartX1, ExpCaretX1, ExpectSelStartX2, ExpCaretX2: integer;
|
||||
AnXDelPos1, AnXDelPos2: array of integer;
|
||||
AnInsText1: String; AnXPos1: array of integer;
|
||||
AnInsText2: String; AnXPos2: array of integer;
|
||||
ExpGroupUndo: boolean;
|
||||
SkipRedo: boolean = False // in case the selection gets 0 column width / undo does not store that
|
||||
);
|
||||
var
|
||||
TestTextSub1, TestTextSub2: TStringArray;
|
||||
undo: Integer;
|
||||
begin
|
||||
TestTextSub1 := DelFromText(TestTextColumn2, AnXDelPos1);
|
||||
TestTextSub1 := InsertIntoText(TestTextSub1, AnInsText1, AnXPos1);
|
||||
TestTextSub2 := DelFromText(TestTextSub1, AnXDelPos2);
|
||||
TestTextSub2 := InsertIntoText(TestTextSub2, AnInsText2, AnXPos2);
|
||||
|
||||
for undo := 0 to 2 do begin
|
||||
DoTest(AName, x1, y1, x2, y2, ExpectSelStartX1, ExpCaretX1, AnXDelPos1, AnInsText1, AnXPos1, undo);
|
||||
|
||||
SynEdit.CommandProcessor(TheCommand, '', nil);
|
||||
TestColSelAndText('unindend 2', ExpectSelStartX2,y1, ExpCaretX2,y2, TestTextSub2 );
|
||||
|
||||
SynEdit.CommandProcessor(ecUndo, '', nil);
|
||||
if ExpGroupUndo then
|
||||
TestColSelAndText('undo 2-1', x1,y1, x2,y2, TestTextColumn2 )
|
||||
else
|
||||
TestColSelAndText('undo 2-1', ExpectSelStartX1,y1, ExpCaretX1,y2, TestTextSub1 );
|
||||
//
|
||||
SynEdit.CommandProcessor(ecRedo, '', nil);
|
||||
if not SkipRedo then
|
||||
TestColSelAndText('redo 2-1', ExpectSelStartX2,y1, ExpCaretX2,y2, TestTextSub2 );
|
||||
|
||||
|
||||
SynEdit.CommandProcessor(ecUndo, '', nil);
|
||||
if not ExpGroupUndo then begin
|
||||
TestColSelAndText('undo 2-2a', ExpectSelStartX1,y1, ExpCaretX1,y2, TestTextSub1 );
|
||||
SynEdit.CommandProcessor(ecUndo, '', nil);
|
||||
end;
|
||||
TestColSelAndText('undo 2-2b', x1,y1, x2,y2, TestTextColumn2 );
|
||||
//
|
||||
SynEdit.CommandProcessor(ecRedo, '', nil);
|
||||
if not ExpGroupUndo then begin
|
||||
TestColSelAndText('redo 2-2b', ExpectSelStartX1,y1, ExpCaretX1,y2, TestTextSub1 );
|
||||
SynEdit.CommandProcessor(ecRedo, '', nil);
|
||||
end;
|
||||
if not SkipRedo then
|
||||
TestColSelAndText('redo 2-2a', ExpectSelStartX2,y1, ExpCaretX2,y2, TestTextSub2 );
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
|
||||
(*
|
||||
1 5 10
|
||||
ABCDEFGHIJKLMNOPQRSTUABCDEFGHIJKLMNOPQRSTU';
|
||||
1 14
|
||||
abc def';
|
||||
1 5 6 10
|
||||
abc '#9#9' def;
|
||||
abc >____> def;
|
||||
ABCDEFGHIJKLMNOPQRSTUABCDEFGHIJKLMNOPQRSTU';
|
||||
*)
|
||||
SynEdit.Options := SynEdit.Options - [eoGroupUndo] + [eoShowSpecialChars];
|
||||
SynEdit.TabWidth := 5;
|
||||
SynEdit.BlockIndent := 2;
|
||||
SynEdit.BlockTabIndent := 0;
|
||||
TheCommand := ecBlockUnindent;
|
||||
|
||||
DoTest2('', 4,1, 15,4, 4, 15, 4, 15, [0,0, 12,2, 8,2], [0,0, 10,2, 6,2], '',[], ' ',[0,0,6], False); // no unindent in last row / no shrink sel
|
||||
// 12 as 4 spaces where added
|
||||
DoTest2('', 4,1, 12,3, 4, 10, 4, 12, [0,0, 12,2, 8,2], [0,0, 10,2, 6,2], '',[], ' ',[0,0,6], False);
|
||||
|
||||
// at end of tab / don't del outside select
|
||||
DoTest2('', 4,1, 7,3, 4, 9, 4, 7, [0,0, 9,2, 6,1], [0,0, 7,2, 7,2], ' ',[0,0,6], '',[], False);
|
||||
DoTest2('', 11,2, 4,3, 9, 4, 7, 4, [0,0, 9,2, 6,1], [0,0, 7,2, 7,2], ' ',[0,0,6], '',[], False);
|
||||
|
||||
SynEdit.BlockIndent := 8;
|
||||
DoTest2('', 15,2, 4,3, 7, 4, 5, 4, [0,0, 6,8, 6,4], [0,0, 4,2, 4,2], '',[], '',[], False);
|
||||
// at end of tab / don't del outside select
|
||||
DoTest2('', 13,2, 4,3, 5, 4, 4, 4, [0,0, 5,8, 5,4], [0,0, 4,1, 4,1], '',[], '',[], False, True);
|
||||
|
||||
|
||||
SynEdit.BlockIndent := 1;
|
||||
SynEdit.BlockTabIndent := 1;
|
||||
DoTest2('', 15,2, 4,3, 12, 4, 9, 4, [0,0, 11,3, 4,4], [0,0, 8,3, 4,2], #9'',[0,0,4], '',[], False);
|
||||
|
||||
|
||||
SynEdit.Options := SynEdit.Options + [eoGroupUndo] + [eoShowSpecialChars];
|
||||
SynEdit.BlockIndent := 2;
|
||||
SynEdit.BlockTabIndent := 0;
|
||||
|
||||
DoTest2('', 4,1, 12,3, 4, 10, 4, 12, [0,0, 12,2, 8,2], [0,0, 10,2, 6,2], '',[], ' ',[0,0,6], True);
|
||||
|
||||
(* **************************************************************************** *)
|
||||
|
||||
SynEdit.Options := SynEdit.Options - [eoGroupUndo] + [eoShowSpecialChars];
|
||||
SynEdit.TabWidth := 5;
|
||||
SynEdit.BlockIndent := 2;
|
||||
SynEdit.BlockTabIndent := 0;
|
||||
TheCommand := ecBlockUnindentMove;
|
||||
|
||||
DoTest2('', 12,2, 11,3, 10,13, 8,11, [0,0, 10,2, 6,2], [0,0, 8,2, 8,2], ' ',[0,0,6], '',[], False);
|
||||
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
RegisterTest(TTestBlockIndent);
|
||||
|
@ -555,6 +555,8 @@ begin
|
||||
ecToggleMode : Result:= srkmecToggleMode;
|
||||
ecBlockIndent : Result:= srkmecBlockIndent;
|
||||
ecBlockUnindent : Result:= srkmecBlockUnindent;
|
||||
ecBlockIndentMove : Result:= srkmecBlockIndentMove;
|
||||
ecBlockUnindentMove : Result:= srkmecBlockUnindentMove;
|
||||
ecTab : Result:= lisTab;
|
||||
ecShiftTab : Result:= srkmecShiftTab;
|
||||
ecMatchBracket : Result:= srkmecMatchBracket;
|
||||
@ -1264,6 +1266,8 @@ begin
|
||||
// editing
|
||||
ecBlockIndent: SetCombo(VK_I,[XCtrl],VK_UNKNOWN,[], VK_K,[XCtrl],VK_I,[]);
|
||||
ecBlockUnindent: SetCombo(VK_U,[XCtrl],VK_UNKNOWN,[], VK_K,[XCtrl],VK_U,[]);
|
||||
ecBlockIndentMove: SetSingle(VK_I,[XCtrl, ssShift]);
|
||||
ecBlockUnindentMove: SetSingle(VK_U,[XCtrl, ssShift]);
|
||||
ecDeleteLastChar: SetSingle(VK_BACK,[], VK_BACK,[ssShift]); // ctrl H used for scroll window.
|
||||
ecDeleteChar: SetSingle(VK_DELETE,[]); // ctrl G conflicts with GO
|
||||
ecDeleteWord: SetSingle(VK_T,[XCtrl], VK_DELETE,[XCtrl]);
|
||||
@ -2916,6 +2920,8 @@ begin
|
||||
AddDefault(C, 'Line selection mode', srkmecLineSelect, ecLineSelect);
|
||||
AddDefault(C, 'Indent block', srkmecBlockIndent, ecBlockIndent);
|
||||
AddDefault(C, 'Unindent block', srkmecBlockUnindent, ecBlockUnindent);
|
||||
AddDefault(C, 'Indent block move', srkmecBlockIndentMove, ecBlockIndentMove);
|
||||
AddDefault(C, 'Unindent block move', srkmecBlockUnindentMove, ecBlockUnindentMove);
|
||||
AddDefault(C, 'Uppercase selection', lisMenuUpperCaseSelection, ecSelectionUpperCase);
|
||||
AddDefault(C, 'Lowercase selection', lisMenuLowerCaseSelection, ecSelectionLowerCase);
|
||||
AddDefault(C, 'Swap case in selection', lisMenuSwapCaseSelection, ecSelectionSwapCase);
|
||||
|
@ -3104,6 +3104,8 @@ resourcestring
|
||||
srkmecToggleMode = 'Toggle Mode';
|
||||
srkmecBlockIndent = 'Indent block';
|
||||
srkmecBlockUnindent = 'Unindent block';
|
||||
srkmecBlockIndentMove = 'Indent (move) block';
|
||||
srkmecBlockUnindentMove = 'Unindent (move) block';
|
||||
srkmecPluginMultiCaretSetCaret = 'Add extra caret';
|
||||
srkmecPluginMultiCaretUnsetCaret = 'Remove extra caret';
|
||||
srkmecPluginMultiCaretToggleCaret = 'Toggle extra caret';
|
||||
|
Loading…
Reference in New Issue
Block a user