test markup fold

not supported by Laz 1.4.2
This commit is contained in:
Basile Burg 2015-12-04 16:42:05 +01:00
parent 0f381fb8c3
commit e447a2be64
3 changed files with 515 additions and 4 deletions

View File

@ -0,0 +1,500 @@
unit SynEditMarkupFoldColors;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
LCLProc, SynEditHighlighter, SynEditHighlighterFoldBase;
type
PMarkupFoldColorInfo = ^TMarkupFoldColorInfo;
TMarkupFoldColorInfo = record
Y, X, X2: Integer;
ColorIdx: Integer;
Border : Boolean;
end;
TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
TSynFoldNodeInfos = array of TSynFoldNodeInfo; //for quick compare detection
{ TSynEditMarkupFoldColors }
TSynEditMarkupFoldColors = class(TSynEditMarkup)
private
FDefaultGroup: integer;
// Physical Position
FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
Colors : array of TColor;
CurrentY : integer; //??
FCaretY : integer; // flag identify for refresh begin______
FPrevCaretText : string; // flag identify for refresh begin______
procedure DoMarkupFoldAtRow(aRow: Integer);
procedure DoMarkupParentFoldAtRow(aRow: Integer);
function GetFoldHighLighter: TSynCustomFoldHighlighter;
protected
// Notifications about Changes to the text
procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
procedure DoCaretChanged(Sender: TObject); override;
public
constructor Create(ASynEdit : TSynEditBase);
function GetMarkupAttributeAtRowCol(const aRow: Integer;
const aStartCol: TLazSynDisplayTokenBound;
const {%H-}AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor; override;
procedure GetNextMarkupColAfterRowCol(const aRow: Integer;
const aStartCol: TLazSynDisplayTokenBound;
const {%H-}AnRtlInfo: TLazSynDisplayRtlInfo;
out ANextPhys, ANextLog: Integer); override;
procedure PrepareMarkupForRow(aRow : Integer); override;
property DefaultGroup : integer read FDefaultGroup write FDefaultGroup;
end;
implementation
uses
Forms {debug},
SynEdit,SynEditTypes, SynEditFoldedView, SynEditMiscProcs;
function CompareFI(Item1, Item2: Pointer): Integer;
begin
result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
end;
function SortLeftMostFI(a: TMarkupFoldColorInfos): TMarkupFoldColorInfos;
var
l : TFpList;
i : integer;
begin
l := TFpList.Create;
for i := 0 to Pred(Length(a)) do
l.Add( PMarkupFoldColorInfo(@a[i]) );
l.Sort(@CompareFI);
SetLength(result, Length(a));
for i := 0 to Pred(l.Count) do
result[i] := PMarkupFoldColorInfo(l[i])^;
l.Free;
end;
{ TSynEditMarkupFoldColors }
constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
begin
inherited Create(ASynEdit);
MarkupInfo.Foreground := clGreen;
MarkupInfo.Background := clNone; //clFuchsia;
MarkupInfo.Style := [];
MarkupInfo.StyleMask := [];
MarkupInfo.FrameEdges:= sfeLeft;//sfeBottom;//sfeAround;//
SetLength(Colors, 6);
Colors[0] := clRed;
Colors[1] := $000098F7; //orange
Colors[2] := $0022CC40; //green
Colors[3] := $00D1D54A; // $0098CC42; //teal
Colors[4] := $00FF682A; //blue
Colors[5] := $00CF00C4; //purple
end;
function TSynEditMarkupFoldColors.GetMarkupAttributeAtRowCol(
const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
var
i : integer;
begin
Result := nil;
if (CurrentY = aRow) then
for i := 0 to length(FHighlights)-1 do
with FHighlights[i] do
if (aStartCol.Logical >= x) and (aStartCol.Logical < X2) then
begin
if ColorIdx >= 0 then
begin
MarkupInfo.FrameColor:= clNone;
MarkupInfo.Foreground:= clNone;
Result := MarkupInfo;
MarkupInfo.SetFrameBoundsLog(x, x2);
if Border then
MarkupInfo.FrameColor:= Colors[ColorIdx]
else
MarkupInfo.Foreground := Colors[ColorIdx]
end;
break;
end
end;
procedure TSynEditMarkupFoldColors.GetNextMarkupColAfterRowCol(
const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
var i : integer;
begin
ANextLog := -1;
ANextPhys := -1;
if (CurrentY = aRow) then
for i := 0 to length(FHighlights)-1 do begin
if FHighlights[i].X <= aStartCol.Logical then
continue;
if FHighlights[i].X2 < aStartCol.Logical then
continue;
ANextLog := FHighlights[i].X;
break;
end;
end;
procedure TSynEditMarkupFoldColors.DoMarkupFoldAtRow(aRow: Integer);
procedure AddHighlight( ANode: TSynFoldNodeInfo );
var x,lvl : integer;
begin
//exit; //debug
x := Length(FHighlights);
SetLength(FHighlights, x+1);
with FHighlights[x] do begin
Border := False;
Y := ANode.LineIndex + 1;
X := ANode.LogXStart + 1;
X2 := ANode.LogXEnd + 1;
if sfaOpen in ANode.FoldAction then begin
lvl := ANode.FoldLvlStart;
//lvl := ANode.NestLvlStart; //http://forum.lazarus.freepascal.org/index.php/topic,30122.msg194841.html#msg194841
ColorIdx := lvl mod (length(Colors));
end
else
if sfaClose in ANode.FoldAction then begin
lvl := ANode.FoldLvlEnd;
ColorIdx := lvl mod (length(Colors));
end
else
ColorIdx := -1;
{if sfaOpen in ANode.FoldAction then
lvl := ANode.NestLvlStart
else
lvl := ANode.NestLvlEnd;
ColorIdx := lvl mod (length(Colors));
}
end;
end;
var
y,i : integer;
HL: TSynCustomFoldHighlighter;
NodeList: TLazSynFoldNodeInfoList;
TmpNode: TSynFoldNodeInfo;
begin
y := aRow -1;
HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
HL.CurrentLines := Lines;
HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
NodeList := HL.FoldNodeInfo[y];
NodeList.AddReference;
try
NodeList.ActionFilter := [
{sfaMarkup,}
sfaFold
//sfaFoldFold
//sfaFoldHide
//sfaSingleLine
//sfaMultiLine
//sfaOpen
];
//NodeList.FoldFlags:= [sfbIncludeDisabled];
i := 0;
repeat
TmpNode := NodeList[i];
//find till valid
while (sfaInvalid in TmpNode.FoldAction) and (i < NodeList.Count) do
begin
inc(i);
TmpNode := NodeList[i];
end;
if not (sfaInvalid in TmpNode.FoldAction) then
AddHighlight(TmpNode);
inc(i);
until i >= NodeList.Count;
finally
NodeList.ReleaseReference;
end;
end;
procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
var x,i,lvl : integer;
begin
//don't replace; don't add when already found
x := ANode.LogXStart + 1;
for i := 0 to Pred(length(FHighlights)) do
if FHighlights[i].X = x then
exit;
x := Length(FHighlights);
SetLength(FHighlights, x+1);
with FHighlights[x] do begin
Border := ANode.LineIndex + 1 <> aRow;
Y := aRow;//ANode.LineIndex + 1;
X := ANode.LogXStart + 1;
X2 := X+1; //ANode.LogXEnd + 1;
if sfaOpen in ANode.FoldAction then begin
lvl := ANode.FoldLvlStart;
ColorIdx := lvl mod (length(Colors));
end
else
if sfaClose in ANode.FoldAction then begin
lvl := ANode.FoldLvlEnd;
ColorIdx := lvl mod (length(Colors));
end
else
begin
ColorIdx := -1;
lvl := ANode.NestLvlStart;
ColorIdx := lvl mod (length(Colors));
end;
{
if sfaOpen in ANode.FoldAction then
lvl := ANode.NestLvlStart
else
lvl := ANode.NestLvlEnd;
//ColorIdx := ANode.NodeIndex mod (length(Colors));
lvl := ANode.NestLvlEnd;
//lvl := Longint(ANode.FoldTypeCompatible);
ColorIdx := lvl mod (length(Colors));
}
end;
end;
var
i,y: Integer;
Nest : TLazSynEditNestedFoldsList;
TmpNode: TSynFoldNodeInfo;
begin
y := aRow-1;
Nest := TLazSynEditNestedFoldsList.Create(@GetFoldHighLighter);
Nest.ResetFilter;
Nest.Clear;
Nest.Line := y;
Nest.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
Nest.FoldFlags := [];//[sfbIncludeDisabled]; //
Nest.IncludeOpeningOnLine := True; //False; //
//i := 0; while i < Nest.Count do
i := Nest.Count -1; while i >= 0 do //from right to left
begin
TmpNode := Nest.HLNode[i];
//find till valid
while (sfaInvalid in TmpNode.FoldAction) and (i < Nest.Count) do
begin
inc(i);
TmpNode := Nest.HLNode[i];
end;
if not (sfaInvalid in TmpNode.FoldAction) then
AddVerticalLine(TmpNode);
//inc(i);
dec(i);
end;
end;
procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
begin
CurrentY := aRow;
SetLength(FHighlights,0); //reset needed to prevent using of invalid area
if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
exit;
DoMarkupFoldAtRow(aRow);
DoMarkupParentFoldAtRow(aRow);
FHighlights := SortLeftMostFI(FHighlights);
end;
function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
begin
result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
end;
{.$define debug_FC_line_changed}
procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
ACountDiff: Integer);
{$ifdef debug_FC_line_changed}
var F : TCustomForm;
begin
F := GetParentForm(self.SynEdit);
if F <> nil then
//F.Caption := Format('Start:%d Endline:%d Diff:%d',[StartLine, EndLIne, ACountDiff]);
F.Caption := F.Caption + Caret.LineText
{$else}
function GetPairCloseFold(aRow, X : integer ): Integer;
var
y,i,LCnt : integer;
HL: TSynCustomFoldHighlighter;
NodeList: TLazSynFoldNodeInfoList;
TmpNode, CloseNode: TSynFoldNodeInfo;
function FindEndNode(StartNode: TSynFoldNodeInfo;
{var} YIndex, NIndex: Integer): TSynFoldNodeInfo;
function SearchLine(ALineIdx: Integer; var ANodeIdx: Integer): TSynFoldNodeInfo;
begin
NodeList.Line := ALineIdx;
repeat
inc(ANodeIdx);
Result := NodeList[ANodeIdx];
until (sfaInvalid in Result.FoldAction)
or (Result.NestLvlEnd <= StartNode.NestLvlStart);
end;
begin
Result := SearchLine(YIndex, NIndex);
if not (sfaInvalid in Result.FoldAction) then
exit;
inc(YIndex);
while (YIndex < LCnt) and
(HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
> StartNode.NestLvlStart)
do
inc(YIndex);
if YIndex = LCnt then
exit;
NIndex := -1;
Result := SearchLine(YIndex, NIndex);
if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
end;
begin
Result := -1;
y := aRow -1;
HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
HL.CurrentLines := Lines;
LCnt := Lines.Count;
HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
NodeList := HL.FoldNodeInfo[y];
NodeList.AddReference;
try
NodeList.ActionFilter := [sfaOpen];
i := 0;
repeat
TmpNode := NodeList[i];
if TmpNode.LogXStart < X-1 then
begin
inc(i);
continue;
end;
//find till valid
while (sfaInvalid in TmpNode.FoldAction) and (i < NodeList.Count) do
begin
inc(i);
TmpNode := NodeList[i];
end;
if not (sfaInvalid in TmpNode.FoldAction) then
begin
CloseNode := FindEndNode(TmpNode, y, i);
//AddHighlight(TmpNode);
Result := CloseNode.LineIndex;
exit;
end;
inc(i);
until i >= NodeList.Count;
finally
NodeList.ReleaseReference;
end;
end;
function IsFoldMoved( aRow: Integer ): integer;
var S : string;
i,n : integer;
begin
Result := -1;
n := -1;
S := Caret.LineText;
for i := 1 to Min(Length(S), Length(FPrevCaretText)) do
begin
if S[i] <> FPrevCaretText[i] then
begin
n := i;
break;
end;
end;
if n < 0 then exit;
Result := GetPairCloseFold(aRow, n);
//limit to screen bottom
if Result > 0 then
begin
inc(Result);//because sometime 'end' has trailing vertical line
with TCustomSynEdit(SynEdit) do
Result := min(Result, TopLine +LinesInWindow);// . .RowToScreenRow(i);
end;
end;
var
EndFoldLine,y : integer;
begin
if EndLine < 0 then exit; //already refreshed by syn
y := Caret.LineBytePos.y;
EndFoldLine := IsFoldMoved(y);
if EndFoldLine > 0 then
begin
InvalidateSynLines(y+1, EndFoldLine);
end;
FPrevCaretText := Caret.LineText;
// I found that almost anything has been repaint by the SynEdit,
// except the trailing space editing: we should repaint them here.
{$endif}
end;
procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
var Y : integer;
begin
Y := Caret.LineBytePos.y;
if Y = FCaretY then exit;
FCaretY := Y;
FPrevCaretText := Caret.LineText;
{$ifdef debug_FC_line_changed}
with GetParentForm(self.SynEdit) do
Caption:= Caret.LineText;
{$endif}
end;
end.

View File

@ -31,7 +31,7 @@
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<OtherUnitFiles Value="..\src"/>
<OtherUnitFiles Value="..\src;..\etc"/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Parsing>
@ -375,7 +375,7 @@
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<OtherUnitFiles Value="..\src"/>
<OtherUnitFiles Value="..\src;..\etc"/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<CodeGeneration>

View File

@ -8,7 +8,7 @@ uses
Classes, SysUtils, controls,lcltype, Forms, graphics, ExtCtrls, crc,
SynPluginSyncroEdit, SynCompletion, SynEditKeyCmds, LazSynEditText, SynEdit,
SynHighlighterLFM, SynEditHighlighter, SynEditMouseCmds, SynEditFoldedView,
SynEditMarks,
SynEditMarks, SynEditMarkup, SynEditMarkupFoldColors,
ce_common, ce_observer, ce_writableComponent, ce_d2syn, ce_txtsyn, ce_dialogs,
ce_sharedres;
@ -16,7 +16,7 @@ type
TCESynMemo = class;
TBreakPointModification = (bpAdded, bpRemoved);
TBreakPointModification = (bpAdded, bpRemoved);
// breakpoint added or removed
TBreakPointModifyEvent = procedure(sender: TCESynMemo; line: integer;
@ -117,6 +117,7 @@ type
fImages: TImageList;
fBreakPoints: TFPList;
fBreakpointEvent: TBreakPointModifyEvent;
fMarkupIndent: TSynEditMarkupFoldColors;
function getMouseFileBytePos: Integer;
procedure changeNotify(Sender: TObject);
procedure identifierToD2Syn;
@ -484,6 +485,16 @@ begin
fPositions := TCESynMemoPositions.create(self);
fMultiDocSubject := TCEMultiDocSubject.create;
//
(*fMarkupIndent:= TSynEditMarkupFoldColors.Create(self);
fMarkupIndent.Lines := TextBuffer;
fMarkupIndent.Enabled:=true;
fMarkupIndent.DefaultGroup:=0;
if (GetMarkupMgr <> nil) then
begin
TSynEditMarkupManager(GetMarkupMgr).AddMarkUp(fMarkupIndent, true);
end; *)
//
subjDocNew(TCEMultiDocSubject(fMultiDocSubject), self);
end;