diff --git a/components/synedit/synedithighlighter.pp b/components/synedit/synedithighlighter.pp index 9e60643580..a7fb02c366 100644 --- a/components/synedit/synedithighlighter.pp +++ b/components/synedit/synedithighlighter.pp @@ -153,6 +153,7 @@ type fAttrChangeHooks: TSynNotifyEventChain; {$IFDEF SYN_LAZARUS} FCapabilities: TSynHighlighterCapabilities; + FCurrentLines: TSynEditStrings; {$ENDIF} fUpdateCount: integer; //mh 2001-09-13 fEnabled: Boolean; @@ -176,7 +177,6 @@ type procedure SetDefaultFilter(Value: string); virtual; procedure SetSampleSource(Value: string); virtual; // code fold - only valid if hcCodeFolding in Capabilities - function GetLastLineCodeFoldLevelFix: integer; virtual; public procedure DefHighlightChange(Sender: TObject); {$IFNDEF SYN_CPPB_1} class {$ENDIF} @@ -220,14 +220,12 @@ type property IdentChars: TSynIdentChars read GetIdentChars; property WordBreakChars: TSynIdentChars read fWordBreakChars write SetWordBreakChars; property LanguageName: string read GetLanguageName; + property CurrentLines: TSynEditStrings read FCurrentLines write FCurrentLines; - // folding - Function GetWordTriplet(LogicalCaret: TPoint; Lines: TSynEditStrings; - out Y1, XB1, XE1, Y2, XB2, XE2, Y3, XB3, XE3: Integer): Boolean; virtual; (* Methds for folding *) function MinimumCodeFoldBlockLevel: integer; virtual; function CurrentCodeFoldBlockLevel: integer; virtual; - property LastLineCodeFoldLevelFix: integer read GetLastLineCodeFoldLevelFix; + function LastLineCodeFoldLevelFix: integer; virtual; public property AttrCount: integer read GetAttribCount; property Attribute[idx: integer]: TSynHighlighterAttributes @@ -1087,13 +1085,6 @@ begin fAttrChangeHooks.Remove(ANotifyEvent); end; -function TSynCustomHighlighter.GetWordTriplet(LogicalCaret: TPoint; - Lines: TSynEditStrings; out Y1, XB1, XE1, Y2, XB2, XE2, Y3, XB3, XE3: Integer - ): Boolean; -begin - Result := False; -end; - function TSynCustomHighlighter.MinimumCodeFoldBlockLevel: integer; begin Result := 0; @@ -1111,7 +1102,7 @@ begin end; end; -function TSynCustomHighlighter.GetLastLineCodeFoldLevelFix: integer; +function TSynCustomHighlighter.LastLineCodeFoldLevelFix: integer; begin Result := 0; end; diff --git a/components/synedit/synedithighlighterfoldbase.pas b/components/synedit/synedithighlighterfoldbase.pas index edeb7602aa..bdb0c01004 100644 --- a/components/synedit/synedithighlighterfoldbase.pas +++ b/components/synedit/synedithighlighterfoldbase.pas @@ -94,6 +94,20 @@ type TSynCustomHighlighterRanges = class; + TSynFoldAction = (sfaOpen, // At this node a new Fold can start + sfaClose, // At this node a fold ends + sfaMarkup, // This node can be highlighted, by the matching Word-Pair Markup + sfaInvalid // Wrong Index + ); + TSynFoldActions = set of TSynFoldAction; + + TSynFoldNodeInfo = record + LogXStart, LogXEnd: Integer; // -1 previous line + FoldLvlStart, FoldLvlEnd: Integer; + FoldAction: TSynFoldActions; + FoldType: Pointer; + end; + { TSynCustomFoldHighlighter } TSynCustomFoldHighlighter = class(TSynCustomHighlighter) @@ -104,6 +118,8 @@ type protected FMinimumCodeFoldBlockLevel: integer; protected + function GetFoldNodeInfo(Line, Index: Integer): TSynFoldNodeInfo; virtual; + function GetFoldNodeInfoCount(Line: Integer): Integer; virtual; property CodeFoldRange: TSynCustomHighlighterRange read FCodeFoldRange; function GetRangeClass: TSynCustomHighlighterRangeClass; virtual; function TopCodeFoldBlockType: Pointer; @@ -125,6 +141,14 @@ type function MinimumCodeFoldBlockLevel: integer; override; function CurrentCodeFoldBlockLevel: integer; override; + + // requires CurrentLines; + function MinimumFoldLevel(Index: Integer): integer; virtual; abstract; + function EndFoldLevel(Index: Integer): integer; virtual; abstract; + function LastLineFoldLevelFix(Index: Integer): integer; virtual; abstract; + + property FoldNodeInfo[Line, Index: Integer]: TSynFoldNodeInfo read GetFoldNodeInfo; + property FoldNodeInfoCount[Line: Integer]: Integer read GetFoldNodeInfoCount; end; TSynCustomHighlighterClass = class of TSynCustomFoldHighlighter; @@ -260,6 +284,16 @@ begin Result:=0; end; +function TSynCustomFoldHighlighter.GetFoldNodeInfoCount(Line: Integer): Integer; +begin + Result := 0; +end; + +function TSynCustomFoldHighlighter.GetFoldNodeInfo(Line, Index: Integer): TSynFoldNodeInfo; +begin + Result.FoldAction := [sfaInvalid]; +end; + function TSynCustomFoldHighlighter.GetRangeClass: TSynCustomHighlighterRangeClass; begin Result:=TSynCustomHighlighterRange; diff --git a/components/synedit/syneditmarkupwordgroup.pp b/components/synedit/syneditmarkupwordgroup.pp index 369eab5717..9ba7aaba7b 100644 --- a/components/synedit/syneditmarkupwordgroup.pp +++ b/components/synedit/syneditmarkupwordgroup.pp @@ -27,7 +27,7 @@ interface uses Classes, SysUtils, Graphics, SynEditMarkup, SynEditMiscClasses, Controls, - LCLProc, SynEditHighlighter; + LCLProc, SynEditHighlighter, SynEditHighlighterFoldBase; type @@ -108,8 +108,92 @@ end; procedure TSynEditMarkupWordGroup.FindMatchingWords(PhysCaret: TPoint; out Word1, Word2, Word3: TWordPoint); +var + LCnt: Integer; + HL: TSynCustomFoldHighlighter; + + function FindEndNode(StartNode: TSynFoldNodeInfo; + var YIndex, NIndex: Integer): TSynFoldNodeInfo; + begin + repeat + inc(NIndex); + Result := HL.FoldNodeInfo[YIndex, NIndex]; + until (sfaInvalid in Result.FoldAction) or + (Result.FoldLvlEnd <= StartNode.FoldLvlStart); + if not (sfaInvalid in Result.FoldAction) then + exit; + + inc(YIndex); + while (YIndex < LCnt) and + (HL.MinimumFoldLevel(YIndex) > StartNode.FoldLvlStart) do + inc(YIndex); + if YIndex = LCnt then + exit; + + NIndex := -1; + repeat + inc(NIndex); + Result:= HL.FoldNodeInfo[YIndex, NIndex]; + until (sfaInvalid in Result.FoldAction) or + (Result.FoldLvlEnd <= StartNode.FoldLvlStart); + if (Result.LogXEnd = 0) then + Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?) + end; + + function FindStartNode(EndNode: TSynFoldNodeInfo; + var YIndex, NIndex: Integer): TSynFoldNodeInfo; + begin + repeat + dec(NIndex); + Result := HL.FoldNodeInfo[YIndex, NIndex]; + until (sfaInvalid in Result.FoldAction) or + (Result.FoldLvlStart <= EndNode.FoldLvlEnd); + if not(sfaInvalid in Result.FoldAction) then + exit; + + dec(YIndex); + while (YIndex >= 0) and (HL.MinimumFoldLevel(YIndex) > EndNode.FoldLvlEnd) do + dec(YIndex); + if YIndex < 0 then + exit; + NIndex := HL.FoldNodeInfoCount[YIndex]; + repeat + dec(NIndex); + Result:= HL.FoldNodeInfo[YIndex, NIndex]; + until (sfaInvalid in Result.FoldAction) or + (Result.FoldLvlStart <= EndNode.FoldLvlEnd); + end; + + function FindMultiNode(OrigNode: TSynFoldNodeInfo; + var YIndex, NIndex: Integer): TSynFoldNodeInfo; + var + i: LongInt; + begin + i := NIndex; + repeat + dec(NIndex); + Result := HL.FoldNodeInfo[YIndex, NIndex]; + if (sfaMarkup in Result.FoldAction) and + (Result.LogXStart = OrigNode.LogXStart) and (Result.LogXEnd > 0) + then + exit; + until (sfaInvalid in Result.FoldAction) or (Result.LogXStart <> OrigNode.LogXStart); + NIndex := i; + repeat + inc(NIndex); + Result := HL.FoldNodeInfo[YIndex, NIndex]; + if (sfaMarkup in Result.FoldAction) and + (Result.LogXStart = OrigNode.LogXStart) and (Result.LogXEnd > 0) + then + exit; + until (sfaInvalid in Result.FoldAction) or (Result.LogXStart <> OrigNode.LogXStart); + Result.FoldAction := [sfaInvalid]; + end; + var LogCaretXY: TPoint; + i, i2, i3, y, y1, y2: integer; + Node1, Node2, Node3: TSynFoldNodeInfo; begin Word1.Y := -1; Word2.Y := -1; @@ -119,34 +203,86 @@ begin then Exit; - // Check for Begin-End like pairs - if not assigned(FHighlighter) then Exit; - LogCaretXY := TSynEdit(SynEdit).PhysicalToLogicalPos(PhysCaret); - if FHighlighter.GetWordTriplet(LogCaretXY, Lines, - Word1.Y, Word1.X, Word1.X2, - Word3.Y, Word3.X, Word3. X2, - Word2.Y, Word2.X, Word2. X2 - ) then - begin - if Word1.Y > 0 then begin - Word1.X := TSynEdit(SynEdit).LogicalToPhysicalPos(Point(Word1.X, Word1.Y)).X; - Word1.X2 := TSynEdit(SynEdit).LogicalToPhysicalPos(Point(Word1.X2, Word1.Y)).X; - end; - if Word2.Y > 0 then begin - Word2.X := TSynEdit(SynEdit).LogicalToPhysicalPos(Point(Word2.X, Word2.Y)).X; - Word2.X2 := TSynEdit(SynEdit).LogicalToPhysicalPos(Point(Word2.X2, Word2.Y)).X; - end; - if Word3.Y > 0 then begin - Word3.X := TSynEdit(SynEdit).LogicalToPhysicalPos(Point(Word3.X, Word3.Y)).X; - Word3.X2 := TSynEdit(SynEdit).LogicalToPhysicalPos(Point(Word3.X2, Word3.Y)).X; - end; + if not (FHighlighter is TSynCustomFoldHighlighter) then exit; + hl := TSynCustomFoldHighlighter(FHighlighter); + LogCaretXY := TSynEdit(SynEdit).PhysicalToLogicalPos(PhysCaret); + y := LogCaretXY.Y - 1; + LCnt := Lines.Count; + HL.CurrentLines := Lines; + i := 0; + repeat + Node1 := HL.FoldNodeInfo[y, i]; + inc(i); + until (sfaInvalid in Node1.FoldAction) or + ((Node1.LogXEnd >= LogCaretXY.X - 1) and (Node1.LogXEnd > 0)); + while not(Node1.FoldAction * [sfaInvalid, sfaMarkup] <> []) + and (Node1.LogXStart <= LogCaretXY.X - 1) do + begin + Node1 := HL.FoldNodeInfo[y, i]; + inc(i); + end; + if (Node1.LogXStart > LogCaretXY.X - 1) or not(sfaMarkup in Node1.FoldAction) then + exit; + dec(i); + + if sfaOpen in Node1.FoldAction then begin + y1 := y; + Node2 := FindEndNode(Node1, y, i); + if (sfaInvalid in Node2.FoldAction) then + exit; + y2 := y; + i2 := i; + i3 := i; + Node3 := FindMultiNode(Node2, y, i3); + end else begin + Node2 := Node1; + i3 := i; + Node3 := FindMultiNode(Node2, y, i3); + y2 := y; + i2 := i; + Node1 := FindStartNode(Node2, y, i); + if (sfaInvalid in Node1.FoldAction) then + exit; + y1 := y; end; - // In Case GetWordTriplet did set them - Word1.Y := -1; - Word2.Y := -1; - Word3.Y := -1; + y := y2; + if not(sfaInvalid in Node3.FoldAction) then + Node3 := FindStartNode(Node3, y, i); + + Word1.Y := y1 + 1; + Word1.X := Node1.LogXStart + 1; + Word1.X2 := Node1.LogXEnd + 1; + Word2.Y := y2 + 1; + Word2.X := Node2.LogXStart + 1; + Word2.X2 := Node2.LogXEnd + 1; + if not(sfaInvalid in Node3.FoldAction) then begin + Word3 := Word2; + if i3 > i2 then begin + Word2 := Word1; + Word1.Y := y + 1; + Word1.X := Node3.LogXStart + 1; + Word1.X2 := Node3.LogXEnd + 1; + end else begin + Word2.Y := y + 1; + Word2.X := Node3.LogXStart + 1; + Word2.X2 := Node3.LogXEnd + 1; + end; + end; + + if Word1.Y > 0 then begin + Word1.X := TSynEdit(SynEdit).LogicalToPhysicalPos(Point(Word1.X, Word1.Y)).X; + Word1.X2 := TSynEdit(SynEdit).LogicalToPhysicalPos(Point(Word1.X2, Word1.Y)).X; + end; + if Word2.Y > 0 then begin + Word2.X := TSynEdit(SynEdit).LogicalToPhysicalPos(Point(Word2.X, Word2.Y)).X; + Word2.X2 := TSynEdit(SynEdit).LogicalToPhysicalPos(Point(Word2.X2, Word2.Y)).X; + end; + if Word3.Y > 0 then begin + Word3.X := TSynEdit(SynEdit).LogicalToPhysicalPos(Point(Word3.X, Word3.Y)).X; + Word3.X2 := TSynEdit(SynEdit).LogicalToPhysicalPos(Point(Word3.X2, Word3.Y)).X; + end; end; procedure TSynEditMarkupWordGroup.DoCaretChanged(OldCaret: TPoint); diff --git a/components/synedit/synhighlighterpas.pp b/components/synedit/synhighlighterpas.pp index 8571f7cfd0..03578760f5 100644 --- a/components/synedit/synhighlighterpas.pp +++ b/components/synedit/synhighlighterpas.pp @@ -57,7 +57,7 @@ uses {$ELSE} Windows, Messages, {$ENDIF} - Classes, Registry, Controls, SynEditHighlighterFoldBase, + Classes, Registry, Controls, SynEditHighlighterFoldBase, SynEditMiscProcs, SynEditTypes, SynEditHighlighter, SynEditTextBuffer, SynEditTextBase; type @@ -195,6 +195,10 @@ type {$ENDIF} fD4syntax: boolean; {$IFDEF SYN_LAZARUS} + FCatchNodeInfo: Boolean; + FNodeInfoLine, FNodeInfoCount: Integer; + FNodeInfoList: Array of TSynFoldNodeInfo; + procedure GrowNodeInfoList; function GetPasCodeFoldRange: TSynPasSynRange; procedure SetCompilerMode(const AValue: TPascalCompilerMode); function TextComp(aText: PChar): Boolean; @@ -322,7 +326,11 @@ type procedure SymbolProc; procedure UnknownProc; procedure SetD4syntax(const Value: boolean); + procedure InitNode(var Node: TSynFoldNodeInfo; EndOffs: Integer; + ABlockType: TPascalCodeFoldBlockType); protected + function GetFoldNodeInfo(Line, Index: Integer): TSynFoldNodeInfo; override; + function GetFoldNodeInfoCount(Line: Integer): Integer; override; function GetIdentChars: TSynIdentChars; override; function IsFilterStored: boolean; override; //mh 2000-10-08 procedure CreateRootCodeFoldBlock; override; @@ -334,7 +342,6 @@ type function GetRangeClass: TSynCustomHighlighterRangeClass; override; {$ENDIF} procedure EndCodeFoldBlockLastLine; - function GetLastLineCodeFoldLevelFix: integer; override; property PasCodeFoldRange: TSynPasSynRange read GetPasCodeFoldRange; public {$IFNDEF SYN_CPPB_1} class {$ENDIF} @@ -365,11 +372,14 @@ type procedure EnumUserSettings(settings: TStrings); override; //code fold + function LastLineCodeFoldLevelFix: integer; override; {$IFDEF SYN_LAZARUS} function TopPascalCodeFoldBlockType: TPascalCodeFoldBlockType; {$ENDIF} - Function GetWordTriplet(LogicalCaret: TPoint; Lines: TSynEditStrings; - out Y1, XB1, XE1, Y2, XB2, XE2, Y3, XB3, XE3: Integer): Boolean; override; + + function MinimumFoldLevel(Index: Integer): integer; override; + function EndFoldLevel(Index: Integer): integer; override; + function LastLineFoldLevelFix(Index: Integer): integer; override; published property AsmAttri: TSynHighlighterAttributes read fAsmAttri write fAsmAttri; property CommentAttri: TSynHighlighterAttributes read fCommentAttri @@ -746,6 +756,13 @@ begin //DebugLn(['TSynPasSyn.SetCompilerMode FCompilerMode=',ord(FCompilerMode),' FNestedComments=',FNestedComments]); end; +procedure TSynPasSyn.GrowNodeInfoList; +begin + if FNodeInfoCount < length(FNodeInfoList) then + exit; + SetLength(FNodeInfoList, FNodeInfoCount + Max(10, FNodeInfoCount div 4)); +end; + function TSynPasSyn.GetPasCodeFoldRange: TSynPasSynRange; begin Result := TSynPasSynRange(CodeFoldRange); @@ -1646,6 +1663,7 @@ begin fRange := []; fAsmStart := False; fDefaultFilter := SYNS_FilterPascal; + FNodeInfoLine := -1; end; { Create } procedure TSynPasSyn.SetLine(const NewValue: string; LineNumber:Integer); @@ -1663,8 +1681,10 @@ begin fLine := PChar(NewValue); Run := 0; {$ENDIF} + FNodeInfoLine := -1; fLineNumber := LineNumber; - Next; + if not FCatchNodeInfo then + Next; end; { SetLine } procedure TSynPasSyn.AddressOpProc; @@ -2210,6 +2230,7 @@ begin {$ELSE} fRange := TRangeStates(PtrUInt(Value)); {$ENDIF} + FNodeInfoLine := -1; FStartCodeFoldBlockLevel := FMinimumCodeFoldBlockLevel; end; @@ -2259,189 +2280,55 @@ begin Result := TPascalCodeFoldBlockType(PtrUInt(p)); end; -// ToDO: pass in Max for the search lines (Begin must be found always) -function TSynPasSyn.GetWordTriplet(LogicalCaret: TPoint; Lines: TSynEditStrings; - out Y1, XB1, XE1, Y2, XB2, XE2, Y3, XB3, XE3: Integer): Boolean; - - function GetLevelBeforeCaret: Integer; - var - l: Integer; - begin - SetRange(Lines.Ranges[LogicalCaret.Y - 1]); - FMinimumCodeFoldBlockLevel := CurrentCodeFoldBlockLevel; - l := CurrentCodeFoldBlockLevel; - SetLine(Lines[LogicalCaret.Y - 1], LogicalCaret.Y - 1); - if (MinimumCodeFoldBlockLevel < CurrentCodeFoldBlockLevel) - then Result := MinimumCodeFoldBlockLevel - else Result := l; - while (Run <= LogicalCaret.x - 1) and not GetEol do begin - if (Run = LogicalCaret.x-1) and (Result <> CurrentCodeFoldBlockLevel) then - break; - FMinimumCodeFoldBlockLevel := CurrentCodeFoldBlockLevel; - l := CurrentCodeFoldBlockLevel; - Next; - if (MinimumCodeFoldBlockLevel < CurrentCodeFoldBlockLevel) - then Result := MinimumCodeFoldBlockLevel - else Result := l; - end; - end; - - function FindBegin(EndY, EndX, SeekLevel: Integer; out X1, X2: Integer; - out FoldType: TPascalCodeFoldBlockType): integer; // Returns Line - var - MinLvl: Integer; - begin - Result := EndY; - MinLvl := 0; // Search the current line always - repeat - SetRange(Lines.Ranges[Result - 1]); - //PasCodeFoldRange.MinimumCodeFoldBlockLevel; - if (EndX > 0) and (MinLvl <= SeekLevel) then begin - // Search in current Line - X1 := -1; - // Use MinimumCodefoldlevel to detect theimplicit end of "var"/"type" blocks at the next "begin" - FMinimumCodeFoldBlockLevel := CurrentCodeFoldBlockLevel; - MinLvl := PasCodeFoldRange.MinimumCodeFoldBlockLevel; // Before SetLine = LastLine's MinLvl - SetLine(Lines[Result - 1], Result - 1); - while (Run <= EndX) and not GetEol do begin - if (MinimumCodeFoldBlockLevel <= SeekLevel) then begin - X1 := fTokenPos; - X2 := Run; - FoldType := TopPascalCodeFoldBlockType; - end; - FMinimumCodeFoldBlockLevel := CurrentCodeFoldBlockLevel; - Next; - end; - if X1 >= 0 then exit; - end - else - MinLvl := PasCodeFoldRange.MinimumCodeFoldBlockLevel; // Before SetLine = LastLine's MinLvl - // Search previous lines - EndX := MaxInt; - dec(Result); - until Result = 0; // should always exit before - end; - - function FindEnd(BeginY, BeginX2, SeekLevel: Integer; - out X1, X2, LvlBefore: Integer): integer; // Returns Line - var - c, MinLvl : Integer; - begin - Result := BeginY; - c := Lines.Count; - MinLvl := 0; // CurrentLine always has samller Minlevel - repeat - SetRange(Lines.Ranges[Result-1]); - if (MinLvl < SeekLevel) then begin - // Search in current Line - LvlBefore := CurrentCodeFoldBlockLevel; - SetLine(Lines[Result - 1], Result - 1); - if (FTokenPos < BeginX2) then - begin - while (FTokenPos < BeginX2) and not GetEol do - Next; - FMinimumCodeFoldBlockLevel := CurrentCodeFoldBlockLevel; - end; - while not GetEol do - begin - if CurrentCodeFoldBlockLevel < SeekLevel then - begin - X1 := fTokenPos; - X2 := Run; - exit - end - else if (MinimumCodeFoldBlockLevel < SeekLevel) or - (CurrentCodeFoldBlockLevel + LastLineCodeFoldLevelFix < SeekLevel) - then - exit(-1); // This block ended in the previous line (implicit end) - LvlBefore := CurrentCodeFoldBlockLevel; - Next; - end; - end; - // Search next lines - BeginX2 := 0; - inc(Result); - if Result < c-1 then begin - SetRange(Lines.Ranges[Result]); - MinLvl := PasCodeFoldRange.MinimumCodeFoldBlockLevel; - end else begin - MinLvl := 0; - end; - until Result >= c; - Result := -1; - end; - +function TSynPasSyn.MinimumFoldLevel(Index: Integer): integer; var - CurStartLevel, CurEndLevel, CurStartPosX, CurEndPosX, CurPosY: Integer; - EndStartLevel: Integer; - ft, ft2: TPascalCodeFoldBlockType; + r: Pointer; begin - Result := False; - - CurPosY := LogicalCaret.Y; - CurStartLevel := GetLevelBeforeCaret; - CurEndLevel := CurrentCodeFoldBlockLevel; - CurStartPosX := fTokenPos; // 0 based - CurEndPosX := Run; // 0 based - - IF CurEndLevel > CurStartLevel then begin - // block open: begin or middle - if not (TopPascalCodeFoldBlockType in PascalWordTrippletRanges) then - exit; - - Y1 := CurPosY; - XB1 := CurStartPosX; - XE1 := CurEndPosX; - - Y3 := FindEnd(CurPosY, CurEndPosX, CurEndLevel, XB3, XE3, EndStartLevel); - if Y3 < 0 then exit(false); - - Result := true; - if (EndStartLevel - CurrentCodeFoldBlockLevel = 1) then begin; - Y2:=-2; - end else begin - if CurStartLevel = CurrentCodeFoldBlockLevel - then Y2 := FindBegin(Y3, XB3, CurrentCodeFoldBlockLevel+1, XB2, XE2, ft) - else Y2 := FindBegin(Y3, XB3, CurrentCodeFoldBlockLevel, XB2, XE2, ft); - if not (ft in PascalWordTrippletRanges) then - Y2 := -1; - end; - end + if Index = CurrentLines.Count - 1 then exit(0); + r := CurrentLines.Ranges[Index + 1]; // stored as the start of the next line + if (r <> nil) and (r <> NullRange) then + Result := TSynPasSynRange(r).MinimumCodeFoldBlockLevel else - if CurStartLevel - CurEndLevel = 1 then begin - // block end, 1 lvl - Y1 := FindBegin(CurPosY, CurStartPosX, CurEndLevel, XB1, XE1, ft); - if not (ft in PascalWordTrippletRanges) then - exit; - Y2 := CurPosY; - XB2 := CurStartPosX; - XE2 := CurEndPosX; - Y3 := -2; - Result := true; - end - else - if CurEndLevel < CurStartLevel then begin - // block end, 2 lvl - Y2 := FindBegin(CurPosY, CurStartPosX, CurEndLevel+1, XB2, XE2, ft); - Y1 := FindBegin(Y2, XB2, CurEndLevel, XB1, XE1, ft2); - if not (ft in PascalWordTrippletRanges) then - Y2 := -1; - if not (ft2 in PascalWordTrippletRanges) then - Y1 := -1; - if (Y2 = -1) and (Y1 = -1) then - exit; - Y3 := CurPosY; - XB3 := CurStartPosX; - XE3 := CurEndPosX; - Result := true; - end; + Result := 0; +end; - inc(XB1); - inc(XE1); - inc(XB2); - inc(XE2); - inc(XB3); - inc(XE3); +function TSynPasSyn.EndFoldLevel(Index: Integer): integer; +var + r: Pointer; +begin + if Index = CurrentLines.Count - 1 then exit(0); + r := CurrentLines.Ranges[Index + 1]; // stored as the start of the next line + if (r <> nil) and (r <> NullRange) then + Result := TSynPasSynRange(r).CodeFoldStackSize + else + Result := 0; +end; + +function TSynPasSyn.LastLineFoldLevelFix(Index: Integer): integer; +var + r: Pointer; +begin + if Index = CurrentLines.Count - 1 then exit(0); + r := CurrentLines.Ranges[Index + 1]; // stored as the start of the next line + if (r <> nil) and (r <> NullRange) then + Result := TSynPasSynRange(r).LastLineCodeFoldLevelFix + else + Result := 0; +end; + + +procedure TSynPasSyn.InitNode(var Node: TSynFoldNodeInfo; EndOffs: Integer; + ABlockType: TPascalCodeFoldBlockType); +begin + Node.LogXStart := Run; + Node.LogXEnd := Run + fStringLen; + Node.FoldLvlStart := CurrentCodeFoldBlockLevel; + Node.FoldLvlEnd := CurrentCodeFoldBlockLevel + EndOffs; + Node.FoldType := Pointer(PtrInt(ABlockType)); + if ABlockType in PascalWordTrippletRanges then + Node.FoldAction := [sfaMarkup] + else + Node.FoldAction := []; end; function TSynPasSyn.StartPascalCodeFoldBlock( @@ -2451,15 +2338,29 @@ var p: PtrInt; begin p := 0; + if FCatchNodeInfo and not SubBlock then begin // exclude subblocks, because they do not increase the foldlevel yet + GrowNodeInfoList; + InitNode(FNodeInfoList[FNodeInfoCount], +1, ABlockType); + if not SubBlock then + include(FNodeInfoList[FNodeInfoCount].FoldAction, sfaOpen); + inc(FNodeInfoCount); + end; if SubBlock then p := PtrInt(CountPascalCodeFoldBlockOffset); Result:=TSynCustomCodeFoldBlock( - inherited StartCodeFoldBlock(p+Pointer(PtrInt(ABlockType)), not SubBlock)); + inherited StartCodeFoldBlock(p+Pointer(PtrInt(ABlockType)), not SubBlock)); end; procedure TSynPasSyn.EndCodeFoldBlock(DecreaseLevel: Boolean); begin DecreaseLevel := TopCodeFoldBlockType < CountPascalCodeFoldBlockOffset; + if FCatchNodeInfo and DecreaseLevel then begin // exclude subblocks, because they do not increase the foldlevel yet + GrowNodeInfoList; + InitNode(FNodeInfoList[FNodeInfoCount], -1, TopPascalCodeFoldBlockType); + if DecreaseLevel then + include(FNodeInfoList[FNodeInfoCount].FoldAction, sfaClose); + inc(FNodeInfoCount); + end; inherited EndCodeFoldBlock(DecreaseLevel); end; @@ -2468,9 +2369,9 @@ begin if TopPascalCodeFoldBlockType <> cfbtBeginEnd then exit; while TopPascalCodeFoldBlockType = cfbtBeginEnd do - EndCodeFoldBlock; + EndCodeFoldBlockLastLine; if TopPascalCodeFoldBlockType = cfbtProcedure then - EndCodeFoldBlock; // This procedure did have a begin/end block, so it must end too + EndCodeFoldBlockLastLine; // This procedure did have a begin/end block, so it must end too end; procedure TSynPasSyn.EndCodeFoldBlockLastLine; @@ -2484,12 +2385,45 @@ begin end; end; +function TSynPasSyn.GetFoldNodeInfo(Line, Index: Integer): TSynFoldNodeInfo; +var + i: LongInt; +begin + if FNodeInfoLine <> Line then begin + FCatchNodeInfo := True; + FNodeInfoCount := 0; + SetRange(CurrentLines.Ranges[Line]); + SetLine(CurrentLines[Line], Line); + fStringLen := 0; + i := LastLineFoldLevelFix(Line); + while i < 0 do begin + EndCodeFoldBlock; + inc(i); + end; + NextToEol; + FCatchNodeInfo := False; + FNodeInfoLine := Line; + end; + + if (index < 0) or (index >= FNodeInfoCount) then + Result := inherited GetFoldNodeInfo(Line, Index) + else + Result := FNodeInfoList[Index]; +end; + +function TSynPasSyn.GetFoldNodeInfoCount(Line: Integer): Integer; +begin + if FNodeInfoLine <> Line then + GetFoldNodeInfo(Line, 0); + Result := FNodeInfoCount; +end; + function TSynPasSyn.GetRangeClass: TSynCustomHighlighterRangeClass; begin Result:=TSynPasSynRange; end; -function TSynPasSyn.GetLastLineCodeFoldLevelFix: integer; +function TSynPasSyn.LastLineCodeFoldLevelFix: integer; begin Result := TSynPasSynRange(CodeFoldRange).LastLineCodeFoldLevelFix; end;