synedit: moved fold code to TSynEditStringList, fixed folding after enter, bug #7773

git-svn-id: trunk@15990 -
This commit is contained in:
mattias 2008-08-08 04:57:39 +00:00
parent 3ca7d74abe
commit 5ebb7d717d
2 changed files with 207 additions and 151 deletions

View File

@ -661,6 +661,9 @@ type
procedure ListDeleted(Index: integer);
procedure ListInserted(Index: integer);
procedure ListPutted(Index: integer);
{$IFDEF SYN_LAZARUS}
procedure FoldChanged(Index: integer);
{$ENDIF}
procedure ListScanRanges(Sender: TObject);
procedure Loaded; override;
procedure MarkListChange(Sender: TObject);
@ -1445,6 +1448,9 @@ begin
OnCleared := {$IFDEF FPC}@{$ENDIF}ListCleared;
OnDeleted := {$IFDEF FPC}@{$ENDIF}ListDeleted;
OnInserted := {$IFDEF FPC}@{$ENDIF}ListInserted;
{$IFDEF SYN_LAZARUS}
OnFoldChanged := {$IFDEF FPC}@{$ENDIF}FoldChanged;
{$ENDIF}
OnPutted := {$IFDEF FPC}@{$ENDIF}ListPutted;
// OnScanRanges := {$IFDEF FPC}@{$ENDIF}ListScanRanges;
end;
@ -2879,80 +2885,13 @@ end;
{$IFDEF SYN_LAZARUS}
procedure TCustomSynEdit.CodeFoldAction(iLine: integer);
// iLine is 1 based as parameter
// and 0 based in the procedure below
procedure UpdateFolded(var iLine: integer);
var
FoldType: TSynEditCodeFoldType;
Level: LongInt;
CurFoldType: TSynEditCodeFoldType;
SLines: TSynEditStringList;
begin
SLines:=TSynEditStringList(fLines);
FoldType:=SLines.FoldType[iLine];
Level:=SLines.FoldEndLevel[iLine];
if FoldType=cfCollapsed then begin
// fold all lines including sub blocks
inc(iLine);
while (iLine<Lines.Count)
and (SLines.FoldMinLevel[iLine]>=Level) do begin
//debugln('UpdateFolded Fold ',dbgs(iLine),' ',Lines[iLine]);
SLines.Folded[iLine]:=true;
inc(iLine);
end;
// fold last line of block
if (iLine<Lines.Count)
and (SLines.FoldType[iLine]=cfEnd) then begin
//debugln('UpdateFolded Fold END ',dbgs(iLine),' ',Lines[iLine]);
SLines.Folded[iLine]:=true;
inc(iLine);
end;
end else if FoldType=cfExpanded then begin
// expand all lines of this block and all sub expanded blocks
// sub blocks, that are collapsed, remain collapsed
inc(iLine);
while (iLine<Lines.Count)
and (SLines.FoldMinLevel[iLine]>=Level) do begin
//debugln('UpdateFolded Expand ',dbgs(iLine),' ',Lines[iLine]);
SLines.Folded[iLine]:=false;
CurFoldType:=SLines.FoldType[iLine];
if CurFoldType in [cfExpanded,cfCollapsed] then
UpdateFolded(iLine)
else
inc(iLine);
end;
// expand last line of block
if (iLine<Lines.Count)
and (SLines.FoldType[iLine]=cfEnd) then begin
//debugln('UpdateFolded Expand END ',dbgs(iLine),' ',Lines[iLine]);
SLines.Folded[iLine]:=false;
inc(iLine);
end;
end;
end;
var
FoldType: TSynEditCodeFoldType;
// and 0 based in TSynEsitStringList
begin
if (iLine<=0) or (iLine>Lines.Count) then exit;
dec(iLine);
FoldType:=TSynEditStringList(fLines).FoldType[iLine];
//debugln('TCustomSynEdit.CodeFoldAction A ',dbgs(iLine),' ',dbgs(ord(FoldType)));
if FoldType in [cfExpanded,cfCollapsed] then begin
if FoldType=cfExpanded then begin
// collapse the branch
FoldType:=cfCollapsed;
//debugln('collapsing node: ',dbgs(iLine));
end else begin
// expand the branch
//debugln('expanding node: ',dbgs(iLine));
FoldType:=cfExpanded;
end;
//DebugLn(['TCustomSynEdit.CodeFoldAction iLine=',iLine]);
TSynEditStringList(fLines).FoldType[iLine] := FoldType;
UpdateFolded(iLine);
Invalidate;
case TSynEditStringList(fLines).FoldType[iLine] of
cfExpanded : TSynEditStringList(fLines).FoldLines(iLine);
cfCollapsed : TSynEditStringList(fLines).UnFoldLines(iLine);
end;
end;
@ -2967,13 +2906,8 @@ begin
end;
procedure TCustomSynEdit.UnfoldAll;
var
SLines: TSynEditStringList;
i: Integer;
begin
SLines:=TSynEditStringList(Lines);
for i:=0 to SLines.Count-1 do
SLines.Folded[i]:=false;
TSynEditStringList(Lines).UnfoldAll;
Invalidate;
end;
@ -4881,6 +4815,8 @@ begin
EnsureCursorPosVisible;
Include(fStateFlags, sfCaretChanged);
{$IFDEF SYN_LAZARUS}
if TSynEditStringList(fLines).Folded[fCaretY - 1] then
TSynEditStringList(fLines).UnFoldLines(fCaretY - 1);
{$ELSE}
Include(fStateFlags, sfScrollbarChanged);
{$ENDIF}
@ -5899,6 +5835,8 @@ function TCustomSynEdit.ScanFrom(Index: integer
{$IFDEF SYN_LAZARUS}; AtLeastTilIndex: integer{$ENDIF}): integer;
{$IFDEF SYN_LAZARUS}
// Index and AtLeastTilIndex are 0 based
var
FixFStart: Integer;
procedure SetCodeFoldAttributes;
var
@ -5906,13 +5844,17 @@ function TCustomSynEdit.ScanFrom(Index: integer
CodeFoldEndLevel: LongInt;
CodeFoldType: TSynEditCodeFoldType;
LastCodeFoldEndLevel: LongInt;
i : integer;
UnFoldLevel: LongInt;
begin
CodeFoldMinLevel:=fHighlighter.MinimumCodeFoldBlockLevel;
CodeFoldEndLevel:=fHighlighter.CurrentCodeFoldBlockLevel;
CodeFoldType:=cfNone;
if CodeFoldEndLevel>CodeFoldMinLevel then begin
if CodeFoldEndLevel > CodeFoldMinLevel then begin
// block started (and not closed in the same line)
CodeFoldType:=cfExpanded;
if TSynEditStringList(Lines).FoldType[Result-1] = cfCollapsed
then CodeFoldType := cfCollapsed
else CodeFoldType := cfExpanded;
//debugln(['TCustomSynEdit.ScanFrom Block started Y=',Result,' MinLevel=',CodeFoldMinLevel,' EndLevel=',CodeFoldEndLevel,' CodeFoldType=',ord(CodeFoldType),' Line="',Lines[Result-1],'"']);
end else if (Result>1) then begin
LastCodeFoldEndLevel:=TSynEditStringList(Lines).FoldEndLevel[Result-2];
@ -5924,84 +5866,35 @@ function TCustomSynEdit.ScanFrom(Index: integer
CodeFoldType:=cfContinue;
end;
end;
// check if an cfEnd got removed
if (TSynEditStringList(Lines).FoldType[Result-1] = cfEnd)
and not(CodeFoldType = cfEnd) and (Result >= 2) then begin
// unfolde it; do not trust Folded[], as Fixfolded has not yet run
i := Result - 2;
UnFoldLevel := TSynEditStringList(Lines).FoldEndLevel[i];
while (i >= 0)
and (TSynEditStringList(Lines).FoldMinLevel[i] >= UnFoldLevel) do
dec(i);
if TSynEditStringList(Lines).FoldType[i] = cfCollapsed then begin
TSynEditStringList(Lines).FoldType[i] := cfExpanded;
if i < FixFStart then FixFStart := i;
end;
end;
//DebugLn(['TCustomSynEdit.ScanFrom CodeFoldType=',SynEditCodeFoldTypeNames[CodeFoldType],' FoldMinLevel=',CodeFoldMinLevel,' FoldEndLevel=',CodeFoldEndLevel,' Folded=',false]);
TSynEditStringList(Lines).FoldMinLevel[Result-1] := CodeFoldMinLevel;
TSynEditStringList(Lines).FoldEndLevel[Result-1] := CodeFoldEndLevel;
TSynEditStringList(Lines).FoldType[Result-1] := CodeFoldType;
end;
procedure CheckFolded(FromIndex, ToIndex: integer);
{ Checks/Updates the Folded attributes of every scanned line
}
var
i: LongInt;
FoldStart: LongInt;
FoldLevel: LongInt;
SLines: TSynEditStringList;
begin
i:=FromIndex;
if i<0 then i:=0;
if i>ToIndex then exit;
SLines:=TSynEditStringList(Lines);
// find start and level of folded block at start of scan range
if SLines.Folded[i] then begin
FoldStart:=i;
while (FoldStart>0) and SLines.Folded[FoldStart] do
dec(FoldStart);
FoldLevel:=SLines.FoldEndLevel[FoldStart];
//DebugLn(['CheckFolded First FoldStart=',FoldStart,' FoldLevel=',FoldLevel,' Line="',Lines[FoldStart],'"']);
end else begin
FoldStart:=-1;
FoldLevel:=0;
end;
// check and fix 'folded' attributes of scanned range
while i<=ToIndex do begin
if FoldLevel<=0 then begin
// last line is not folded
if not SLines.Folded[i] then begin
// this line was not folded
// no change needed
end else begin
// this line was folded
if (i>0) and (SLines.FoldMinLevel[i-1]>=SLines.FoldEndLevel[i-1]) then
begin
// last line did not contain a block start
// => unfolded block must continue
SLines.Folded[i]:=false;
FoldLevel:=0;
//DebugLn(['CheckFolded Change A Folded of line ',i,' to FoldStart=',FoldStart,' FoldLevel=',FoldLevel,' Line="',Lines[i],'" SLines.Folded[i]=',SLines.Folded[i]]);
end;
end;
end else begin
// last line is folded
if SLines.Folded[i] then begin
// this line was folded
if (i>0) and (SLines.FoldEndLevel[i-1]<FoldLevel) then begin
// last fold block ended with last line
FoldStart:=i;
FoldLevel:=SLines.FoldMinLevel[FoldStart];
SLines.Folded[i]:=false;
//DebugLn(['CheckFolded Change B Folded of line ',i,' to FoldStart=',FoldStart,' FoldLevel=',FoldLevel,' Line="',Lines[i],'" SLines.Folded[i]=',SLines.Folded[i]]);
end;
end else begin
// this line was not folded
if (i>0) and (SLines.FoldEndLevel[i-1]>=FoldLevel) then begin
// current folded block must be continued
SLines.Folded[i]:=true;
//DebugLn(['CheckFolded Change C Folded of line ',i,' to FoldStart=',FoldStart,' FoldLevel=',FoldLevel,' Line="',Lines[i],'" SLines.Folded[i]=',SLines.Folded[i]]);
end;
end;
end;
inc(i);
end;
end;
{$ENDIF}
begin
Result := Index;
if Index >= Lines.Count - 1 then Exit;
//debugln('TCustomSynEdit.ScanFrom A Index=',dbgs(Index),' Line="',Lines[Index],'"');
{$IFDEF SYN_LAZARUS}
FixFStart := Index;
{$ENDIF}
fHighlighter.SetLine(Lines[Result], Result);
inc(Result);
fHighlighter.NextToEol;
@ -6027,12 +5920,16 @@ begin
break;
end;
{$IFDEF SYN_LAZARUS}
if (Result>Index+1) and (Result<=Lines.Count) then begin
// at least one line changed
// => update code fold attributes of last scanned line
// at least one line changed
// => update code fold attributes of last scanned line
if (Result>Index+1) and (Result<=Lines.Count) then
SetCodeFoldAttributes;
end;
CheckFolded(Index,Result);
TSynEditStringList(Lines).FixFolding(FixFStart, Result);
if TSynEditStringList(fLines).Folded[fCaretY - 1] then
TSynEditStringList(fLines).UnFoldLines(fCaretY - 1);
if TSynEditStringList(Lines).Folded[TopLine] then
TopLine := FindNextUnfoldedLine(TopLine, False);
if FixFStart < index then Invalidate;
{$ENDIF}
Dec(Result);
end;
@ -6178,6 +6075,18 @@ begin
{$ENDIF}
end;
{$IFDEF SYN_LAZARUS}
procedure TCustomSynEdit.FoldChanged(Index : integer);
begin
if Index + 1 > ScreenRowToRow(LinesInWindow + 1) then exit;
if Index + 1 < TopLine then Index := TopLine;
if TSynEditStringList(Lines).Folded[TopLine] then
TopLine := FindNextUnfoldedLine(TopLine, False);
InvalidateLines(Index + 1, ScreenRowToRow(LinesInWindow + 1));
InvalidateGutterLines(Index + 1, ScreenRowToRow(LinesInWindow + 1));
end;
{$ENDIF}
procedure TCustomSynEdit.ListScanRanges(Sender: TObject);
{$IFNDEF SYN_LAZARUS}
var
@ -7635,6 +7544,7 @@ var
LogCaret: TPoint;
LogSpacePos: integer;
LastUndoItem:TSynEditUndoItem;
CY: Integer;
{$ENDIF}
{begin} //mh 2000-10-30
@ -7800,6 +7710,10 @@ begin
Caret := CaretXY;
CaretNew := PrevWordPos;
{$IFDEF SYN_LAZARUS}
if TSynEditStringList(fLines).Folded[CaretNew.Y - 1] then begin
CY := FindNextUnfoldedLine(CaretNew.Y, False);
CaretNew := LogicalToPhysicalPos(Point(1 + Length(fLines[CY-1]), CY));
end;
MoveCaretAndSelectionPhysical
{$ELSE}
MoveCaretAndSelection
@ -7815,6 +7729,8 @@ begin
Caret := CaretXY;
CaretNew := NextWordPos;
{$IFDEF SYN_LAZARUS}
if TSynEditStringList(fLines).Folded[CaretNew.Y - 1] then
CaretNew := Point(1, FindNextUnfoldedLine(CaretNew.Y, True));
MoveCaretAndSelectionPhysical
{$ELSE}
MoveCaretAndSelection

View File

@ -150,6 +150,9 @@ type
fOnDeleted: TStringListIndexEvent;
fOnInserted: TStringListIndexEvent;
fOnPutted: TStringListIndexEvent;
{$IFDEF SYN_LAZARUS}
fOnFoldChanged: TStringListIndexEvent;
{$ENDIF}
function Get(Index: integer): string; override;
function GetCapacity: integer;
{$IFDEF SYN_COMPILER_3_UP} override; {$ENDIF} //mh 2000-10-18
@ -163,6 +166,7 @@ type
procedure SetUpdateState(Updating: Boolean); override;
{$IFDEF SYN_LAZARUS}
procedure SetTextStr(const Value: string); override;
procedure MaybeUnfoldAboveNewLine(ANewLineIndex: Integer);
{$ENDIF}
public
constructor Create;
@ -180,6 +184,10 @@ type
procedure SaveToFile(const FileName: string); override;
{$IFDEF SYN_LAZARUS}
procedure ClearRanges(ARange: TSynEditRange);
procedure FoldLines(AStartIndex: Integer);
procedure UnFoldLines(AStartIndex: Integer);
procedure UnfoldAll;
procedure FixFolding(AStartIndex: Integer; AMinEndIndex: Integer = 0);
{$ENDIF}
public
property DosFileFormat: boolean read fDosFileFormat write fDosFileFormat;
@ -198,6 +206,8 @@ type
write fOnInserted;
property OnPutted: TStringListIndexEvent read fOnPutted write fOnPutted;
{$IFDEF SYN_LAZARUS}
property OnFoldChanged: TStringListIndexEvent read fOnFoldChanged
write fOnFoldChanged;
property Folded[Index: integer]: boolean read GetFolded write SetFolded;
property FoldMinLevel[Index: integer]: integer read GetFoldMinLevel
write SetFoldMinLevel;
@ -527,6 +537,9 @@ begin
BeginUpdate;
Result := fCount;
InsertItem(Result, S);
{$IFDEF SYN_LAZARUS}
MaybeUnfoldAboveNewLine(Result);
{$ENDIF}
if Assigned(fOnAdded) then
fOnAdded(Result);
EndUpdate;
@ -562,6 +575,9 @@ begin
end;
Inc(fCount);
end;
{$IFDEF SYN_LAZARUS}
MaybeUnfoldAboveNewLine(FirstAdded);
{$ENDIF}
if Assigned(fOnAdded) then
fOnAdded(FirstAdded);
finally
@ -877,6 +893,9 @@ begin
ListIndexOutOfBounds(Index);
BeginUpdate;
InsertItem(Index, S);
{$IFDEF SYN_LAZARUS}
MaybeUnfoldAboveNewLine(Index);
{$ENDIF}
if Assigned(fOnInserted) then
fOnInserted(Index);
EndUpdate;
@ -928,6 +947,9 @@ begin
end;
FillChar(fList^[Index], NumLines * SynEditStringRecSize, 0);
Inc(fCount, NumLines);
{$IFDEF SYN_LAZARUS}
MaybeUnfoldAboveNewLine(Index);
{$ENDIF}
if Assigned(fOnAdded) then
fOnAdded(Index);
finally
@ -1048,6 +1070,124 @@ begin
for Index:=0 to fCount-1 do
fList^[Index].fRange := ARange;
end;
procedure TSynEditStringList.FoldLines(AStartIndex : Integer);
begin
//debugln(['TSynEditStringList.FoldLines AstartIndex=', AStartIndex, ' FoldType=', ord(FoldType[AStartIndex]), ' Folded=',dbgs(Folded[AStartIndex]) ]);
if (AStartIndex < 0) or (AStartIndex >= Count)
or not (FoldType[AStartIndex] = cfExpanded) then exit;
FoldType[AStartIndex] := cfCollapsed;
FixFolding(AStartIndex);
if Assigned(fOnFoldChanged) then
fOnFoldChanged(AStartIndex);
end;
procedure TSynEditStringList.UnFoldLines(AStartIndex : Integer);
var
i1, i2: LongInt;
begin
//debugln(['TSynEditStringList.UnFoldLines AstartIndex=', AStartIndex, ' FoldType(0=nil;1=col;2=exp)=', ord(FoldType[AStartIndex]), ' Folded=',dbgs(Folded[AStartIndex]) ]);
if (AStartIndex < 0) or (AStartIndex >= Count)
and ((FoldType[AStartIndex] = cfCollapsed) or Folded[AStartIndex])
then exit;
if Folded[AStartIndex] then begin
i2 := AStartIndex;
while Folded[AStartIndex] do begin
i1 := AStartIndex-1;
while (i1 >= 0) and Folded[i1] do dec(i1);
FoldType[i1] := cfExpanded;
FixFolding(i1);
if i1 < i2 then i2 := i1;
end;
if Assigned(fOnFoldChanged) then
fOnFoldChanged(i2);
exit;
end;
FoldType[AStartIndex] := cfExpanded;
FixFolding(AStartIndex);
if Assigned(fOnFoldChanged) then
fOnFoldChanged(AStartIndex);
end;
procedure TSynEditStringList.UnfoldAll;
var
i: Integer;
begin
for i:=0 to Count-1 do begin
Folded[i]:=false;
if FoldType[i] = cfCollapsed then FoldType[i] := cfExpanded;
end;
end;
procedure TSynEditStringList.FixFolding(AStartIndex: integer; AMinEndIndex : Integer = 0);
var
Level, CoLevel: LongInt;
cnt, i: Integer;
begin
cnt := Count;
if (AStartIndex < 0) or (AStartIndex >= cnt) then exit;
//debugln(['TSynEditStringList.FixFolding AStartIndex=', AStartIndex, ' AMinEndindex=', AMinEndIndex, ' FoldType(0=nil;1=col;2=exp)=', ord(FoldType[AStartIndex]), ' Folded=',dbgs(Folded[AStartIndex]) ]);
// Always do the current line
if AMinEndIndex < AStartIndex then AMinEndIndex := AStartIndex;
// Always start from unfolded line
while (AStartIndex > 0) and Folded[AStartIndex] do
dec(AStartIndex);
// Remember current Level, fix must run at least to the end of current level
Level := FoldEndLevel[AStartIndex];
while (AStartIndex < cnt)
and ((FoldMinLevel[AStartIndex] >= Level) or (AStartIndex <= AMinEndIndex))
do begin
if (FoldType[AStartIndex] = cfCollapsed) then begin
// begin of a new fold; keep first line visible
Folded[AStartIndex] := False;
CoLevel := FoldEndLevel[AStartIndex];
inc(AStartIndex);
while (AStartIndex < cnt)
and (FoldMinLevel[AStartIndex] >= CoLevel) do begin
Folded[AStartIndex] := true;
inc(AStartIndex);
end;
// fold last line of block
if (AStartIndex < cnt)
and (FoldType[AStartIndex] = cfEnd) then begin
Folded[AStartIndex] := true;
inc(AStartIndex);
end;
end
else begin
//unfolded
Folded[AStartIndex] := false;
inc(AStartIndex);
end;
end;
// last line of block, if cfEnd (otherwise the next block begins here)
if (AStartIndex < cnt)
and (FoldType[AStartIndex] = cfEnd) then
Folded[AStartIndex] := false;
end;
procedure TSynEditStringList.MaybeUnfoldAboveNewLine(ANewLineIndex : Integer);
begin
exit;
if ANewLineIndex = 0 then exit;
if (FoldType[ANewLineIndex - 1] = cfCollapsed)
and not Folded[ANewLineIndex - 1] then begin
FoldType[ANewLineIndex - 1] := cfExpanded;
// ScanFrom will override this, unless we moved a "begin" from the previous line
FoldType[ANewLineIndex] := cfCollapsed;
if Assigned(fOnFoldChanged) then
fOnFoldChanged(ANewLineIndex - 1);
end;
end;
{$ENDIF}
procedure TSynEditStringList.SetCapacity(NewCapacity: integer);