View Issue Details

IDProjectCategoryView StatusLast Update
0030421LazarusPackagespublic2017-02-15 05:56
ReporterPascal RiekenbergAssigned ToMartin Friebe 
PrioritynormalSeverityminorReproducibilityN/A
Status closedResolutionfixed 
Product Version1.7 (SVN)Product Build 
Target Version1.8Fixed in Version1.8 
Summary0030421: Update to TSynEditMarkupFoldColors including correct invalidation
DescriptionI've updateted TSynEditMarkupFoldColors to work correct now.
Patch to Pascal highlighter is also needed. See previous reported issue.
TagsNo tags attached.
Fixed in Revision
LazTarget1.8
Widgetset
Attached Files
  • syneditmarkupfoldcoloring.pas.patch (28,670 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 52753)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -55,7 +55,8 @@
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText, fgl;
     
     type
     
    @@ -66,37 +67,38 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    +    FHighlighter: TSynCustomHighlighter;
         FNestList: TLazSynEditNestedFoldsList;
    +    FFirstCharacterColumn: Array of Byte;
         FDefaultGroup: integer;
          // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FHighlights: TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
         Colors : array of TColor;
     
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    +    FCurrentRow: integer;  // prepared Row
     
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
     
         function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    procedure SetHighlighter(AValue: TSynCustomHighlighter);
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -111,12 +113,54 @@
         procedure PrepareMarkupForRow(aRow : Integer); override;
         procedure EndMarkup; override;
         property DefaultGroup : integer read FDefaultGroup write SetDefaultGroup;
    +    property  Highlighter: TSynCustomHighlighter
    +      read FHighlighter write SetHighlighter;
       end;
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit,SynEditTypes, SynEditMiscProcs, Dialogs, strutils;
     
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  case PtrInt(p_FoldType) of
    +    00: Result := 'cfbtBeginEnd      ';
    +    01: Result := 'cfbtTopBeginEnd   ';
    +    02: Result := 'cfbtNestedComment ';
    +    03: Result := 'cfbtProcedure     ';
    +    04: Result := 'cfbtUses          ';
    +    05: Result := 'cfbtVarType       ';
    +    06: Result := 'cfbtLocalVarType  ';
    +    07: Result := 'cfbtClass         ';
    +    08: Result := 'cfbtClassSection  ';
    +    09: Result := 'cfbtUnitSection   ';
    +    10: Result := 'cfbtProgram       ';
    +    11: Result := 'cfbtUnit          ';
    +    12: Result := 'cfbtRecord        ';
    +    13: Result := 'cfbtTry           ';
    +    14: Result := 'cfbtExcept        ';
    +    15: Result := 'cfbtRepeat        ';
    +    16: Result := 'cfbtAsm           ';
    +    17: Result := 'cfbtCase          ';
    +    18: Result := 'cfbtIfDef         ';
    +    19: Result := 'cfbtRegion        ';
    +    20: Result := 'cfbtAnsiComment   ';
    +    21: Result := 'cfbtBorCommand    ';
    +    22: Result := 'cfbtSlashComment  ';
    +    23: Result := 'cfbtIfThen        ';
    +    24: Result := 'cfbtForDo         ';
    +    25: Result := 'cfbtWhileDo       ';
    +    26: Result := 'cfbtWithDo        ';
    +    27: Result := 'cfbtIfElse        ';
    +    28: Result := '// Internal type  ';
    +    29: Result := 'cfbtCaseElse (int)';
    +    30: Result := 'cfbtPackage (int) ';
    +    31: Result := 'cfbtNone (int)    ';
    +    else Result := Format('unknown %.2d        ', [PtrInt(p_FoldType)]);
    +  end;
    +end;
    +
    +
       {%region Sorting FoldInfo -fold}
       function CompareFI(Item1, Item2: Pointer): Integer;
       begin
    @@ -143,6 +187,12 @@
           result[i] := PMarkupFoldColorInfo(l[i])^;
          l.Free;
       end;
    +
    +  operator = (z1, z2: TSynFoldNodeInfo): boolean;
    +  begin
    +      Result := false;
    +  end;
    +
       {%endregion}
     
     { TSynEditMarkupFoldColors }
    @@ -151,6 +201,9 @@
     begin
       inherited Create(ASynEdit);
     
    +  if Assigned(Lines) then
    +    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +
       FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
       FNestList.ResetFilter;
       FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    @@ -157,6 +210,8 @@
       FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
       FNestList.IncludeOpeningOnLine := True; //False; //
     
    +  SetLength(FFirstCharacterColumn, 0);
    +
       MarkupInfo.Foreground := clGreen;
       MarkupInfo.Background := clNone; //clFuchsia;
       MarkupInfo.Style := [];
    @@ -174,6 +229,8 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
       FreeAndNil(FNestList);
       inherited Destroy;
     end;
    @@ -182,12 +239,15 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i,x2both , found, color: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if (FCurrentRow = aRow) then begin
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
     
         x2both := -3; //flag
    +    //found := -1;
    +    //color := -1;
         for i := 0 to length(FHighlights)-1 do
           with FHighlights[i] do
             if not Ignore
    @@ -194,11 +254,21 @@
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    +        and (aStartCol.Logical < X2) then begin
    +          //if (found >= 0)
    +          //and (found = x)
    +          //and (
    +          //  (ToPos(SrcNode.LineIndex) <> aRow)
    +          //  or not (sfaOpen in SrcNode.FoldAction)
    +          //) then
    +          //  Continue;
    +          //
    +          //if x > found then
    +          //  found := -1;
    +
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
               //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,13 +279,16 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    -            MarkupInfo.FrameColor:= Colors[ColorIdx];
    +          //if found < 0 then
    +            color := Colors[ColorIdx];
    +          if Border then begin
    +            MarkupInfo.FrameColor:= color;
                 MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    -            MarkupInfo.Foreground := Colors[ColorIdx];
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
    +            MarkupInfo.Foreground := color;
    +          end;
     
               //MarkupInfo.FrameEdges:= sfeAround; //debug
     
    @@ -227,7 +300,7 @@
                 MarkupInfo.FrameColor:= clBlue; //debug
               end;}
     
    -          //break;
    +          //found := x;
             end;
       end;
     end;
    @@ -237,9 +310,10 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    +  if (FCurrentRow = aRow)  then
       for i := 0 to length(FHighlights)-1  do
         with FHighlights[i] do
         begin
    @@ -258,16 +332,42 @@
       i,lvl,z : integer; //iterate parents fold
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    -  var x,j : integer;
    +  var
    +    j, p, s: integer;
    +    l: String;
    +
       begin
    +    // get column of first character in row
    +      // Fallback if Highlighter doesn't populate ColumnOfFirstCharInRow
    +    s := Length(FFirstCharacterColumn);
    +    if (s <= ANode.LineIndex)
    +    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
    +      l := SynEdit.Lines[ANode.LineIndex];
    +      p := 1;
    +      while not (l[p] in [#13, #10, #0])
    +      and (l[p] = #32) do inc(p);
    +      if p > 255 then p := 255;
    +      if s > ANode.LineIndex then begin
    +        FFirstCharacterColumn[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
         z := Length(FHighlights);
         SetLength(FHighlights, z+1);
         with FHighlights[z] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      //X  := ANode.LogXStart + 1;
    +      //X  := p;
    +      if s <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumn[ANode.LineIndex];
           Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      X2 := X + 1; //ANode.LogXEnd + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -274,7 +374,9 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
    +      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
         end;
       end;
     
    @@ -292,7 +394,7 @@
       end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
     
    @@ -318,14 +420,33 @@
           lvlB := lvl;
     
           if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +        inc(lvl)
    +      else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
             dec(lvl);
    +      if (FLastNode.LineIndex >= 0)
    +      and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +      and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +       inc(lvl);
     
           AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +      if (z > 0)
    +      and (FHighlights[z].X = FHighlights[z - 1].X) then begin
    +        // if child is on same x-pos keep level
    +        if sfaOutlineMergeLevelOnWrongCol in FHighlights[z].SrcNode.FoldAction then begin
    +          lvl := FHighlights[z - 1].Level;
    +          FHighlights[z].Level := lvl;
    +          FHighlights[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        end;
    +      end;
    +
    +      if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +      and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction) then
             inc(lvl);
     
    +      if sfaOpen in TmpNode.FoldAction then
    +        FLastNode := TmpNode;
    +
           lvlA := lvl;
     
           with FHighlights[z] do begin
    @@ -370,9 +491,9 @@
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -382,10 +503,10 @@
       y,i,j,lvlB,lvlA : integer;
       HL: TSynCustomFoldHighlighter;
       NodeList: TLazSynFoldNodeInfoList;
    -  TmpNode: TSynFoldNodeInfo;
    +  TmpNode, PrevNode: TSynFoldNodeInfo;
       Found : boolean;
     begin
    -  y := aRow -1;
    +  y := ToIdx(aRow);
     
       HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
       HL.CurrentLines := Lines;
    @@ -401,6 +522,7 @@
         if J >=0 then
           lvl := max(0,FHighlights[J].LevelAfter);
         i := 0;
    +    PrevNode.LineIndex := -2;
         repeat
           TmpNode := NodeList[i];
     
    @@ -416,15 +538,37 @@
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          if (FLastNode.LineIndex >= 0)
    +          and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +           inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          if (z > 0)
    +          and (FHighlights[z].X = FHighlights[z - 1].X) then
    +          begin
    +            // if child is on same x-pos keep level
    +            if (sfaClose in FHighlights[z].SrcNode.FoldAction)
    +            or (sfaOutlineMergeLevelOnWrongCol in FHighlights[z].SrcNode.FoldAction) then begin
    +              lvl := FHighlights[z - 1].Level;
    +              FHighlights[z].Level := lvl;
    +              FHighlights[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +            end;
    +          end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction) then
                 inc(lvl);
    +
               lvlA := lvl;
     
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
               with FHighlights[z] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
    @@ -437,28 +581,50 @@
               Found := False;
               for j := Length(FHighlights)-1 downto 0 do begin
                 with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FHighlights[j].Level;
    +                lvlA := FHighlights[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
    +            //FLastNode.LineIndex := -1;
    +            //for j := j - 1 downto 0 do begin
    +            //  with FHighlights[j].SrcNode do begin
    +            //    if (sfaOpen in FoldAction) then begin
    +            //      FLastNode := FHighlights[j].SrcNode;
    +            //      Break;
    +            //    end;
    +            //  end;
    +            //end;
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            //lvl := lvlA;
    +            with FHighlights[z] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FHighlights[j].X > FHighlights[z].X then begin
    +              for j := j to z - 1 do begin
    +                FHighlights[j] := FHighlights[j+1];
    +              end;
    +              SetLength(FHighlights, z);
    +            end;
               end;
     
               //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
                 //inc(lvl);
             end;
    +
    +        PrevNode := TmpNode;
           end;
     
           inc(i);
    @@ -470,23 +636,49 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, li, LastX, j: Integer;
    +  n: TMarkupFoldColorInfos;
    +  l: TList;
     begin
    -  CurrentY := aRow;
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  FCurrentRow := aRow;
       SetLength(FHighlights,0); //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    +  FLastNode.LineIndex := -1;
    +
       //DoMarkupFoldAtRow(aRow);
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
       //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  //FHighlights := SortLeftMostFI(FHighlights);
    +
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := length(FHighlights) - 1 downto 0 do begin
    +    if FHighlights[i].X > LastX then begin
    +      for j := i to length(FHighlights) - 2 do begin
    +        FHighlights[j] := FHighlights[j + 1];
    +      end;
    +      SetLength(FHighlights, length(FHighlights) - 1);
    +    end;
    +    LastX := FHighlights[i].X;
    +  end;
    +
    +  //DebugLn('Zeile %d', [aRow]);
    +  //for i := 0 to length(FHighlights) - 1 do with FHighlights[i] do begin
    +  //  DebugLn('   %.2d: x=%.03d l=%.5d %s %s %s %s lvl=%.2d - %.2d - %.2d', [i, X, ToPos(SrcNode.LineIndex), IfThen(sfaOpen in SrcNode.FoldAction, 'O', IfThen(sfaClose in SrcNode.FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in SrcNode.FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in SrcNode.FoldAction, '+', IfThen(sfaOutlineMergeParent in SrcNode.FoldAction, '-', ' ')) ,FoldTypeToStr(SrcNode.FoldType), LevelBefore, Level, LevelAfter]);
    +  //end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  //DebugLn('EndMarkup');
       inherited EndMarkup;
       FNestList.Clear; // for next markup start
     end;
    @@ -503,164 +695,180 @@
       FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
     end;
     
    -{.$define debug_FC_line_changed}
    -procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
    -  ACountDiff: Integer);
    -{$ifdef debug_FC_line_changed}
    -var F : TCustomForm;
    +procedure TSynEditMarkupFoldColors.SetHighlighter(AValue: TSynCustomHighlighter
    +  );
     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}
    +  if FHighlighter = AValue then Exit;
    +  FHighlighter := AValue;
     
    +  if not (FHighlighter is TSynCustomFoldHighlighter) then
    +    exit;
     
    +  FreeAndNil(FNestList);
     
    -  function GetPairCloseFold(aRow, X : integer  ): Integer;
    -  var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, TSynCustomFoldHighlighter(FHighlighter));
    +  FNestList.ResetFilter;
    +  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +  FNestList.IncludeOpeningOnLine := True; //False; //
    +end;
     
    -    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;
    +procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
    +  ACountDiff: Integer);
     
    -    begin
    -      Result := SearchLine(YIndex, NIndex);
    -      if not (sfaInvalid in Result.FoldAction) then
    -        exit;
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
    +  var
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +    pNestList.Line := pLine;
    +    lCount := pNestList.Count;
    +    SetLength(pList, lCount);
    +    lLineIdx := ToIdx(pLine);
    +    lAnz := 0;
    +    for i := 0 to lCount - 1 do begin
    +      lNode := pNestList.HLNode[i];
    +      if (sfaInvalid in lNode.FoldAction)
    +      or (
    +        (sfaOpen in lNode.FoldAction)
    +        and (lNode.LineIndex = lLineIdx)
    +      ) then
    +        Continue;
     
    -      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?)
    +      pList[i] := lNode;
    +      inc(lAnz);
         end;
    +    SetLength(pList, lAnz);
    +  end;
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    +var
    +  i, lMinAnz, lEndLine, j: integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +  HL: TSynCustomFoldHighlighter;
    +  lNodeList: TLazSynFoldNodeInfoList;
    +  lNode: TSynFoldNodeInfo;
    +  found: Boolean;
     
    -    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
    +begin
    +  //DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  if EndLine < 0 then
    +    EndLine := StartLine;
     
    -    NodeList := HL.FoldNodeInfo[y];
    -    NodeList.AddReference;
    -    try
    -      NodeList.ActionFilter := [sfaOpen];
    -      i := 0;
    -      repeat
    -        TmpNode := NodeList[i];
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do
    +    FFirstCharacterColumn[i] := 0;
     
    -        if TmpNode.LogXStart < X-1 then
    -        begin
    -          inc(i);
    -          continue;
    -        end;
    +  lEndLine := EndLine;
     
    -        //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;
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  FillNestList(lEndNestList, EndLine, FNestList);
     
    -        inc(i);
    -      until i >= NodeList.Count;
    -
    -    finally
    -      NodeList.ReleaseReference;
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         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;
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    //DebugLn('   x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
    +    HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    +    HL.CurrentLines := Lines;
    +    found := false;
    +    repeat
    +      inc(lEndLine);
    +      if lEndLine > Lines.Count then break;
    +      HL.FoldNodeInfo[ToIdx(lEndLine)].ClearFilter;
    +      lNodeList := HL.FoldNodeInfo[ToIdx(lEndLine)];
    +      lNodeList.AddReference;
    +      try
    +        lNodeList.ActionFilter := [sfaOutline, sfaClose];
    +        i := 0;
    +        repeat
    +          lNode := lNodeList[i];
    +          if not (sfaInvalid in lNode.FoldAction)
    +          and (lNode.FoldType = lEndNestList[0].FoldType)
    +          and (lNode.FoldGroup = lEndNestList[0].FoldGroup)
    +          and (lNode.NestLvlEnd = lEndNestList[0].NestLvlStart) then begin
    +            found := true;
    +            break;
    +          end;
    +          inc(i);
    +        until (i >= lNodeList.Count) or found;
    +      finally
    +        lNodeList.ReleaseReference;
           end;
    -    end;
    +    until found;
     
    -    if n < 0 then exit;
    +  end;
    +  if lEndLine > EndLine then begin
    +    //DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    InvalidateSynLines(EndLine + 1, lEndLine);
    +  end;
    +end;
     
    -    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;
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
     var
    -  EndFoldLine,y : integer;
    +  old: TSynEditStrings;
     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);
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // Änderung:
    +    // Remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
       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}
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // Änderung:
    +    if Assigned(AValue) then begin
    +      // Add Changehandler
    +      SetLength(FFirstCharacterColumn, AValue.Count);
    +      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    +    end else begin
    +      SetLength(FFirstCharacterColumn, 0);
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
    +var
    +  absCount,
    +  idx, i: Integer;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    -
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    +  //DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  idx := ToIdx(aIndex);
    +  if aCount < 0 then begin
    +    // Zeilen entfernt
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
    +      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
    +    end;
    +  end;
    +  SetLength(FFirstCharacterColumn, Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // Zeilen eingefügt
    +      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do FFirstCharacterColumn[i] := 0;
    +    end else begin
    +      // erste Zeilen werden eingefügt
    +      for i := 0 to Length(FFirstCharacterColumn) - 1 do FFirstCharacterColumn[i] := 0;
    +    end;
    +  end;
     end;
     
    -
    -
     end.
     
    
  • syneditmarkupfoldcoloring.pas_v2.patch (29,678 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 52753)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -55,7 +55,8 @@
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText, fgl;
     
     type
     
    @@ -66,37 +67,38 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    +    FHighlighter: TSynCustomHighlighter;
         FNestList: TLazSynEditNestedFoldsList;
    +    FFirstCharacterColumn: Array of Byte;
         FDefaultGroup: integer;
          // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FHighlights: TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
         Colors : array of TColor;
     
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    +    FCurrentRow: integer;  // prepared Row
     
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
     
         function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    procedure SetHighlighter(AValue: TSynCustomHighlighter);
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -111,12 +113,54 @@
         procedure PrepareMarkupForRow(aRow : Integer); override;
         procedure EndMarkup; override;
         property DefaultGroup : integer read FDefaultGroup write SetDefaultGroup;
    +    property  Highlighter: TSynCustomHighlighter
    +      read FHighlighter write SetHighlighter;
       end;
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit,SynEditTypes, SynEditMiscProcs, Dialogs, strutils;
     
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  case PtrInt(p_FoldType) of
    +    00: Result := 'cfbtBeginEnd      ';
    +    01: Result := 'cfbtTopBeginEnd   ';
    +    02: Result := 'cfbtNestedComment ';
    +    03: Result := 'cfbtProcedure     ';
    +    04: Result := 'cfbtUses          ';
    +    05: Result := 'cfbtVarType       ';
    +    06: Result := 'cfbtLocalVarType  ';
    +    07: Result := 'cfbtClass         ';
    +    08: Result := 'cfbtClassSection  ';
    +    09: Result := 'cfbtUnitSection   ';
    +    10: Result := 'cfbtProgram       ';
    +    11: Result := 'cfbtUnit          ';
    +    12: Result := 'cfbtRecord        ';
    +    13: Result := 'cfbtTry           ';
    +    14: Result := 'cfbtExcept        ';
    +    15: Result := 'cfbtRepeat        ';
    +    16: Result := 'cfbtAsm           ';
    +    17: Result := 'cfbtCase          ';
    +    18: Result := 'cfbtIfDef         ';
    +    19: Result := 'cfbtRegion        ';
    +    20: Result := 'cfbtAnsiComment   ';
    +    21: Result := 'cfbtBorCommand    ';
    +    22: Result := 'cfbtSlashComment  ';
    +    23: Result := 'cfbtIfThen        ';
    +    24: Result := 'cfbtForDo         ';
    +    25: Result := 'cfbtWhileDo       ';
    +    26: Result := 'cfbtWithDo        ';
    +    27: Result := 'cfbtIfElse        ';
    +    28: Result := '// Internal type  ';
    +    29: Result := 'cfbtCaseElse (int)';
    +    30: Result := 'cfbtPackage (int) ';
    +    31: Result := 'cfbtNone (int)    ';
    +    else Result := Format('unknown %.2d        ', [PtrInt(p_FoldType)]);
    +  end;
    +end;
    +
    +
       {%region Sorting FoldInfo -fold}
       function CompareFI(Item1, Item2: Pointer): Integer;
       begin
    @@ -143,6 +187,12 @@
           result[i] := PMarkupFoldColorInfo(l[i])^;
          l.Free;
       end;
    +
    +  operator = (z1, z2: TSynFoldNodeInfo): boolean;
    +  begin
    +      Result := false;
    +  end;
    +
       {%endregion}
     
     { TSynEditMarkupFoldColors }
    @@ -151,6 +201,9 @@
     begin
       inherited Create(ASynEdit);
     
    +  if Assigned(Lines) then
    +    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +
       FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
       FNestList.ResetFilter;
       FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    @@ -157,6 +210,8 @@
       FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
       FNestList.IncludeOpeningOnLine := True; //False; //
     
    +  SetLength(FFirstCharacterColumn, 0);
    +
       MarkupInfo.Foreground := clGreen;
       MarkupInfo.Background := clNone; //clFuchsia;
       MarkupInfo.Style := [];
    @@ -174,6 +229,8 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
       FreeAndNil(FNestList);
       inherited Destroy;
     end;
    @@ -182,12 +239,15 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i,x2both , found, color: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if (FCurrentRow = aRow) then begin
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
     
         x2both := -3; //flag
    +    //found := -1;
    +    //color := -1;
         for i := 0 to length(FHighlights)-1 do
           with FHighlights[i] do
             if not Ignore
    @@ -194,11 +254,21 @@
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    +        and (aStartCol.Logical < X2) then begin
    +          //if (found >= 0)
    +          //and (found = x)
    +          //and (
    +          //  (ToPos(SrcNode.LineIndex) <> aRow)
    +          //  or not (sfaOpen in SrcNode.FoldAction)
    +          //) then
    +          //  Continue;
    +          //
    +          //if x > found then
    +          //  found := -1;
    +
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
               //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,13 +279,16 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    -            MarkupInfo.FrameColor:= Colors[ColorIdx];
    +          //if found < 0 then
    +            color := Colors[ColorIdx];
    +          if Border then begin
    +            MarkupInfo.FrameColor:= color;
                 MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    -            MarkupInfo.Foreground := Colors[ColorIdx];
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
    +            MarkupInfo.Foreground := color;
    +          end;
     
               //MarkupInfo.FrameEdges:= sfeAround; //debug
     
    @@ -227,7 +300,7 @@
                 MarkupInfo.FrameColor:= clBlue; //debug
               end;}
     
    -          //break;
    +          //found := x;
             end;
       end;
     end;
    @@ -237,9 +310,10 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    +  if (FCurrentRow = aRow)  then
       for i := 0 to length(FHighlights)-1  do
         with FHighlights[i] do
         begin
    @@ -258,16 +332,42 @@
       i,lvl,z : integer; //iterate parents fold
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    -  var x,j : integer;
    +  var
    +    j, p, s: integer;
    +    l: String;
    +
       begin
    +    // get column of first character in row
    +      // Fallback if Highlighter doesn't populate ColumnOfFirstCharInRow
    +    s := Length(FFirstCharacterColumn);
    +    if (s <= ANode.LineIndex)
    +    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
    +      l := SynEdit.Lines[ANode.LineIndex];
    +      p := 1;
    +      while not (l[p] in [#13, #10, #0])
    +      and (l[p] = #32) do inc(p);
    +      if p > 255 then p := 255;
    +      if s > ANode.LineIndex then begin
    +        FFirstCharacterColumn[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
         z := Length(FHighlights);
         SetLength(FHighlights, z+1);
         with FHighlights[z] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      //X  := ANode.LogXStart + 1;
    +      //X  := p;
    +      if s <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumn[ANode.LineIndex];
           Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      X2 := X + 1; //ANode.LogXEnd + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -274,7 +374,9 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
    +      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
         end;
       end;
     
    @@ -292,7 +394,7 @@
       end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
     
    @@ -318,14 +420,33 @@
           lvlB := lvl;
     
           if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +        inc(lvl)
    +      else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
             dec(lvl);
    +      if (FLastNode.LineIndex >= 0)
    +      and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +      and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +       inc(lvl);
     
           AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +      if (z > 0)
    +      and (FHighlights[z].X = FHighlights[z - 1].X) then begin
    +        // if child is on same x-pos keep level
    +        if sfaOutlineMergeLevelOnWrongCol in FHighlights[z].SrcNode.FoldAction then begin
    +          lvl := FHighlights[z - 1].Level;
    +          FHighlights[z].Level := lvl;
    +          FHighlights[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        end;
    +      end;
    +
    +      if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +      and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction) then
             inc(lvl);
     
    +      if sfaOpen in TmpNode.FoldAction then
    +        FLastNode := TmpNode;
    +
           lvlA := lvl;
     
           with FHighlights[z] do begin
    @@ -370,9 +491,9 @@
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -382,10 +503,10 @@
       y,i,j,lvlB,lvlA : integer;
       HL: TSynCustomFoldHighlighter;
       NodeList: TLazSynFoldNodeInfoList;
    -  TmpNode: TSynFoldNodeInfo;
    +  TmpNode, PrevNode: TSynFoldNodeInfo;
       Found : boolean;
     begin
    -  y := aRow -1;
    +  y := ToIdx(aRow);
     
       HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
       HL.CurrentLines := Lines;
    @@ -401,6 +522,7 @@
         if J >=0 then
           lvl := max(0,FHighlights[J].LevelAfter);
         i := 0;
    +    PrevNode.LineIndex := -2;
         repeat
           TmpNode := NodeList[i];
     
    @@ -416,15 +538,37 @@
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          if (FLastNode.LineIndex >= 0)
    +          and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +           inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          if (z > 0)
    +          and (FHighlights[z].X = FHighlights[z - 1].X) then
    +          begin
    +            // if child is on same x-pos keep level
    +            if (sfaClose in FHighlights[z].SrcNode.FoldAction)
    +            or (sfaOutlineMergeLevelOnWrongCol in FHighlights[z].SrcNode.FoldAction) then begin
    +              lvl := FHighlights[z - 1].Level;
    +              FHighlights[z].Level := lvl;
    +              FHighlights[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +            end;
    +          end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction) then
                 inc(lvl);
    +
               lvlA := lvl;
     
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
               with FHighlights[z] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
    @@ -437,28 +581,50 @@
               Found := False;
               for j := Length(FHighlights)-1 downto 0 do begin
                 with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FHighlights[j].Level;
    +                lvlA := FHighlights[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
    +            //FLastNode.LineIndex := -1;
    +            //for j := j - 1 downto 0 do begin
    +            //  with FHighlights[j].SrcNode do begin
    +            //    if (sfaOpen in FoldAction) then begin
    +            //      FLastNode := FHighlights[j].SrcNode;
    +            //      Break;
    +            //    end;
    +            //  end;
    +            //end;
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            //lvl := lvlA;
    +            with FHighlights[z] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FHighlights[j].X > FHighlights[z].X then begin
    +              for j := j to z - 1 do begin
    +                FHighlights[j] := FHighlights[j+1];
    +              end;
    +              SetLength(FHighlights, z);
    +            end;
               end;
     
               //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
                 //inc(lvl);
             end;
    +
    +        PrevNode := TmpNode;
           end;
     
           inc(i);
    @@ -470,23 +636,49 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, li, LastX, j: Integer;
    +  n: TMarkupFoldColorInfos;
    +  l: TList;
     begin
    -  CurrentY := aRow;
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  FCurrentRow := aRow;
       SetLength(FHighlights,0); //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    +  FLastNode.LineIndex := -1;
    +
       //DoMarkupFoldAtRow(aRow);
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
       //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  //FHighlights := SortLeftMostFI(FHighlights);
    +
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := length(FHighlights) - 1 downto 0 do begin
    +    if FHighlights[i].X > LastX then begin
    +      for j := i to length(FHighlights) - 2 do begin
    +        FHighlights[j] := FHighlights[j + 1];
    +      end;
    +      SetLength(FHighlights, length(FHighlights) - 1);
    +    end;
    +    LastX := FHighlights[i].X;
    +  end;
    +
    +  //DebugLn('Zeile %d', [aRow]);
    +  //for i := 0 to length(FHighlights) - 1 do with FHighlights[i] do begin
    +  //  DebugLn('   %.2d: x=%.03d l=%.5d %s %s %s %s lvl=%.2d - %.2d - %.2d', [i, X, ToPos(SrcNode.LineIndex), IfThen(sfaOpen in SrcNode.FoldAction, 'O', IfThen(sfaClose in SrcNode.FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in SrcNode.FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in SrcNode.FoldAction, '+', IfThen(sfaOutlineMergeParent in SrcNode.FoldAction, '-', ' ')) ,FoldTypeToStr(SrcNode.FoldType), LevelBefore, Level, LevelAfter]);
    +  //end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  //DebugLn('EndMarkup');
       inherited EndMarkup;
       FNestList.Clear; // for next markup start
     end;
    @@ -503,164 +695,188 @@
       FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
     end;
     
    -{.$define debug_FC_line_changed}
    -procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
    -  ACountDiff: Integer);
    -{$ifdef debug_FC_line_changed}
    -var F : TCustomForm;
    +procedure TSynEditMarkupFoldColors.SetHighlighter(AValue: TSynCustomHighlighter
    +  );
     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}
    +  if FHighlighter = AValue then Exit;
    +  FHighlighter := AValue;
     
    +  if not (FHighlighter is TSynCustomFoldHighlighter) then
    +    exit;
     
    +  FreeAndNil(FNestList);
     
    -  function GetPairCloseFold(aRow, X : integer  ): Integer;
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, TSynCustomFoldHighlighter(FHighlighter));
    +  FNestList.ResetFilter;
    +  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +  FNestList.IncludeOpeningOnLine := True; //False; //
    +end;
    +
    +procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
    +  ACountDiff: Integer);
    +
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +      lLineIdx := ToIdx(pLine);
    +      pNestList.Line := lLineIdx;
    +      lCount := pNestList.Count;
    +      SetLength(pList, lCount);
    +      lAnz := 0;
    +      for i := 0 to lCount - 1 do begin
    +        lNode := pNestList.HLNode[i];
    +        if (sfaInvalid in lNode.FoldAction)
    +        or (
    +          (sfaOpen in lNode.FoldAction)
    +          and (lNode.LineIndex = lLineIdx)
    +        ) then
    +          Continue;
     
    -    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);
    +        pList[i] := lNode;
    +        inc(lAnz);
           end;
    +      SetLength(pList, lAnz);
    +  end;
     
    -    begin
    -      Result := SearchLine(YIndex, NIndex);
    -      if not (sfaInvalid in Result.FoldAction) then
    -        exit;
    +var
    +  i, lMinAnz, lEndLine, j: integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +  HL: TSynCustomFoldHighlighter;
    +  lNodeList: TLazSynFoldNodeInfoList;
    +  lNode: TSynFoldNodeInfo;
    +  found: Boolean;
     
    -      inc(YIndex);
    -      while (YIndex < LCnt) and
    -            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
    -             > StartNode.NestLvlStart)
    -      do
    -        inc(YIndex);
    -      if YIndex = LCnt then
    -        exit;
    +begin
    +  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  if EndLine < 0 then
    +    EndLine := StartLine;
     
    -      NIndex := -1;
    -      Result := SearchLine(YIndex, NIndex);
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do
    +    FFirstCharacterColumn[i] := 0;
     
    -      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
    -        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
    +  lEndLine := EndLine;
    +
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  DebugLn('   Nodes at Start:');
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
    +  FillNestList(lEndNestList, EndLine, FNestList);
    +  DebugLn('   Nodes at End:');
    +  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
    +
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
    +  end;
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    -
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    DebugLn('   Remaining Nodes:');
    +    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
         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
    +    found := false;
    +    repeat
    +      inc(lEndLine);
    +      if lEndLine > Lines.Count then break;
    +      HL.FoldNodeInfo[ToIdx(lEndLine)].ClearFilter;
    +      lNodeList := HL.FoldNodeInfo[ToIdx(lEndLine)];
    +      lNodeList.AddReference;
    +      try
    +        lNodeList.ActionFilter := [sfaOutline, sfaClose];
    +        i := 0;
    +        repeat
    +          lNode := lNodeList[i];
    +          if not (sfaInvalid in lNode.FoldAction)
    +          and (lNode.FoldType = lEndNestList[0].FoldType)
    +          and (lNode.FoldGroup = lEndNestList[0].FoldGroup)
    +          and (lNode.NestLvlEnd = lEndNestList[0].NestLvlStart) then begin
    +            found := true;
    +            break;
    +          end;
               inc(i);
    -          continue;
    -        end;
    +        until (i >= lNodeList.Count) or found;
    +      finally
    +        lNodeList.ReleaseReference;
    +      end;
    +    until found;
     
    -        //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;
    +  if lEndLine > EndLine then begin
    +    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  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;
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
     var
    -  EndFoldLine,y : integer;
    +  old: TSynEditStrings;
     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);
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // Änderung:
    +    // Remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
       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}
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // Änderung:
    +    if Assigned(AValue) then begin
    +      // Add Changehandler
    +      SetLength(FFirstCharacterColumn, AValue.Count);
    +      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    +    end else begin
    +      SetLength(FFirstCharacterColumn, 0);
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
    +var
    +  absCount,
    +  idx, i: Integer;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    -
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    +  //DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  idx := ToIdx(aIndex);
    +  if aCount < 0 then begin
    +    // Zeilen entfernt
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
    +      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
    +    end;
    +  end;
    +  SetLength(FFirstCharacterColumn, Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // Zeilen eingefügt
    +      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do FFirstCharacterColumn[i] := 0;
    +    end else begin
    +      // erste Zeilen werden eingefügt
    +      for i := 0 to Length(FFirstCharacterColumn) - 1 do FFirstCharacterColumn[i] := 0;
    +    end;
    +  end;
     end;
     
    -
    -
     end.
     
    
  • syneditmarkupfoldcoloring.pas_v3.patch (29,749 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 52753)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -55,7 +55,8 @@
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText, fgl;
     
     type
     
    @@ -66,37 +67,38 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    +    FHighlighter: TSynCustomHighlighter;
         FNestList: TLazSynEditNestedFoldsList;
    +    FFirstCharacterColumn: Array of Byte;
         FDefaultGroup: integer;
          // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FHighlights: TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
         Colors : array of TColor;
     
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    +    FCurrentRow: integer;  // prepared Row
     
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
     
         function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    procedure SetHighlighter(AValue: TSynCustomHighlighter);
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -111,12 +113,54 @@
         procedure PrepareMarkupForRow(aRow : Integer); override;
         procedure EndMarkup; override;
         property DefaultGroup : integer read FDefaultGroup write SetDefaultGroup;
    +    property  Highlighter: TSynCustomHighlighter
    +      read FHighlighter write SetHighlighter;
       end;
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit,SynEditTypes, SynEditMiscProcs, Dialogs, strutils;
     
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  case PtrInt(p_FoldType) of
    +    00: Result := 'cfbtBeginEnd      ';
    +    01: Result := 'cfbtTopBeginEnd   ';
    +    02: Result := 'cfbtNestedComment ';
    +    03: Result := 'cfbtProcedure     ';
    +    04: Result := 'cfbtUses          ';
    +    05: Result := 'cfbtVarType       ';
    +    06: Result := 'cfbtLocalVarType  ';
    +    07: Result := 'cfbtClass         ';
    +    08: Result := 'cfbtClassSection  ';
    +    09: Result := 'cfbtUnitSection   ';
    +    10: Result := 'cfbtProgram       ';
    +    11: Result := 'cfbtUnit          ';
    +    12: Result := 'cfbtRecord        ';
    +    13: Result := 'cfbtTry           ';
    +    14: Result := 'cfbtExcept        ';
    +    15: Result := 'cfbtRepeat        ';
    +    16: Result := 'cfbtAsm           ';
    +    17: Result := 'cfbtCase          ';
    +    18: Result := 'cfbtIfDef         ';
    +    19: Result := 'cfbtRegion        ';
    +    20: Result := 'cfbtAnsiComment   ';
    +    21: Result := 'cfbtBorCommand    ';
    +    22: Result := 'cfbtSlashComment  ';
    +    23: Result := 'cfbtIfThen        ';
    +    24: Result := 'cfbtForDo         ';
    +    25: Result := 'cfbtWhileDo       ';
    +    26: Result := 'cfbtWithDo        ';
    +    27: Result := 'cfbtIfElse        ';
    +    28: Result := '// Internal type  ';
    +    29: Result := 'cfbtCaseElse (int)';
    +    30: Result := 'cfbtPackage (int) ';
    +    31: Result := 'cfbtNone (int)    ';
    +    else Result := Format('unknown %.2d        ', [PtrInt(p_FoldType)]);
    +  end;
    +end;
    +
    +
       {%region Sorting FoldInfo -fold}
       function CompareFI(Item1, Item2: Pointer): Integer;
       begin
    @@ -143,6 +187,12 @@
           result[i] := PMarkupFoldColorInfo(l[i])^;
          l.Free;
       end;
    +
    +  operator = (z1, z2: TSynFoldNodeInfo): boolean;
    +  begin
    +      Result := false;
    +  end;
    +
       {%endregion}
     
     { TSynEditMarkupFoldColors }
    @@ -151,6 +201,9 @@
     begin
       inherited Create(ASynEdit);
     
    +  if Assigned(Lines) then
    +    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +
       FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
       FNestList.ResetFilter;
       FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    @@ -157,6 +210,8 @@
       FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
       FNestList.IncludeOpeningOnLine := True; //False; //
     
    +  SetLength(FFirstCharacterColumn, 0);
    +
       MarkupInfo.Foreground := clGreen;
       MarkupInfo.Background := clNone; //clFuchsia;
       MarkupInfo.Style := [];
    @@ -174,6 +229,8 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
       FreeAndNil(FNestList);
       inherited Destroy;
     end;
    @@ -182,12 +239,15 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i,x2both , found, color: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if (FCurrentRow = aRow) then begin
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
     
         x2both := -3; //flag
    +    //found := -1;
    +    //color := -1;
         for i := 0 to length(FHighlights)-1 do
           with FHighlights[i] do
             if not Ignore
    @@ -194,11 +254,21 @@
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    +        and (aStartCol.Logical < X2) then begin
    +          //if (found >= 0)
    +          //and (found = x)
    +          //and (
    +          //  (ToPos(SrcNode.LineIndex) <> aRow)
    +          //  or not (sfaOpen in SrcNode.FoldAction)
    +          //) then
    +          //  Continue;
    +          //
    +          //if x > found then
    +          //  found := -1;
    +
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
               //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,13 +279,16 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    -            MarkupInfo.FrameColor:= Colors[ColorIdx];
    +          //if found < 0 then
    +            color := Colors[ColorIdx];
    +          if Border then begin
    +            MarkupInfo.FrameColor:= color;
                 MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    -            MarkupInfo.Foreground := Colors[ColorIdx];
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
    +            MarkupInfo.Foreground := color;
    +          end;
     
               //MarkupInfo.FrameEdges:= sfeAround; //debug
     
    @@ -227,7 +300,7 @@
                 MarkupInfo.FrameColor:= clBlue; //debug
               end;}
     
    -          //break;
    +          //found := x;
             end;
       end;
     end;
    @@ -237,9 +310,10 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    +  if (FCurrentRow = aRow)  then
       for i := 0 to length(FHighlights)-1  do
         with FHighlights[i] do
         begin
    @@ -258,16 +332,42 @@
       i,lvl,z : integer; //iterate parents fold
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    -  var x,j : integer;
    +  var
    +    j, p, s: integer;
    +    l: String;
    +
       begin
    +    // get column of first character in row
    +      // Fallback if Highlighter doesn't populate ColumnOfFirstCharInRow
    +    s := Length(FFirstCharacterColumn);
    +    if (s <= ANode.LineIndex)
    +    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
    +      l := SynEdit.Lines[ANode.LineIndex];
    +      p := 1;
    +      while not (l[p] in [#13, #10, #0])
    +      and (l[p] = #32) do inc(p);
    +      if p > 255 then p := 255;
    +      if s > ANode.LineIndex then begin
    +        FFirstCharacterColumn[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
         z := Length(FHighlights);
         SetLength(FHighlights, z+1);
         with FHighlights[z] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      //X  := ANode.LogXStart + 1;
    +      //X  := p;
    +      if s <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumn[ANode.LineIndex];
           Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      X2 := X + 1; //ANode.LogXEnd + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -274,7 +374,9 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
    +      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
         end;
       end;
     
    @@ -292,7 +394,7 @@
       end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
     
    @@ -318,14 +420,33 @@
           lvlB := lvl;
     
           if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +        inc(lvl)
    +      else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
             dec(lvl);
    +      if (FLastNode.LineIndex >= 0)
    +      and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +      and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +       inc(lvl);
     
           AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +      if (z > 0)
    +      and (FHighlights[z].X = FHighlights[z - 1].X) then begin
    +        // if child is on same x-pos keep level
    +        if sfaOutlineMergeLevelOnWrongCol in FHighlights[z].SrcNode.FoldAction then begin
    +          lvl := FHighlights[z - 1].Level;
    +          FHighlights[z].Level := lvl;
    +          FHighlights[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        end;
    +      end;
    +
    +      if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +      and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction) then
             inc(lvl);
     
    +      if sfaOpen in TmpNode.FoldAction then
    +        FLastNode := TmpNode;
    +
           lvlA := lvl;
     
           with FHighlights[z] do begin
    @@ -370,9 +491,9 @@
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -382,10 +503,10 @@
       y,i,j,lvlB,lvlA : integer;
       HL: TSynCustomFoldHighlighter;
       NodeList: TLazSynFoldNodeInfoList;
    -  TmpNode: TSynFoldNodeInfo;
    +  TmpNode, PrevNode: TSynFoldNodeInfo;
       Found : boolean;
     begin
    -  y := aRow -1;
    +  y := ToIdx(aRow);
     
       HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
       HL.CurrentLines := Lines;
    @@ -401,6 +522,7 @@
         if J >=0 then
           lvl := max(0,FHighlights[J].LevelAfter);
         i := 0;
    +    PrevNode.LineIndex := -2;
         repeat
           TmpNode := NodeList[i];
     
    @@ -416,15 +538,37 @@
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          if (FLastNode.LineIndex >= 0)
    +          and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +           inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          if (z > 0)
    +          and (FHighlights[z].X = FHighlights[z - 1].X) then
    +          begin
    +            // if child is on same x-pos keep level
    +            if (sfaClose in FHighlights[z].SrcNode.FoldAction)
    +            or (sfaOutlineMergeLevelOnWrongCol in FHighlights[z].SrcNode.FoldAction) then begin
    +              lvl := FHighlights[z - 1].Level;
    +              FHighlights[z].Level := lvl;
    +              FHighlights[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +            end;
    +          end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction) then
                 inc(lvl);
    +
               lvlA := lvl;
     
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
               with FHighlights[z] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
    @@ -437,28 +581,50 @@
               Found := False;
               for j := Length(FHighlights)-1 downto 0 do begin
                 with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FHighlights[j].Level;
    +                lvlA := FHighlights[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
    +            //FLastNode.LineIndex := -1;
    +            //for j := j - 1 downto 0 do begin
    +            //  with FHighlights[j].SrcNode do begin
    +            //    if (sfaOpen in FoldAction) then begin
    +            //      FLastNode := FHighlights[j].SrcNode;
    +            //      Break;
    +            //    end;
    +            //  end;
    +            //end;
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            //lvl := lvlA;
    +            with FHighlights[z] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FHighlights[j].X > FHighlights[z].X then begin
    +              for j := j to z - 1 do begin
    +                FHighlights[j] := FHighlights[j+1];
    +              end;
    +              SetLength(FHighlights, z);
    +            end;
               end;
     
               //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
                 //inc(lvl);
             end;
    +
    +        PrevNode := TmpNode;
           end;
     
           inc(i);
    @@ -470,23 +636,49 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, li, LastX, j: Integer;
    +  n: TMarkupFoldColorInfos;
    +  l: TList;
     begin
    -  CurrentY := aRow;
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  FCurrentRow := aRow;
       SetLength(FHighlights,0); //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    +  FLastNode.LineIndex := -1;
    +
       //DoMarkupFoldAtRow(aRow);
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
       //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  //FHighlights := SortLeftMostFI(FHighlights);
    +
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := length(FHighlights) - 1 downto 0 do begin
    +    if FHighlights[i].X > LastX then begin
    +      for j := i to length(FHighlights) - 2 do begin
    +        FHighlights[j] := FHighlights[j + 1];
    +      end;
    +      SetLength(FHighlights, length(FHighlights) - 1);
    +    end;
    +    LastX := FHighlights[i].X;
    +  end;
    +
    +  //DebugLn('Zeile %d', [aRow]);
    +  //for i := 0 to length(FHighlights) - 1 do with FHighlights[i] do begin
    +  //  DebugLn('   %.2d: x=%.03d l=%.5d %s %s %s %s lvl=%.2d - %.2d - %.2d', [i, X, ToPos(SrcNode.LineIndex), IfThen(sfaOpen in SrcNode.FoldAction, 'O', IfThen(sfaClose in SrcNode.FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in SrcNode.FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in SrcNode.FoldAction, '+', IfThen(sfaOutlineMergeParent in SrcNode.FoldAction, '-', ' ')) ,FoldTypeToStr(SrcNode.FoldType), LevelBefore, Level, LevelAfter]);
    +  //end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  //DebugLn('EndMarkup');
       inherited EndMarkup;
       FNestList.Clear; // for next markup start
     end;
    @@ -503,164 +695,188 @@
       FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
     end;
     
    -{.$define debug_FC_line_changed}
    -procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
    -  ACountDiff: Integer);
    -{$ifdef debug_FC_line_changed}
    -var F : TCustomForm;
    +procedure TSynEditMarkupFoldColors.SetHighlighter(AValue: TSynCustomHighlighter
    +  );
     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}
    +  if FHighlighter = AValue then Exit;
    +  FHighlighter := AValue;
     
    +  if not (FHighlighter is TSynCustomFoldHighlighter) then
    +    exit;
     
    +  FreeAndNil(FNestList);
     
    -  function GetPairCloseFold(aRow, X : integer  ): Integer;
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, TSynCustomFoldHighlighter(FHighlighter));
    +  FNestList.ResetFilter;
    +  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +  FNestList.IncludeOpeningOnLine := True; //False; //
    +end;
    +
    +procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
    +  ACountDiff: Integer);
    +
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +      lLineIdx := ToIdx(pLine);
    +      pNestList.Line := lLineIdx;
    +      lCount := pNestList.Count;
    +      SetLength(pList, lCount);
    +      lAnz := 0;
    +      for i := 0 to lCount - 1 do begin
    +        lNode := pNestList.HLNode[i];
    +        if (sfaInvalid in lNode.FoldAction)
    +        or (
    +          (sfaOpen in lNode.FoldAction)
    +          and (lNode.LineIndex = lLineIdx)
    +        ) then
    +          Continue;
     
    -    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);
    +        pList[i] := lNode;
    +        inc(lAnz);
           end;
    +      SetLength(pList, lAnz);
    +  end;
     
    -    begin
    -      Result := SearchLine(YIndex, NIndex);
    -      if not (sfaInvalid in Result.FoldAction) then
    -        exit;
    +var
    +  i, lMinAnz, lEndLine, j: integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +  HL: TSynCustomFoldHighlighter;
    +  lNodeList: TLazSynFoldNodeInfoList;
    +  lNode: TSynFoldNodeInfo;
    +  found: Boolean;
     
    -      inc(YIndex);
    -      while (YIndex < LCnt) and
    -            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
    -             > StartNode.NestLvlStart)
    -      do
    -        inc(YIndex);
    -      if YIndex = LCnt then
    -        exit;
    +begin
    +  //DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  if EndLine < 0 then
    +    EndLine := StartLine;
     
    -      NIndex := -1;
    -      Result := SearchLine(YIndex, NIndex);
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do
    +    FFirstCharacterColumn[i] := 0;
     
    -      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
    -        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
    +  lEndLine := EndLine;
    +
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  //DebugLn('   Nodes at Start:');
    +  //for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +  //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
    +  FillNestList(lEndNestList, EndLine, FNestList);
    +  //DebugLn('   Nodes at End:');
    +  //for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +  //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
    +
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
    +  end;
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    -
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    //DebugLn('   Remaining Nodes:');
    +    //for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
         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;
    +    found := false;
    +    repeat
    +        inc(lEndLine);
    +        if lEndLine > Lines.Count then break;
    +        HL.FoldNodeInfo[ToIdx(lEndLine)].ClearFilter;
    +        lNodeList := HL.FoldNodeInfo[ToIdx(lEndLine)];
    +        lNodeList.AddReference;
    +        try
    +          lNodeList.ActionFilter := [sfaOutline, sfaClose];
    +          i := 0;
    +          repeat
    +            lNode := lNodeList[i];
    +            if not (sfaInvalid in lNode.FoldAction)
    +            and (lNode.FoldType = lEndNestList[0].FoldType)
    +            and (lNode.FoldGroup = lEndNestList[0].FoldGroup)
    +            and (lNode.NestLvlEnd = lEndNestList[0].NestLvlStart) then begin
    +              found := true;
    +              break;
    +            end;
    +            inc(i);
    +          until (i >= lNodeList.Count) or found;
    +        finally
    +          lNodeList.ReleaseReference;
             end;
    +    until found;
     
    -        //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;
    +  if lEndLine > EndLine then begin
    +    //DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  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;
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
     var
    -  EndFoldLine,y : integer;
    +  old: TSynEditStrings;
     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);
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // Änderung:
    +    // Remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
       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}
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // Änderung:
    +    if Assigned(AValue) then begin
    +      // Add Changehandler
    +      SetLength(FFirstCharacterColumn, AValue.Count);
    +      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    +    end else begin
    +      SetLength(FFirstCharacterColumn, 0);
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
    +var
    +  absCount,
    +  idx, i: Integer;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    -
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    +  //DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  idx := ToIdx(aIndex);
    +  if aCount < 0 then begin
    +    // Zeilen entfernt
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
    +      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
    +    end;
    +  end;
    +  SetLength(FFirstCharacterColumn, Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // Zeilen eingefügt
    +      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do FFirstCharacterColumn[i] := 0;
    +    end else begin
    +      // erste Zeilen werden eingefügt
    +      for i := 0 to Length(FFirstCharacterColumn) - 1 do FFirstCharacterColumn[i] := 0;
    +    end;
    +  end;
     end;
     
    -
    -
     end.
     
    
  • ide_opts_syn_outline_DO_NOT_SVN_commit.diff (15,196 bytes)
    Index: ide/editoroptions.pp
    ===================================================================
    --- ide/editoroptions.pp	(revision 52759)
    +++ ide/editoroptions.pp	(working copy)
    @@ -1396,6 +1396,8 @@
     
         // Code Folding
         FUseCodeFolding: Boolean;
    +    FUseMarkupWordBracket: Boolean;
    +    FUseMarkupOutline: Boolean;
         FReverseFoldPopUpOrder: Boolean;
     
         // Multi window
    @@ -1605,6 +1607,10 @@
         // Code Folding
         property UseCodeFolding: Boolean
             read FUseCodeFolding write FUseCodeFolding default True;
    +    property UseMarkupWordBracket: Boolean
    +        read FUseMarkupWordBracket write FUseMarkupWordBracket default True;
    +    property UseMarkupOutline: Boolean
    +        read FUseMarkupOutline write FUseMarkupOutline default True;
     
         // Multi window
         property MultiWinEditAccessOrder: TEditorOptionsEditAccessOrderList
    @@ -4866,6 +4872,12 @@
         FUseCodeFolding :=
           XMLConfig.GetValue(
           'EditorOptions/CodeFolding/UseCodeFolding', True);
    +    FUseMarkupWordBracket :=
    +      XMLConfig.GetValue(
    +      'EditorOptions/CodeFolding/UseMarkupWordBracket', True);
    +    FUseMarkupOutline :=
    +      XMLConfig.GetValue(
    +      'EditorOptions/CodeFolding/UseMarkupOutline', True);
     
         FUserMouseSettings.LoadFromXml(XMLConfig, 'EditorOptions/Mouse/',
                                       'EditorOptions/General/Editor/', FileVersion);
    @@ -5052,6 +5064,10 @@
         // Code Folding
         XMLConfig.SetDeleteValue('EditorOptions/CodeFolding/UseCodeFolding',
             FUseCodeFolding, True);
    +    XMLConfig.SetDeleteValue('EditorOptions/CodeFolding/UseMarkupWordBracket',
    +        FUseMarkupWordBracket, True);
    +    XMLConfig.SetDeleteValue('EditorOptions/CodeFolding/UseMarkupOutline',
    +        FUseMarkupOutline, True);
     
         FUserMouseSettings.SaveToXml(XMLConfig, 'EditorOptions/Mouse/');
     
    @@ -5369,8 +5385,13 @@
             (* if ReadForOptions=True then Enabled appies only to fmFold,fmHide.
                This allows to store what selection was previously active *)
             if not ReadForOptions then begin
    -          if not FoldHl.FoldConfig[idx].Enabled then
    +          if (not FoldHl.FoldConfig[idx].Enabled) or (not FUseCodeFolding) then
                 FoldHl.FoldConfig[idx].Modes := FoldHl.FoldConfig[idx].Modes - [fmFold, fmHide];
    +          if (not FUseMarkupWordBracket) then
    +            FoldHl.FoldConfig[idx].Modes := FoldHl.FoldConfig[idx].Modes - [fmMarkup];
    +          if (not FUseMarkupOutline) then
    +            FoldHl.FoldConfig[idx].Modes := FoldHl.FoldConfig[idx].Modes - [fmOutline];
    +
               FoldHl.FoldConfig[idx].Enabled := FoldHl.FoldConfig[idx].Modes <> [];
             end;
           end;
    Index: ide/frames/editor_markup_options.lfm
    ===================================================================
    --- ide/frames/editor_markup_options.lfm	(revision 52759)
    +++ ide/frames/editor_markup_options.lfm	(working copy)
    @@ -317,13 +317,14 @@
         AnchorSideBottom.Control = Owner
         AnchorSideBottom.Side = asrBottom
         Left = 6
    -    Height = 156
    -    Top = 330
    +    Height = 150
    +    Top = 380
         Width = 220
         Anchors = [akTop, akLeft, akBottom]
         BorderSpacing.Around = 6
         Constraints.MinHeight = 150
         ItemHeight = 0
    +    OnClick = chkKWGroupsClick
         OnClickCheck = chkKWGroupsClickCheck
         OnExit = chkKWGroupsClickCheck
         OnKeyUp = chkKWGroupsKeyUp
    @@ -331,11 +332,11 @@
       end
       object LanguageComboBox: TComboBox
         AnchorSideLeft.Control = Owner
    -    AnchorSideTop.Control = divKeyWordGroups
    +    AnchorSideTop.Control = cbMarkupOutline
         AnchorSideTop.Side = asrBottom
         Left = 6
         Height = 23
    -    Top = 301
    +    Top = 351
         Width = 200
         BorderSpacing.Left = 6
         BorderSpacing.Top = 6
    @@ -353,10 +354,64 @@
         AnchorSideTop.Side = asrCenter
         Left = 212
         Height = 15
    -    Top = 305
    +    Top = 355
         Width = 80
         BorderSpacing.Left = 6
         Caption = 'LanguageLabel'
         ParentColor = False
       end
    +  object cbMarkup: TCheckBox
    +    AnchorSideLeft.Control = chkKWGroups
    +    AnchorSideLeft.Side = asrBottom
    +    AnchorSideTop.Control = chkKWGroups
    +    Left = 232
    +    Height = 19
    +    Top = 380
    +    Width = 74
    +    BorderSpacing.Left = 6
    +    Caption = 'cbMarkup'
    +    OnChange = cbMarkupChange
    +    TabOrder = 10
    +  end
    +  object cbOutline: TCheckBox
    +    AnchorSideLeft.Control = chkKWGroups
    +    AnchorSideLeft.Side = asrBottom
    +    AnchorSideTop.Control = cbMarkup
    +    AnchorSideTop.Side = asrBottom
    +    Left = 232
    +    Height = 19
    +    Top = 405
    +    Width = 72
    +    BorderSpacing.Left = 6
    +    BorderSpacing.Top = 6
    +    Caption = 'cbOutline'
    +    OnChange = cbMarkupChange
    +    TabOrder = 11
    +  end
    +  object cbMarkupWordBracket: TCheckBox
    +    AnchorSideLeft.Control = Owner
    +    AnchorSideTop.Control = divKeyWordGroups
    +    AnchorSideTop.Side = asrBottom
    +    Left = 6
    +    Height = 19
    +    Top = 301
    +    Width = 142
    +    BorderSpacing.Around = 6
    +    Caption = 'cbMarkupWordBracket'
    +    OnChange = cbMarkupWordBracketChange
    +    TabOrder = 12
    +  end
    +  object cbMarkupOutline: TCheckBox
    +    AnchorSideLeft.Control = Owner
    +    AnchorSideTop.Control = cbMarkupWordBracket
    +    AnchorSideTop.Side = asrBottom
    +    Left = 6
    +    Height = 19
    +    Top = 326
    +    Width = 113
    +    BorderSpacing.Around = 6
    +    Caption = 'cbMarkupOutline'
    +    OnChange = cbMarkupOutlineChange
    +    TabOrder = 13
    +  end
     end
    Index: ide/frames/editor_markup_options.pas
    ===================================================================
    --- ide/frames/editor_markup_options.pas	(revision 52759)
    +++ ide/frames/editor_markup_options.pas	(working copy)
    @@ -38,6 +38,10 @@
         BracketCombo: TComboBox;
         BracketLabel: TLabel;
         BracketLink: TLabel;
    +    cbMarkup: TCheckBox;
    +    cbOutline: TCheckBox;
    +    cbMarkupWordBracket: TCheckBox;
    +    cbMarkupOutline: TCheckBox;
         chkKWGroups: TCheckListBox;
         chkExtPasKeywords: TCheckBox;
         divKeyWordGroups: TDividerBevel;
    @@ -64,7 +68,11 @@
         procedure BracketLinkClick(Sender: TObject);
         procedure BracketLinkMouseEnter(Sender: TObject);
         procedure BracketLinkMouseLeave(Sender: TObject);
    +    procedure cbMarkupChange(Sender: TObject);
    +    procedure cbMarkupOutlineChange(Sender: TObject);
    +    procedure cbMarkupWordBracketChange(Sender: TObject);
         procedure chkExtPasKeywordsChange(Sender: TObject);
    +    procedure chkKWGroupsClick(Sender: TObject);
         procedure chkKWGroupsClickCheck(Sender: TObject);
         procedure chkKWGroupsKeyUp(Sender: TObject; var {%H-}Key: Word; {%H-}Shift: TShiftState);
         procedure dropPasStringKeywordsChange(Sender: TObject);
    @@ -79,10 +87,14 @@
       private
         { private declarations }
         FDialog: TAbstractOptionsEditorDialog;
    +    FModeLock: Boolean;
         FCurHighlighter: TSrcIDEHighlighter;
         FCurFoldInfo: TEditorOptionsFoldRecord;
    +    FUseMarkupWordBracket: Boolean;
    +    FUseMarkupOutline: Boolean;
         function GetHighlighter(SynType: TLazSyntaxHighlighter;
           CreateIfNotExists: Boolean): TSrcIDEHighlighter;
    +    procedure UpdateMarkupCheckBoxes;
     
       public
         function GetTitle: String; override;
    @@ -157,6 +169,46 @@
       (Sender as TLabel).Font.Color := clBlue;
     end;
     
    +procedure TEditorMarkupOptionsFrame.cbMarkupChange(Sender: TObject);
    +var
    +  Hl: TSynCustomFoldHighlighter;
    +  Modes: TSynCustomFoldConfigModes;
    +  i: LongInt;
    +begin
    +  if FModeLock then exit;
    +  if not (assigned(FCurHighlighter) and
    +         (FCurHighlighter is TSynCustomFoldHighlighter)) then exit;
    +
    +  i := chkKWGroups.ItemIndex;
    +  if i < 0 then exit;
    +  i := PtrUInt(chkKWGroups.Items.Objects[i]);
    +  i := FCurFoldInfo.Info^[i].Index;
    +  Hl := TSynCustomFoldHighlighter(FCurHighlighter);
    +
    +  Modes := [fmMarkup];
    +  if Sender = cbOutline then
    +    Modes := [fmOutline];
    +
    +  if TCheckBox(Sender).Checked then
    +    Hl.FoldConfig[i].Modes := Hl.FoldConfig[i].Modes + Modes
    +  else
    +    Hl.FoldConfig[i].Modes := Hl.FoldConfig[i].Modes - Modes;
    +
    +  chkKWGroups.Checked[chkKWGroups.ItemIndex] := cbMarkup.Checked or cbOutline.Checked;
    +end;
    +
    +procedure TEditorMarkupOptionsFrame.cbMarkupOutlineChange(Sender: TObject);
    +begin
    +  FUseMarkupOutline := cbMarkupOutline.Checked;
    +  LanguageComboBoxExit(nil);
    +end;
    +
    +procedure TEditorMarkupOptionsFrame.cbMarkupWordBracketChange(Sender: TObject);
    +begin
    +  FUseMarkupWordBracket := cbMarkupWordBracket.Checked;
    +  LanguageComboBoxExit(nil);
    +end;
    +
     procedure TEditorMarkupOptionsFrame.chkExtPasKeywordsChange(Sender: TObject);
     begin
       GeneralPage.PasExtendedKeywordsMode := chkExtPasKeywords.Checked;
    @@ -163,26 +215,39 @@
       GeneralPage.UpdatePrevieEdits;
     end;
     
    +procedure TEditorMarkupOptionsFrame.chkKWGroupsClick(Sender: TObject);
    +begin
    +  UpdateMarkupCheckBoxes;
    +end;
    +
     procedure TEditorMarkupOptionsFrame.chkKWGroupsClickCheck(Sender: TObject);
     var
    -  i, j, idx: Integer;
    +  i, j, idx, i1: Integer;
       Hl: TSynCustomFoldHighlighter;
    +  FMask: TSynCustomFoldConfigModes;
     begin
       if not (assigned(FCurHighlighter) and
              (FCurHighlighter is TSynCustomFoldHighlighter)) then exit;
       Hl := TSynCustomFoldHighlighter(FCurHighlighter);
    +  FMask := [fmMarkup, fmOutline];
    +  if not FUseMarkupWordBracket then FMask := FMask - [fmMarkup];
    +  if not FUseMarkupOutline then FMask := FMask - [fmOutline];
     
       j := 0;
    -  for i := 0 to FCurFoldInfo.Count - 1 do begin
    +  for i1 := 0 to chkKWGroups.Count - 1 do begin
    +    i := PtrUInt(chkKWGroups.Items.Objects[i1]);
         idx := FCurFoldInfo.Info^[i].Index;
    -    if fmMarkup in Hl.FoldConfig[idx].SupportedModes then begin
    -      if chkKWGroups.Checked[j] then
    -        Hl.FoldConfig[idx].Modes := Hl.FoldConfig[idx].Modes + [fmMarkup]
    +    if Hl.FoldConfig[idx].SupportedModes * [fmMarkup, fmOutline] <> [] then begin
    +      if not chkKWGroups.Checked[j] then
    +        Hl.FoldConfig[idx].Modes := Hl.FoldConfig[idx].Modes - FMask
           else
    -        Hl.FoldConfig[idx].Modes := Hl.FoldConfig[idx].Modes - [fmMarkup];
    +      if Hl.FoldConfig[idx].Modes * FMask = [] then
    +        Hl.FoldConfig[idx].Modes := Hl.FoldConfig[idx].Modes + FMask;
           inc(j);
         end;
       end;
    +
    +  UpdateMarkupCheckBoxes;
     end;
     
     procedure TEditorMarkupOptionsFrame.chkKWGroupsKeyUp(Sender: TObject; var Key: Word;
    @@ -217,13 +282,13 @@
     
     procedure TEditorMarkupOptionsFrame.LanguageComboBoxExit(Sender: TObject);
     var
    -  ComboBox: TComboBox absolute Sender;
       tp: TLazSyntaxHighlighter;
       i, j: Integer;
       Hl: TSynCustomFoldHighlighter;
    +  FMask: TSynCustomFoldConfigModes;
     begin
       tp := EditorOpts.HighlighterList
    -          [EditorOpts.HighlighterList.FindByName(ComboBox.Text)].TheType;
    +          [EditorOpts.HighlighterList.FindByName(LanguageComboBox.Text)].TheType;
       FCurHighlighter := GetHighlighter(tp, True);
       FCurFoldInfo := EditorOptionsFoldDefaults[tp];
     
    @@ -232,14 +297,18 @@
              (FCurHighlighter is TSynCustomFoldHighlighter)) then exit;
       Hl := TSynCustomFoldHighlighter(FCurHighlighter);
     
    +  FMask := [fmMarkup, fmOutline];
    +  if not FUseMarkupWordBracket then FMask := FMask - [fmMarkup];
    +  if not FUseMarkupOutline then FMask := FMask - [fmOutline];
       for i := 0 to FCurFoldInfo.Count - 1 do begin
    -    if Hl.FoldConfig[FCurFoldInfo.Info^[i].Index].SupportedModes * [fmMarkup{, fmOutline}] <> [] then begin
    +    if Hl.FoldConfig[FCurFoldInfo.Info^[i].Index].SupportedModes * FMask <> [] then begin
           j := chkKWGroups.Items.Add(FCurFoldInfo.Info^[i].Name);
           chkKWGroups.Checked[j] :=
    -        (Hl.FoldConfig[FCurFoldInfo.Info^[i].Index].Modes * [fmMarkup{, fmOutline}] <> []);
    +        (Hl.FoldConfig[FCurFoldInfo.Info^[i].Index].Modes * FMask <> []);
           chkKWGroups.Items.Objects[j] := TObject({%H-}Pointer(PtrUInt(i)));
         end;
       end;
    +  UpdateMarkupCheckBoxes;
     end;
     
     procedure TEditorMarkupOptionsFrame.LanguageComboBoxKeyDown(Sender: TObject;
    @@ -276,6 +345,40 @@
       Result := FoldPage.GetHighlighter(SynType, CreateIfNotExists);
     end;
     
    +procedure TEditorMarkupOptionsFrame.UpdateMarkupCheckBoxes;
    +var
    +  i: LongInt;
    +  FMask: TSynCustomFoldConfigModes;
    +  Hl: TSynCustomFoldHighlighter;
    +begin
    +  if not (assigned(FCurHighlighter) and
    +         (FCurHighlighter is TSynCustomFoldHighlighter)) then exit;
    +  Hl := TSynCustomFoldHighlighter(FCurHighlighter);
    +  FModeLock := True;
    +  i := chkKWGroups.ItemIndex;
    +  if i >= 0 then
    +    i := PtrUInt(chkKWGroups.Items.Objects[i]);
    +
    +  if i >= 0 then begin
    +    i := FCurFoldInfo.Info^[i].Index;
    +    FMask := [fmMarkup, fmOutline];
    +    if not FUseMarkupWordBracket then FMask := FMask - [fmMarkup];
    +    if not FUseMarkupOutline then FMask := FMask - [fmOutline];
    +
    +    cbMarkup.Enabled := fmMarkup in Hl.FoldConfig[i].SupportedModes * FMask;
    +    cbMarkup.Checked := fmMarkup in Hl.FoldConfig[i].Modes * FMask;
    +    cbOutline.Enabled := fmOutline in Hl.FoldConfig[i].SupportedModes * FMask;
    +    cbOutline.Checked := fmOutline in Hl.FoldConfig[i].Modes * FMask;
    +  end else
    +  begin
    +    cbMarkup.Enabled  := false;
    +    cbMarkup.Checked  := false;
    +    cbOutline.Enabled := false;
    +    cbOutline.Checked := false;
    +  end;
    +  FModeLock := False;
    +end;
    +
     function TEditorMarkupOptionsFrame.GetTitle: String;
     begin
       Result := lisAutoMarkup;
    @@ -314,6 +417,11 @@
       LanguageLabel.Caption := dlgLang;
       divKeyWordGroups.Caption := dlgPasKeywordsMatches;
     
    +  cbMarkup.Caption := dlgPasKeywordsMarkup;
    +  cbOutline.Caption := dlgPasKeywordsOutline;
    +  cbMarkupWordBracket.Caption := dlgMarkupWordBracket;
    +  cbMarkupOutline.Caption := dlgMarkupOutline;
    +
       with LanguageComboBox.Items do begin
         BeginUpdate;
         for i := 0 to EditorOpts.HighlighterList.Count - 1 do begin
    @@ -342,11 +450,17 @@
     
         chkExtPasKeywords.Checked := PasExtendedKeywordsMode;
         dropPasStringKeywords.ItemIndex := ord(PasStringKeywordMode);
    +
    +    FUseMarkupWordBracket := UseMarkupWordBracket;
    +    FUseMarkupOutline := UseMarkupOutline;
       end;
       AutoDelayTrackBarChange(nil);
     
       LanguageComboBox.ItemIndex := 0;
       LanguageComboBoxExit(LanguageComboBox);
    +  cbMarkupOutline.Checked := FUseMarkupOutline;
    +  cbMarkupWordBracket.Checked := FUseMarkupWordBracket;
    +  UpdateMarkupCheckBoxes;
     end;
     
     procedure TEditorMarkupOptionsFrame.WriteSettings(AOptions: TAbstractIDEOptions);
    @@ -369,6 +483,9 @@
     
         PasExtendedKeywordsMode := chkExtPasKeywords.Checked;
         PasStringKeywordMode := TSynPasStringMode(dropPasStringKeywords.ItemIndex);
    +
    +    UseMarkupWordBracket := FUseMarkupWordBracket;
    +    UseMarkupOutline := FUseMarkupOutline;
       end;
     end;
     
    Index: ide/lazarusidestrconsts.pas
    ===================================================================
    --- ide/lazarusidestrconsts.pas	(revision 52759)
    +++ ide/lazarusidestrconsts.pas	(working copy)
    @@ -1897,6 +1897,10 @@
       dlgBracketMatchGroup = 'Matching bracket pairs';
       dlgPasExtKeywordsGroup = 'Extended Pascal Keyword Options';
       dlgPasKeywordsMatches = 'Matching Keywords';
    +  dlgPasKeywordsMarkup = 'Markup (on caret)';
    +  dlgPasKeywordsOutline = 'Outline';
    +  dlgMarkupWordBracket = 'Word Brackets on caret (global)';
    +  dlgMarkupOutline = 'Outline (global)';
       dlgPasExtKeywords = 'Highlight control statements as keywords';
       dlgPasStringKeywords = 'Highlight "String" keyword(s)';
       dlgPasStringKeywordsOptDefault = 'Default';
    
  • syneditmarkupfoldcoloring.pas_v4.patch (37,102 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 52763)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -50,12 +50,14 @@
     unit SynEditMarkupFoldColoring;
     
     {$mode objfpc}{$H+}
    +{.$define SynEditMarkupFoldColoringDebug}
     
     interface
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText;
     
     type
     
    @@ -66,37 +68,39 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    +    function GetFirstCharacterColumn(index: Integer): Byte;
    +  private
    +    FHighlighter: TSynCustomFoldHighlighter;
         FNestList: TLazSynEditNestedFoldsList;
    +
    +    // cache
    +    FFirstCharacterColumn: Array of Byte;
    +    FEndLine: Array of Integer;
    +
         FDefaultGroup: integer;
    -     // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FFoldColorInfos: TMarkupFoldColorInfos;
         Colors : array of TColor;
    -
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    -
    +    FPreparedRow: integer;
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
    -
    -    function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
    +    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -115,36 +119,21 @@
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +  , SynHighlighterPas
    +{$ENDIF}
    +  ;
     
    -  {%region Sorting FoldInfo -fold}
    -  function CompareFI(Item1, Item2: Pointer): Integer;
    -  begin
    -    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
    -    if result = 0 then
    -        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
    -    if result = 0 then
    -        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
    -          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  WriteStr(Result, TPascalCodeFoldBlockType(p_FoldType));
    +  while length(Result) < 17 do Result := Result + ' ';
    +end;
    +{$ENDIF}
     
    -    SetLength(result, Length(a));
    -    for i := 0 to Pred(l.Count) do
    -      result[i] := PMarkupFoldColorInfo(l[i])^;
    -     l.Free;
    -  end;
    -  {%endregion}
    -
     { TSynEditMarkupFoldColors }
     
     constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
    @@ -151,23 +140,36 @@
     begin
       inherited Create(ASynEdit);
     
    -  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
    +  if Assigned(Lines) then begin
    +    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    SetLength(FFirstCharacterColumn, Lines.Count);
    +    SetLength(FEndLine, Lines.Count);
    +  end;
    +
    +  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(FHighlighter)
    +  and not (FHighlighter  is TSynCustomFoldHighlighter) then
    +    FHighlighter := nil;
    +
    +  FDefaultGroup := 0;
    +
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
       FNestList.ResetFilter;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    -  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    -  FNestList.IncludeOpeningOnLine := True; //False; //
    +  FNestList.FoldGroup := FDefaultGroup;
    +  FNestList.FoldFlags :=  [sfbIncludeDisabled];
    +  FNestList.IncludeOpeningOnLine := True;
     
       MarkupInfo.Foreground := clGreen;
    -  MarkupInfo.Background := clNone; //clFuchsia;
    +  MarkupInfo.Background := clNone;
       MarkupInfo.Style := [];
       MarkupInfo.StyleMask := [];
    -  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
    +  MarkupInfo.FrameEdges:= sfeLeft;
     
       SetLength(Colors, 5);
       Colors[0] := clRed;
       Colors[1] := $000098F7; //orange
       Colors[2] := $0022CC40; //green
    -  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
       Colors[3] := $00FF682A; //blue
       Colors[4] := $00CF00C4; //purple
     end;
    @@ -174,6 +176,10 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then begin
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
       FreeAndNil(FNestList);
       inherited Destroy;
     end;
    @@ -182,23 +188,27 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i, x2both: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if not Assigned(FHighlighter) then exit;
    +  if (FPreparedRow = aRow) then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
    +    {$ENDIF}
     
    -    x2both := -3; //flag
    -    for i := 0 to length(FHighlights)-1 do
    -      with FHighlights[i] do
    +    x2both := -3;
    +    for i := 0 to length(FFoldColorInfos)-1 do
    +      with FFoldColorInfos[i] do
             if not Ignore
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    -          //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +        and (aStartCol.Logical < X2) then begin
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
    +          {$ENDIF}
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,25 +219,14 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    +          if Border then begin
                 MarkupInfo.FrameColor:= Colors[ColorIdx];
    -            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    +            MarkupInfo.FrameEdges:= sfeLeft;
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
                 MarkupInfo.Foreground := Colors[ColorIdx];
    -
    -          //MarkupInfo.FrameEdges:= sfeAround; //debug
    -
    -          {//2nd debug
    -          if x > x2 then
    -          begin
    -            MarkupInfo.Background:= clYellow;
    -            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
    -            MarkupInfo.FrameColor:= clBlue; //debug
    -          end;}
    -
    -          //break;
    +          end;
             end;
       end;
     end;
    @@ -237,36 +236,85 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter)
    +  or (FPreparedRow <> aRow) then
    +    exit;
    +
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    -  for i := 0 to length(FHighlights)-1  do
    -    with FHighlights[i] do
    -    begin
    -      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
    -        //continue;
    -      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
    -      begin
    -        ANextLog := FHighlights[i].X;
    +  for i := 0 to length(FFoldColorInfos)-1  do
    +    with FFoldColorInfos[i] do begin
    +      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
    +        ANextLog := FFoldColorInfos[i].X;
             break;
           end;
         end;
     end;
     
    +function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
    +var
    +  l: String;
    +  p: Integer;
    +begin
    +  l := SynEdit.Lines[index];
    +  p := 1;
    +  while not (l[p] in [#13, #10, #0])
    +  and (l[p] = #32) do inc(p);
    +  if p > 255 then p := 255;
    +  Result := p;
    +end;
    +
     procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
     var
    -  i,lvl,z : integer; //iterate parents fold
    +  i,lvl,z: integer;
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    +<<<<<<< .mine
    +  var
    +    p, s, l: integer;
    +||||||| .r52761
    +  var x,j : integer;
    +=======
    +>>>>>>> .r52763
       begin
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    // get column of first character in row
    +    s := Length(FFirstCharacterColumn);
    +    if (s <= ANode.LineIndex)
    +    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
    +      p := FirstCharacterColumn[ANode.LineIndex];
    +      if s > ANode.LineIndex then begin
    +        FFirstCharacterColumn[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    s := Length(FEndLine);
    +    if (s <= ANode.LineIndex)
    +    or (FEndLine[ANode.LineIndex] = 0) then begin
    +      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
    +      if s > ANode.LineIndex then begin
    +        FEndLine[ANode.LineIndex] := l;
    +      end else begin
    +        DebugLn('!!! FEndLine-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z+1);
    +    with FFoldColorInfos[z] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    -      Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      if s <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumn[ANode.LineIndex];
    +      Y  := aRow;
    +      X2 := X + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -273,7 +321,9 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
    +      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
         end;
       end;
     
    @@ -281,60 +331,62 @@
       y, lvlB,lvlA: Integer;
       TmpNode: TSynFoldNodeInfo;
       NestCount : integer;
    -  procedure Later(var J:integer);
    -  begin
    -    inc(J);
    -  end;
    -  function Allowed(J: integer):boolean;
    -  begin
    -    result := J < NestCount;
    -  end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
    +  FHighlighter.CurrentLines := Lines;
     
       lvl := 0;
       i := 0;
    -  while Allowed(i) do
    -  begin
    -
    +  while i < NestCount do begin
         TmpNode := FNestList.HLNode[i];
    -    //find till valid
    -    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
    -    begin
    -      Later(i);
    -      TmpNode := FNestList.HLNode[i];
    -    end;
    +    if (sfaOutline in TmpNode.FoldAction)
    +    and not (sfaInvalid in TmpNode.FoldAction) then
    +      //avoid bug of IncludeOpeningOnLine := False;
    +      if (sfaOpen in TmpNode.FoldAction)
    +      and (TmpNode.LineIndex + 1 = aRow) then begin
    +        {do nothing here}
    +      end else begin
    +        lvlB := lvl;
     
    -    if (sfaOutline in TmpNode.FoldAction ) then
    -    //avoid bug of IncludeOpeningOnLine := False;
    -    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
    -    begin {do nothing here} end
    -    else
    -    begin
    -      lvlB := lvl;
    +        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    +          inc(lvl)
    +        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +          dec(lvl);
    +        //if (FLastNode.LineIndex >= 0)
    +        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +        // inc(lvl);
     
    -      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    -        dec(lvl);
    +        AddVerticalLine(TmpNode);
     
    -      AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -        inc(lvl);
    +        //if (z > 0)
    +        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
    +        //  // if child is on same x-pos keep level
    +        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
    +        //    lvl := FFoldColorInfos[z - 1].Level;
    +        //    FFoldColorInfos[z].Level := lvl;
    +        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        //  end;
    +        //end;
     
    -      lvlA := lvl;
    +        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    +          inc(lvl);
     
    -      with FHighlights[z] do begin
    -        LevelBefore := lvlB;
    -        LevelAfter  := lvlA;
    +        if sfaOpen in TmpNode.FoldAction then
    +          FLastNode := TmpNode;
    +
    +        lvlA := lvl;
    +
    +        with FFoldColorInfos[z] do begin
    +          LevelBefore := lvlB;
    +          LevelAfter  := lvlA;
    +        end;
           end;
    -    end;
    -
    -    Later(i);
    -    //break; //debug
    +    inc(i);
       end;
     end;
     
    @@ -345,33 +397,29 @@
       procedure AddHighlight( ANode: TSynFoldNodeInfo );
       var x,j : integer;
       begin
    -        //don't replace; don't add when already found
         x  := ANode.LogXStart + 1;
    -
         if ANode.LogXStart < ANode.LogXEnd then
    -    for j := 0 to Pred(length(FHighlights)) do
    -      if (FHighlights[j].X = x)
    -      and (FHighlights[j].Border)
    -      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
    -      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
    +    for j := 0 to Pred(length(FFoldColorInfos)) do
    +      if (FFoldColorInfos[j].X = x)
    +      and (FFoldColorInfos[j].Border)
    +      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    +      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
           then begin
    -       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
    -       FHighlights[j].Border := False
    -
    +       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    +       FFoldColorInfos[j].Border := False
           end;
     
    -    //exit; //debug
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z + 1);
    +    with FFoldColorInfos[z] do begin
           Border := False;
           SrcNode:= ANode; //needed by close node
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -378,91 +426,105 @@
       end;
     
     var
    -  y,i,j,lvlB,lvlA : integer;
    -  HL: TSynCustomFoldHighlighter;
    +  LineIdx,i,j,lvlB,lvlA : integer;
       NodeList: TLazSynFoldNodeInfoList;
       TmpNode: TSynFoldNodeInfo;
    -  Found : boolean;
    +  Found: boolean;
     begin
    -  y := aRow -1;
    +  LineIdx := ToIdx(aRow);
     
    -  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -  HL.CurrentLines := Lines;
    -  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
    +  FHighlighter.CurrentLines := Lines;
    +  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
     
    -  NodeList := HL.FoldNodeInfo[y];
    +  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
       NodeList.AddReference;
       try
         NodeList.ActionFilter := [sfaOutline];
    -    //NodeList.FoldFlags:= [sfbIncludeDisabled];
         lvl := 0;
    -    J := Length(FHighlights)-1;
    +    J := Length(FFoldColorInfos) - 1;
         if J >=0 then
    -      lvl := max(0,FHighlights[J].LevelAfter);
    +      lvl := max(0,FFoldColorInfos[J].LevelAfter);
         i := 0;
         repeat
           TmpNode := NodeList[i];
     
    -      //find till valid
    -      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
    -      begin
    -        inc(i);
    -        TmpNode := NodeList[i];
    -      end;
    -      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
    -        if sfaOpen in TmpNode.FoldAction then
    -        begin
    +      if not (sfaInvalid in TmpNode.FoldAction)
    +      and (sfaOutline in TmpNode.FoldAction) then begin
    +        if sfaOpen in TmpNode.FoldAction then begin
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          //if (FLastNode.LineIndex >= 0)
    +          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +          // inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          //if (z > 0)
    +          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
    +          //begin
    +          //  // if child is on same x-pos keep level
    +          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
    +          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
    +          //    lvl := FFoldColorInfos[z - 1].Level;
    +          //    FFoldColorInfos[z].Level := lvl;
    +          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +          //  end;
    +          //end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
                 inc(lvl);
    +
               lvlA := lvl;
     
    -          with FHighlights[z] do begin
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
    +          with FFoldColorInfos[z] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
               end;
    -
    -        end
    -        else
    -        if sfaClose in TmpNode.FoldAction then
    -        begin
    +        end else if sfaClose in TmpNode.FoldAction then begin
               Found := False;
    -          for j := Length(FHighlights)-1 downto 0 do begin
    -            with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +          for j := Length(FFoldColorInfos)-1 downto 0 do begin
    +            with FFoldColorInfos[j].SrcNode do begin
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FFoldColorInfos[j].Level;
    +                lvlA := FFoldColorInfos[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            with FFoldColorInfos[z] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
    +              for j := j to z - 1 do begin
    +                FFoldColorInfos[j] := FFoldColorInfos[j+1];
    +              end;
    +              SetLength(FFoldColorInfos, z);
    +            end;
               end;
    -
    -          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -            //inc(lvl);
             end;
           end;
    -
           inc(i);
         until i >= NodeList.Count;
    -
       finally
         NodeList.ReleaseReference;
       end;
    @@ -469,197 +531,274 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, LastX, j: Integer;
     begin
    -  CurrentY := aRow;
    -  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter) then exit;
    +  FPreparedRow := aRow;
    +  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    -  //DoMarkupFoldAtRow(aRow);
    +  // invalidate fLastNode
    +  FLastNode.LineIndex := -1;
    +
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
    -  //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := length(FFoldColorInfos) - 1 downto 0 do begin
    +    if FFoldColorInfos[i].X > LastX then begin
    +      for j := i to length(FFoldColorInfos) - 2 do begin
    +        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
    +      end;
    +      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
    +    end;
    +    LastX := FFoldColorInfos[i].X;
    +  end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('EndMarkup');
    +  {$ENDIF}
       inherited EndMarkup;
       FNestList.Clear; // for next markup start
     end;
     
    -function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
    -begin
    -  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -end;
    -
     procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
     begin
       if FDefaultGroup = AValue then Exit;
       FDefaultGroup := AValue;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldGroup := FDefaultGroup;
     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;
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +    lLineIdx := ToIdx(pLine);
    +    pNestList.Line := lLineIdx;
    +    lCount := pNestList.Count;
    +    SetLength(pList, lCount);
    +    lAnz := 0;
    +    for i := 0 to lCount - 1 do begin
    +      lNode := pNestList.HLNode[i];
    +      if (sfaInvalid in lNode.FoldAction)
    +      or (
    +        (sfaOpen in lNode.FoldAction)
    +        and (lNode.LineIndex = lLineIdx)
    +      ) then
    +        Continue;
     
    -    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?)
    +      pList[i] := lNode;
    +      inc(lAnz);
         end;
    +    SetLength(pList, lAnz);
    +  end;
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    +var
    +  i, lMinAnz, lEndLine, j, l: integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  {$ENDIF}
     
    -    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
    +  if not Assigned(FHighlighter) then exit;
    +  FHighlighter.CurrentLines := Lines;
    +  if EndLine < 0 then
    +    EndLine := StartLine
    +  else
    +    // endline seems to be the first line after the change
    +    EndLine := EndLine - 1;
    +  lEndLine := EndLine;
     
    -    NodeList := HL.FoldNodeInfo[y];
    -    NodeList.AddReference;
    -    try
    -      NodeList.ActionFilter := [sfaOpen];
    -      i := 0;
    -      repeat
    -        TmpNode := NodeList[i];
    +  SetLength(lStartNestList, 0);
    +  SetLength(lEndNestList, 0);
     
    -        if TmpNode.LogXStart < X-1 then
    -        begin
    -          inc(i);
    -          continue;
    -        end;
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at Start:');
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -        //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;
    +  FillNestList(lEndNestList, EndLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at End:');
    +  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -        inc(i);
    -      until i >= NodeList.Count;
    -
    -    finally
    -      NodeList.ReleaseReference;
    +  // delete all nodes in lEndNodeList which where active at StartLine
    +  // to get the nodes which reach behind EndLine
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
       end;
     
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   Remaining Nodes:');
    +    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex]]);
    +    {$ENDIF}
    +    // does position of first character change for remaining node?
    +    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
    +      // position of first character changed -> find endline
    +      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
    +  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;
    +  // check for changes of endline for node which are active at StartLine
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    if sfaOutline in FoldAction then begin
    +      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
    +      if l <> FEndLine[LineIndex] then begin
    +        FEndLine[LineIndex] := l;
    +        lEndLine := Max(lEndLine, l);
           end;
         end;
     
    -    if n < 0 then exit;
    +  // invalidate cache
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
    +    FFirstCharacterColumn[i] := 0;
    +    FEndLine[i] := 0;
    +  end;
     
    -    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);
    +  if lEndLine > EndLine then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    {$ENDIF}
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  end;
    +end;
    +
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
    +var
    +  old: TSynEditStrings;
    +begin
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // change:
    +    // remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // change:
    +    if Assigned(AValue) then begin
    +      // set cache size
    +      SetLength(FFirstCharacterColumn, AValue.Count);
    +      SetLength(FEndLine, AValue.Count);
    +      // add Changehandler
    +      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    +      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    end else begin
    +      // clear cache
    +      SetLength(FFirstCharacterColumn, 0);
    +      SetLength(FEndLine, 0);
         end;
    +  end;
    +end;
     
    -  end;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
     var
    -  EndFoldLine,y : integer;
    +  absCount,
    +  idx, i: 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);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  idx := ToIdx(aIndex);
    +  if aCount < 0 then begin
    +    // lines deleted
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
    +      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
    +      FEndLine[i] := FEndLine[i + absCount];
    +    end;
       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}
    +  SetLength(FFirstCharacterColumn, Sender.Count);
    +  SetLength(FEndLine, Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // lines added
    +      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
    +        FEndLine[i + aCount] := FEndLine[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end else begin
    +      // first lines will be inserted
    +      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
    +  aIndex, aCount: Integer);
    +var
    +  newHighlighter: TSynCustomFoldHighlighter;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  if (aIndex <> -1)
    +  or (aCount <> -1) then
    +    exit;
     
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    -end;
    +  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(newHighlighter)
    +  and not (newHighlighter is TSynCustomFoldHighlighter) then
    +    newHighlighter := nil;
     
    +  if (newHighlighter = FHighlighter) then
    +    exit;
     
    +  FHighlighter := newHighlighter;
     
    +  FreeAndNil(FNestList);
    +  if Assigned(FHighlighter) then begin
    +    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +    FNestList.ResetFilter;
    +    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +    FNestList.IncludeOpeningOnLine := True; //False; //
    +  end;
    +end;
    +
     end.
     
    
  • syneditmarkupfoldcoloring.pas_v5.patch (37,593 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 52763)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -50,12 +50,14 @@
     unit SynEditMarkupFoldColoring;
     
     {$mode objfpc}{$H+}
    +{$define SynEditMarkupFoldColoringDebug}
     
     interface
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText;
     
     type
     
    @@ -66,37 +68,39 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    +    function GetFirstCharacterColumn(index: Integer): Byte;
    +  private
    +    FHighlighter: TSynCustomFoldHighlighter;
         FNestList: TLazSynEditNestedFoldsList;
    +
    +    // cache
    +    FFirstCharacterColumn: Array of Byte;
    +    FEndLine: Array of Integer;
    +
         FDefaultGroup: integer;
    -     // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FFoldColorInfos: TMarkupFoldColorInfos;
         Colors : array of TColor;
    -
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    -
    +    FPreparedRow: integer;
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
    -
    -    function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
    +    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -115,36 +119,21 @@
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +  , SynHighlighterPas
    +{$ENDIF}
    +  ;
     
    -  {%region Sorting FoldInfo -fold}
    -  function CompareFI(Item1, Item2: Pointer): Integer;
    -  begin
    -    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
    -    if result = 0 then
    -        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
    -    if result = 0 then
    -        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
    -          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  WriteStr(Result, TPascalCodeFoldBlockType(p_FoldType));
    +  while length(Result) < 17 do Result := Result + ' ';
    +end;
    +{$ENDIF}
     
    -    SetLength(result, Length(a));
    -    for i := 0 to Pred(l.Count) do
    -      result[i] := PMarkupFoldColorInfo(l[i])^;
    -     l.Free;
    -  end;
    -  {%endregion}
    -
     { TSynEditMarkupFoldColors }
     
     constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
    @@ -151,23 +140,36 @@
     begin
       inherited Create(ASynEdit);
     
    -  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
    +  if Assigned(Lines) then begin
    +    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    SetLength(FFirstCharacterColumn, Lines.Count);
    +    SetLength(FEndLine, Lines.Count);
    +  end;
    +
    +  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(FHighlighter)
    +  and not (FHighlighter  is TSynCustomFoldHighlighter) then
    +    FHighlighter := nil;
    +
    +  FDefaultGroup := 0;
    +
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
       FNestList.ResetFilter;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    -  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    -  FNestList.IncludeOpeningOnLine := True; //False; //
    +  FNestList.FoldGroup := FDefaultGroup;
    +  FNestList.FoldFlags :=  [sfbIncludeDisabled];
    +  FNestList.IncludeOpeningOnLine := True;
     
       MarkupInfo.Foreground := clGreen;
    -  MarkupInfo.Background := clNone; //clFuchsia;
    +  MarkupInfo.Background := clNone;
       MarkupInfo.Style := [];
       MarkupInfo.StyleMask := [];
    -  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
    +  MarkupInfo.FrameEdges:= sfeLeft;
     
       SetLength(Colors, 5);
       Colors[0] := clRed;
       Colors[1] := $000098F7; //orange
       Colors[2] := $0022CC40; //green
    -  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
       Colors[3] := $00FF682A; //blue
       Colors[4] := $00CF00C4; //purple
     end;
    @@ -174,6 +176,10 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then begin
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
       FreeAndNil(FNestList);
       inherited Destroy;
     end;
    @@ -182,23 +188,27 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i, x2both: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if not Assigned(FHighlighter) then exit;
    +  if (FPreparedRow = aRow) then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
    +    {$ENDIF}
     
    -    x2both := -3; //flag
    -    for i := 0 to length(FHighlights)-1 do
    -      with FHighlights[i] do
    +    x2both := -3;
    +    for i := 0 to length(FFoldColorInfos)-1 do
    +      with FFoldColorInfos[i] do
             if not Ignore
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    -          //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +        and (aStartCol.Logical < X2) then begin
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
    +          {$ENDIF}
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,25 +219,14 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    +          if Border then begin
                 MarkupInfo.FrameColor:= Colors[ColorIdx];
    -            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    +            MarkupInfo.FrameEdges:= sfeLeft;
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
                 MarkupInfo.Foreground := Colors[ColorIdx];
    -
    -          //MarkupInfo.FrameEdges:= sfeAround; //debug
    -
    -          {//2nd debug
    -          if x > x2 then
    -          begin
    -            MarkupInfo.Background:= clYellow;
    -            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
    -            MarkupInfo.FrameColor:= clBlue; //debug
    -          end;}
    -
    -          //break;
    +          end;
             end;
       end;
     end;
    @@ -237,36 +236,80 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter)
    +  or (FPreparedRow <> aRow) then
    +    exit;
    +
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    -  for i := 0 to length(FHighlights)-1  do
    -    with FHighlights[i] do
    -    begin
    -      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
    -        //continue;
    -      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
    -      begin
    -        ANextLog := FHighlights[i].X;
    +  for i := 0 to length(FFoldColorInfos)-1  do
    +    with FFoldColorInfos[i] do begin
    +      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
    +        ANextLog := FFoldColorInfos[i].X;
             break;
           end;
         end;
     end;
     
    +function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
    +var
    +  l: String;
    +  p: Integer;
    +begin
    +  l := SynEdit.Lines[index];
    +  p := 1;
    +  while not (l[p] in [#13, #10, #0])
    +  and (l[p] = #32) do inc(p);
    +  if p > 255 then p := 255;
    +  Result := p;
    +end;
    +
     procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
     var
    -  i,lvl,z : integer; //iterate parents fold
    +  i,lvl,z: integer;
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    +  var
    +    p, s, l: integer;
       begin
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    // get column of first character in row
    +    s := Length(FFirstCharacterColumn);
    +    if (s <= ANode.LineIndex)
    +    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
    +      p := FirstCharacterColumn[ANode.LineIndex];
    +      if s > ANode.LineIndex then begin
    +        FFirstCharacterColumn[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    s := Length(FEndLine);
    +    if (s <= ANode.LineIndex)
    +    or (FEndLine[ANode.LineIndex] = 0) then begin
    +      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
    +      if s > ANode.LineIndex then begin
    +        FEndLine[ANode.LineIndex] := l;
    +      end else begin
    +        DebugLn('!!! FEndLine-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z+1);
    +    with FFoldColorInfos[z] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    -      Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      if s <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumn[ANode.LineIndex];
    +      Y  := aRow;
    +      X2 := X + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -273,7 +316,9 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
    +      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
         end;
       end;
     
    @@ -281,60 +326,62 @@
       y, lvlB,lvlA: Integer;
       TmpNode: TSynFoldNodeInfo;
       NestCount : integer;
    -  procedure Later(var J:integer);
    -  begin
    -    inc(J);
    -  end;
    -  function Allowed(J: integer):boolean;
    -  begin
    -    result := J < NestCount;
    -  end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
    +  FHighlighter.CurrentLines := Lines;
     
       lvl := 0;
       i := 0;
    -  while Allowed(i) do
    -  begin
    -
    +  while i < NestCount do begin
         TmpNode := FNestList.HLNode[i];
    -    //find till valid
    -    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
    -    begin
    -      Later(i);
    -      TmpNode := FNestList.HLNode[i];
    -    end;
    +    if (sfaOutline in TmpNode.FoldAction)
    +    and not (sfaInvalid in TmpNode.FoldAction) then
    +      //avoid bug of IncludeOpeningOnLine := False;
    +      if (sfaOpen in TmpNode.FoldAction)
    +      and (TmpNode.LineIndex + 1 = aRow) then begin
    +        {do nothing here}
    +      end else begin
    +        lvlB := lvl;
     
    -    if (sfaOutline in TmpNode.FoldAction ) then
    -    //avoid bug of IncludeOpeningOnLine := False;
    -    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
    -    begin {do nothing here} end
    -    else
    -    begin
    -      lvlB := lvl;
    +        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    +          inc(lvl)
    +        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +          dec(lvl);
    +        //if (FLastNode.LineIndex >= 0)
    +        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +        // inc(lvl);
     
    -      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    -        dec(lvl);
    +        AddVerticalLine(TmpNode);
     
    -      AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -        inc(lvl);
    +        //if (z > 0)
    +        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
    +        //  // if child is on same x-pos keep level
    +        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
    +        //    lvl := FFoldColorInfos[z - 1].Level;
    +        //    FFoldColorInfos[z].Level := lvl;
    +        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        //  end;
    +        //end;
     
    -      lvlA := lvl;
    +        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    +          inc(lvl);
     
    -      with FHighlights[z] do begin
    -        LevelBefore := lvlB;
    -        LevelAfter  := lvlA;
    +        if sfaOpen in TmpNode.FoldAction then
    +          FLastNode := TmpNode;
    +
    +        lvlA := lvl;
    +
    +        with FFoldColorInfos[z] do begin
    +          LevelBefore := lvlB;
    +          LevelAfter  := lvlA;
    +        end;
           end;
    -    end;
    -
    -    Later(i);
    -    //break; //debug
    +    inc(i);
       end;
     end;
     
    @@ -345,33 +392,29 @@
       procedure AddHighlight( ANode: TSynFoldNodeInfo );
       var x,j : integer;
       begin
    -        //don't replace; don't add when already found
         x  := ANode.LogXStart + 1;
    -
         if ANode.LogXStart < ANode.LogXEnd then
    -    for j := 0 to Pred(length(FHighlights)) do
    -      if (FHighlights[j].X = x)
    -      and (FHighlights[j].Border)
    -      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
    -      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
    +    for j := 0 to Pred(length(FFoldColorInfos)) do
    +      if (FFoldColorInfos[j].X = x)
    +      and (FFoldColorInfos[j].Border)
    +      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    +      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
           then begin
    -       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
    -       FHighlights[j].Border := False
    -
    +       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    +       FFoldColorInfos[j].Border := False
           end;
     
    -    //exit; //debug
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z + 1);
    +    with FFoldColorInfos[z] do begin
           Border := False;
           SrcNode:= ANode; //needed by close node
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -378,91 +421,105 @@
       end;
     
     var
    -  y,i,j,lvlB,lvlA : integer;
    -  HL: TSynCustomFoldHighlighter;
    +  LineIdx,i,j,lvlB,lvlA : integer;
       NodeList: TLazSynFoldNodeInfoList;
       TmpNode: TSynFoldNodeInfo;
    -  Found : boolean;
    +  Found: boolean;
     begin
    -  y := aRow -1;
    +  LineIdx := ToIdx(aRow);
     
    -  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -  HL.CurrentLines := Lines;
    -  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
    +  FHighlighter.CurrentLines := Lines;
    +  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
     
    -  NodeList := HL.FoldNodeInfo[y];
    +  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
       NodeList.AddReference;
       try
         NodeList.ActionFilter := [sfaOutline];
    -    //NodeList.FoldFlags:= [sfbIncludeDisabled];
         lvl := 0;
    -    J := Length(FHighlights)-1;
    +    J := Length(FFoldColorInfos) - 1;
         if J >=0 then
    -      lvl := max(0,FHighlights[J].LevelAfter);
    +      lvl := max(0,FFoldColorInfos[J].LevelAfter);
         i := 0;
         repeat
           TmpNode := NodeList[i];
     
    -      //find till valid
    -      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
    -      begin
    -        inc(i);
    -        TmpNode := NodeList[i];
    -      end;
    -      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
    -        if sfaOpen in TmpNode.FoldAction then
    -        begin
    +      if not (sfaInvalid in TmpNode.FoldAction)
    +      and (sfaOutline in TmpNode.FoldAction) then begin
    +        if sfaOpen in TmpNode.FoldAction then begin
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          //if (FLastNode.LineIndex >= 0)
    +          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +          // inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          //if (z > 0)
    +          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
    +          //begin
    +          //  // if child is on same x-pos keep level
    +          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
    +          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
    +          //    lvl := FFoldColorInfos[z - 1].Level;
    +          //    FFoldColorInfos[z].Level := lvl;
    +          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +          //  end;
    +          //end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
                 inc(lvl);
    +
               lvlA := lvl;
     
    -          with FHighlights[z] do begin
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
    +          with FFoldColorInfos[z] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
               end;
    -
    -        end
    -        else
    -        if sfaClose in TmpNode.FoldAction then
    -        begin
    +        end else if sfaClose in TmpNode.FoldAction then begin
               Found := False;
    -          for j := Length(FHighlights)-1 downto 0 do begin
    -            with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +          for j := Length(FFoldColorInfos)-1 downto 0 do begin
    +            with FFoldColorInfos[j].SrcNode do begin
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FFoldColorInfos[j].Level;
    +                lvlA := FFoldColorInfos[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            with FFoldColorInfos[z] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
    +              for j := j to z - 1 do begin
    +                FFoldColorInfos[j] := FFoldColorInfos[j+1];
    +              end;
    +              SetLength(FFoldColorInfos, z);
    +            end;
               end;
    -
    -          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -            //inc(lvl);
             end;
           end;
    -
           inc(i);
         until i >= NodeList.Count;
    -
       finally
         NodeList.ReleaseReference;
       end;
    @@ -469,197 +526,277 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, LastX, j: Integer;
     begin
    -  CurrentY := aRow;
    -  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter) then exit;
    +  FPreparedRow := aRow;
    +  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    -  //DoMarkupFoldAtRow(aRow);
    +  // invalidate fLastNode
    +  FLastNode.LineIndex := -1;
    +
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
    -  //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := length(FFoldColorInfos) - 1 downto 0 do begin
    +    if FFoldColorInfos[i].X > LastX then begin
    +      for j := i to length(FFoldColorInfos) - 2 do begin
    +        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
    +      end;
    +      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
    +    end;
    +    LastX := FFoldColorInfos[i].X;
    +  end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('EndMarkup');
    +  {$ENDIF}
       inherited EndMarkup;
       FNestList.Clear; // for next markup start
     end;
     
    -function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
    -begin
    -  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -end;
    -
     procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
     begin
       if FDefaultGroup = AValue then Exit;
       FDefaultGroup := AValue;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldGroup := FDefaultGroup;
     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;
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +    lLineIdx := ToIdx(pLine);
    +    pNestList.Line := lLineIdx;
    +    lCount := pNestList.Count;
    +    SetLength(pList, lCount);
    +    lAnz := 0;
    +    for i := 0 to lCount - 1 do begin
    +      lNode := pNestList.HLNode[i];
    +      if (sfaInvalid in lNode.FoldAction)
    +      or (
    +        (sfaOpen in lNode.FoldAction)
    +        and (lNode.LineIndex = lLineIdx)
    +      ) then
    +        Continue;
     
    -    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?)
    +      pList[i] := lNode;
    +      inc(lAnz);
         end;
    +    SetLength(pList, lAnz);
    +  end;
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    +var
    +  i, lMinAnz, lEndLine, j, l: integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  {$ENDIF}
     
    -    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
    +  if not Assigned(FHighlighter) then exit;
    +  FHighlighter.CurrentLines := Lines;
    +  if EndLine < 0 then
    +    EndLine := StartLine
    +  else
    +    // endline seems to be the first line after the change
    +    EndLine := EndLine - 1;
    +  lEndLine := EndLine;
     
    -    NodeList := HL.FoldNodeInfo[y];
    -    NodeList.AddReference;
    -    try
    -      NodeList.ActionFilter := [sfaOpen];
    -      i := 0;
    -      repeat
    -        TmpNode := NodeList[i];
    +  SetLength(lStartNestList, 0);
    +  SetLength(lEndNestList, 0);
     
    -        if TmpNode.LogXStart < X-1 then
    -        begin
    -          inc(i);
    -          continue;
    -        end;
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at Start:');
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -        //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;
    +  FillNestList(lEndNestList, EndLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at End:');
    +  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -        inc(i);
    -      until i >= NodeList.Count;
    -
    -    finally
    -      NodeList.ReleaseReference;
    +  // delete all nodes in lEndNodeList which where active at StartLine
    +  // to get the nodes which reach behind EndLine
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
       end;
     
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   Remaining Nodes:');
    +    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex]]);
    +    {$ENDIF}
    +    // does position of first character change for remaining node?
    +    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
    +      // position of first character changed -> find endline
    +      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
    +  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;
    +  // check for changes of endline for node which are active at StartLine
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    if sfaOutline in FoldAction then begin
    +      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
    +      if l <> FEndLine[LineIndex] then begin
    +        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
    +        FEndLine[LineIndex] := l;
    +        {$IFDEF SynEditMarkupFoldColoringDebug}
    +        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +        {$ENDIF}
           end;
         end;
     
    -    if n < 0 then exit;
    +  // invalidate cache
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
    +    FFirstCharacterColumn[i] := 0;
    +    FEndLine[i] := 0;
    +  end;
     
    -    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);
    +  if lEndLine > EndLine then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    {$ENDIF}
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  end;
    +end;
    +
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
    +var
    +  old: TSynEditStrings;
    +begin
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // change:
    +    // remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // change:
    +    if Assigned(AValue) then begin
    +      // set cache size
    +      SetLength(FFirstCharacterColumn, AValue.Count);
    +      SetLength(FEndLine, AValue.Count);
    +      // add Changehandler
    +      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    +      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    end else begin
    +      // clear cache
    +      SetLength(FFirstCharacterColumn, 0);
    +      SetLength(FEndLine, 0);
         end;
    +  end;
    +end;
     
    -  end;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
     var
    -  EndFoldLine,y : integer;
    +  absCount,
    +  idx, i: 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);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  idx := ToIdx(aIndex);
    +  if aCount < 0 then begin
    +    // lines deleted
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
    +      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
    +      FEndLine[i] := FEndLine[i + absCount];
    +    end;
       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}
    +  SetLength(FFirstCharacterColumn, Sender.Count);
    +  SetLength(FEndLine, Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // lines added
    +      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
    +        FEndLine[i + aCount] := FEndLine[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end else begin
    +      // first lines will be inserted
    +      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
    +  aIndex, aCount: Integer);
    +var
    +  newHighlighter: TSynCustomFoldHighlighter;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  if (aIndex <> -1)
    +  or (aCount <> -1) then
    +    exit;
     
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    -end;
    +  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(newHighlighter)
    +  and not (newHighlighter is TSynCustomFoldHighlighter) then
    +    newHighlighter := nil;
     
    +  if (newHighlighter = FHighlighter) then
    +    exit;
     
    +  FHighlighter := newHighlighter;
     
    +  FreeAndNil(FNestList);
    +  if Assigned(FHighlighter) then begin
    +    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +    FNestList.ResetFilter;
    +    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +    FNestList.IncludeOpeningOnLine := True; //False; //
    +  end;
    +end;
    +
     end.
     
    
  • syneditmarkupfoldcoloring.pas_v6.patch (37,594 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 52763)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -50,12 +50,14 @@
     unit SynEditMarkupFoldColoring;
     
     {$mode objfpc}{$H+}
    +{.$define SynEditMarkupFoldColoringDebug}
     
     interface
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText;
     
     type
     
    @@ -66,37 +68,39 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    +    function GetFirstCharacterColumn(index: Integer): Byte;
    +  private
    +    FHighlighter: TSynCustomFoldHighlighter;
         FNestList: TLazSynEditNestedFoldsList;
    +
    +    // cache
    +    FFirstCharacterColumn: Array of Byte;
    +    FEndLine: Array of Integer;
    +
         FDefaultGroup: integer;
    -     // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FFoldColorInfos: TMarkupFoldColorInfos;
         Colors : array of TColor;
    -
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    -
    +    FPreparedRow: integer;
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
    -
    -    function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
    +    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -115,36 +119,21 @@
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +  , SynHighlighterPas
    +{$ENDIF}
    +  ;
     
    -  {%region Sorting FoldInfo -fold}
    -  function CompareFI(Item1, Item2: Pointer): Integer;
    -  begin
    -    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
    -    if result = 0 then
    -        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
    -    if result = 0 then
    -        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
    -          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  WriteStr(Result, TPascalCodeFoldBlockType(p_FoldType));
    +  while length(Result) < 17 do Result := Result + ' ';
    +end;
    +{$ENDIF}
     
    -    SetLength(result, Length(a));
    -    for i := 0 to Pred(l.Count) do
    -      result[i] := PMarkupFoldColorInfo(l[i])^;
    -     l.Free;
    -  end;
    -  {%endregion}
    -
     { TSynEditMarkupFoldColors }
     
     constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
    @@ -151,23 +140,36 @@
     begin
       inherited Create(ASynEdit);
     
    -  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
    +  if Assigned(Lines) then begin
    +    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    SetLength(FFirstCharacterColumn, Lines.Count);
    +    SetLength(FEndLine, Lines.Count);
    +  end;
    +
    +  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(FHighlighter)
    +  and not (FHighlighter  is TSynCustomFoldHighlighter) then
    +    FHighlighter := nil;
    +
    +  FDefaultGroup := 0;
    +
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
       FNestList.ResetFilter;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    -  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    -  FNestList.IncludeOpeningOnLine := True; //False; //
    +  FNestList.FoldGroup := FDefaultGroup;
    +  FNestList.FoldFlags :=  [sfbIncludeDisabled];
    +  FNestList.IncludeOpeningOnLine := True;
     
       MarkupInfo.Foreground := clGreen;
    -  MarkupInfo.Background := clNone; //clFuchsia;
    +  MarkupInfo.Background := clNone;
       MarkupInfo.Style := [];
       MarkupInfo.StyleMask := [];
    -  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
    +  MarkupInfo.FrameEdges:= sfeLeft;
     
       SetLength(Colors, 5);
       Colors[0] := clRed;
       Colors[1] := $000098F7; //orange
       Colors[2] := $0022CC40; //green
    -  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
       Colors[3] := $00FF682A; //blue
       Colors[4] := $00CF00C4; //purple
     end;
    @@ -174,6 +176,10 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then begin
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
       FreeAndNil(FNestList);
       inherited Destroy;
     end;
    @@ -182,23 +188,27 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i, x2both: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if not Assigned(FHighlighter) then exit;
    +  if (FPreparedRow = aRow) then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
    +    {$ENDIF}
     
    -    x2both := -3; //flag
    -    for i := 0 to length(FHighlights)-1 do
    -      with FHighlights[i] do
    +    x2both := -3;
    +    for i := 0 to length(FFoldColorInfos)-1 do
    +      with FFoldColorInfos[i] do
             if not Ignore
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    -          //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +        and (aStartCol.Logical < X2) then begin
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
    +          {$ENDIF}
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,25 +219,14 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    +          if Border then begin
                 MarkupInfo.FrameColor:= Colors[ColorIdx];
    -            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    +            MarkupInfo.FrameEdges:= sfeLeft;
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
                 MarkupInfo.Foreground := Colors[ColorIdx];
    -
    -          //MarkupInfo.FrameEdges:= sfeAround; //debug
    -
    -          {//2nd debug
    -          if x > x2 then
    -          begin
    -            MarkupInfo.Background:= clYellow;
    -            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
    -            MarkupInfo.FrameColor:= clBlue; //debug
    -          end;}
    -
    -          //break;
    +          end;
             end;
       end;
     end;
    @@ -237,36 +236,80 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter)
    +  or (FPreparedRow <> aRow) then
    +    exit;
    +
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    -  for i := 0 to length(FHighlights)-1  do
    -    with FHighlights[i] do
    -    begin
    -      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
    -        //continue;
    -      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
    -      begin
    -        ANextLog := FHighlights[i].X;
    +  for i := 0 to length(FFoldColorInfos)-1  do
    +    with FFoldColorInfos[i] do begin
    +      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
    +        ANextLog := FFoldColorInfos[i].X;
             break;
           end;
         end;
     end;
     
    +function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
    +var
    +  l: String;
    +  p: Integer;
    +begin
    +  l := SynEdit.Lines[index];
    +  p := 1;
    +  while not (l[p] in [#13, #10, #0])
    +  and (l[p] = #32) do inc(p);
    +  if p > 255 then p := 255;
    +  Result := p;
    +end;
    +
     procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
     var
    -  i,lvl,z : integer; //iterate parents fold
    +  i,lvl,z: integer;
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    +  var
    +    p, s, l: integer;
       begin
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    // get column of first character in row
    +    s := Length(FFirstCharacterColumn);
    +    if (s <= ANode.LineIndex)
    +    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
    +      p := FirstCharacterColumn[ANode.LineIndex];
    +      if s > ANode.LineIndex then begin
    +        FFirstCharacterColumn[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    s := Length(FEndLine);
    +    if (s <= ANode.LineIndex)
    +    or (FEndLine[ANode.LineIndex] = 0) then begin
    +      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
    +      if s > ANode.LineIndex then begin
    +        FEndLine[ANode.LineIndex] := l;
    +      end else begin
    +        DebugLn('!!! FEndLine-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z+1);
    +    with FFoldColorInfos[z] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    -      Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      if s <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumn[ANode.LineIndex];
    +      Y  := aRow;
    +      X2 := X + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -273,7 +316,9 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
    +      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
         end;
       end;
     
    @@ -281,60 +326,62 @@
       y, lvlB,lvlA: Integer;
       TmpNode: TSynFoldNodeInfo;
       NestCount : integer;
    -  procedure Later(var J:integer);
    -  begin
    -    inc(J);
    -  end;
    -  function Allowed(J: integer):boolean;
    -  begin
    -    result := J < NestCount;
    -  end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
    +  FHighlighter.CurrentLines := Lines;
     
       lvl := 0;
       i := 0;
    -  while Allowed(i) do
    -  begin
    -
    +  while i < NestCount do begin
         TmpNode := FNestList.HLNode[i];
    -    //find till valid
    -    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
    -    begin
    -      Later(i);
    -      TmpNode := FNestList.HLNode[i];
    -    end;
    +    if (sfaOutline in TmpNode.FoldAction)
    +    and not (sfaInvalid in TmpNode.FoldAction) then
    +      //avoid bug of IncludeOpeningOnLine := False;
    +      if (sfaOpen in TmpNode.FoldAction)
    +      and (TmpNode.LineIndex + 1 = aRow) then begin
    +        {do nothing here}
    +      end else begin
    +        lvlB := lvl;
     
    -    if (sfaOutline in TmpNode.FoldAction ) then
    -    //avoid bug of IncludeOpeningOnLine := False;
    -    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
    -    begin {do nothing here} end
    -    else
    -    begin
    -      lvlB := lvl;
    +        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    +          inc(lvl)
    +        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +          dec(lvl);
    +        //if (FLastNode.LineIndex >= 0)
    +        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +        // inc(lvl);
     
    -      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    -        dec(lvl);
    +        AddVerticalLine(TmpNode);
     
    -      AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -        inc(lvl);
    +        //if (z > 0)
    +        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
    +        //  // if child is on same x-pos keep level
    +        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
    +        //    lvl := FFoldColorInfos[z - 1].Level;
    +        //    FFoldColorInfos[z].Level := lvl;
    +        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        //  end;
    +        //end;
     
    -      lvlA := lvl;
    +        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    +          inc(lvl);
     
    -      with FHighlights[z] do begin
    -        LevelBefore := lvlB;
    -        LevelAfter  := lvlA;
    +        if sfaOpen in TmpNode.FoldAction then
    +          FLastNode := TmpNode;
    +
    +        lvlA := lvl;
    +
    +        with FFoldColorInfos[z] do begin
    +          LevelBefore := lvlB;
    +          LevelAfter  := lvlA;
    +        end;
           end;
    -    end;
    -
    -    Later(i);
    -    //break; //debug
    +    inc(i);
       end;
     end;
     
    @@ -345,33 +392,29 @@
       procedure AddHighlight( ANode: TSynFoldNodeInfo );
       var x,j : integer;
       begin
    -        //don't replace; don't add when already found
         x  := ANode.LogXStart + 1;
    -
         if ANode.LogXStart < ANode.LogXEnd then
    -    for j := 0 to Pred(length(FHighlights)) do
    -      if (FHighlights[j].X = x)
    -      and (FHighlights[j].Border)
    -      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
    -      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
    +    for j := 0 to Pred(length(FFoldColorInfos)) do
    +      if (FFoldColorInfos[j].X = x)
    +      and (FFoldColorInfos[j].Border)
    +      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    +      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
           then begin
    -       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
    -       FHighlights[j].Border := False
    -
    +       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    +       FFoldColorInfos[j].Border := False
           end;
     
    -    //exit; //debug
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z + 1);
    +    with FFoldColorInfos[z] do begin
           Border := False;
           SrcNode:= ANode; //needed by close node
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -378,91 +421,105 @@
       end;
     
     var
    -  y,i,j,lvlB,lvlA : integer;
    -  HL: TSynCustomFoldHighlighter;
    +  LineIdx,i,j,lvlB,lvlA : integer;
       NodeList: TLazSynFoldNodeInfoList;
       TmpNode: TSynFoldNodeInfo;
    -  Found : boolean;
    +  Found: boolean;
     begin
    -  y := aRow -1;
    +  LineIdx := ToIdx(aRow);
     
    -  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -  HL.CurrentLines := Lines;
    -  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
    +  FHighlighter.CurrentLines := Lines;
    +  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
     
    -  NodeList := HL.FoldNodeInfo[y];
    +  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
       NodeList.AddReference;
       try
         NodeList.ActionFilter := [sfaOutline];
    -    //NodeList.FoldFlags:= [sfbIncludeDisabled];
         lvl := 0;
    -    J := Length(FHighlights)-1;
    +    J := Length(FFoldColorInfos) - 1;
         if J >=0 then
    -      lvl := max(0,FHighlights[J].LevelAfter);
    +      lvl := max(0,FFoldColorInfos[J].LevelAfter);
         i := 0;
         repeat
           TmpNode := NodeList[i];
     
    -      //find till valid
    -      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
    -      begin
    -        inc(i);
    -        TmpNode := NodeList[i];
    -      end;
    -      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
    -        if sfaOpen in TmpNode.FoldAction then
    -        begin
    +      if not (sfaInvalid in TmpNode.FoldAction)
    +      and (sfaOutline in TmpNode.FoldAction) then begin
    +        if sfaOpen in TmpNode.FoldAction then begin
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          //if (FLastNode.LineIndex >= 0)
    +          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +          // inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          //if (z > 0)
    +          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
    +          //begin
    +          //  // if child is on same x-pos keep level
    +          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
    +          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
    +          //    lvl := FFoldColorInfos[z - 1].Level;
    +          //    FFoldColorInfos[z].Level := lvl;
    +          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +          //  end;
    +          //end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
                 inc(lvl);
    +
               lvlA := lvl;
     
    -          with FHighlights[z] do begin
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
    +          with FFoldColorInfos[z] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
               end;
    -
    -        end
    -        else
    -        if sfaClose in TmpNode.FoldAction then
    -        begin
    +        end else if sfaClose in TmpNode.FoldAction then begin
               Found := False;
    -          for j := Length(FHighlights)-1 downto 0 do begin
    -            with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +          for j := Length(FFoldColorInfos)-1 downto 0 do begin
    +            with FFoldColorInfos[j].SrcNode do begin
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FFoldColorInfos[j].Level;
    +                lvlA := FFoldColorInfos[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            with FFoldColorInfos[z] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
    +              for j := j to z - 1 do begin
    +                FFoldColorInfos[j] := FFoldColorInfos[j+1];
    +              end;
    +              SetLength(FFoldColorInfos, z);
    +            end;
               end;
    -
    -          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -            //inc(lvl);
             end;
           end;
    -
           inc(i);
         until i >= NodeList.Count;
    -
       finally
         NodeList.ReleaseReference;
       end;
    @@ -469,197 +526,277 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, LastX, j: Integer;
     begin
    -  CurrentY := aRow;
    -  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter) then exit;
    +  FPreparedRow := aRow;
    +  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    -  //DoMarkupFoldAtRow(aRow);
    +  // invalidate fLastNode
    +  FLastNode.LineIndex := -1;
    +
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
    -  //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := length(FFoldColorInfos) - 1 downto 0 do begin
    +    if FFoldColorInfos[i].X > LastX then begin
    +      for j := i to length(FFoldColorInfos) - 2 do begin
    +        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
    +      end;
    +      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
    +    end;
    +    LastX := FFoldColorInfos[i].X;
    +  end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('EndMarkup');
    +  {$ENDIF}
       inherited EndMarkup;
       FNestList.Clear; // for next markup start
     end;
     
    -function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
    -begin
    -  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -end;
    -
     procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
     begin
       if FDefaultGroup = AValue then Exit;
       FDefaultGroup := AValue;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldGroup := FDefaultGroup;
     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;
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +    lLineIdx := ToIdx(pLine);
    +    pNestList.Line := lLineIdx;
    +    lCount := pNestList.Count;
    +    SetLength(pList, lCount);
    +    lAnz := 0;
    +    for i := 0 to lCount - 1 do begin
    +      lNode := pNestList.HLNode[i];
    +      if (sfaInvalid in lNode.FoldAction)
    +      or (
    +        (sfaOpen in lNode.FoldAction)
    +        and (lNode.LineIndex = lLineIdx)
    +      ) then
    +        Continue;
     
    -    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?)
    +      pList[i] := lNode;
    +      inc(lAnz);
         end;
    +    SetLength(pList, lAnz);
    +  end;
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    +var
    +  i, lMinAnz, lEndLine, j, l: integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  {$ENDIF}
     
    -    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
    +  if not Assigned(FHighlighter) then exit;
    +  FHighlighter.CurrentLines := Lines;
    +  if EndLine < 0 then
    +    EndLine := StartLine
    +  else
    +    // endline seems to be the first line after the change
    +    EndLine := EndLine - 1;
    +  lEndLine := EndLine;
     
    -    NodeList := HL.FoldNodeInfo[y];
    -    NodeList.AddReference;
    -    try
    -      NodeList.ActionFilter := [sfaOpen];
    -      i := 0;
    -      repeat
    -        TmpNode := NodeList[i];
    +  SetLength(lStartNestList, 0);
    +  SetLength(lEndNestList, 0);
     
    -        if TmpNode.LogXStart < X-1 then
    -        begin
    -          inc(i);
    -          continue;
    -        end;
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at Start:');
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -        //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;
    +  FillNestList(lEndNestList, EndLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at End:');
    +  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -        inc(i);
    -      until i >= NodeList.Count;
    -
    -    finally
    -      NodeList.ReleaseReference;
    +  // delete all nodes in lEndNodeList which where active at StartLine
    +  // to get the nodes which reach behind EndLine
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
       end;
     
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   Remaining Nodes:');
    +    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex]]);
    +    {$ENDIF}
    +    // does position of first character change for remaining node?
    +    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
    +      // position of first character changed -> find endline
    +      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
    +  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;
    +  // check for changes of endline for node which are active at StartLine
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    if sfaOutline in FoldAction then begin
    +      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
    +      if l <> FEndLine[LineIndex] then begin
    +        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
    +        FEndLine[LineIndex] := l;
    +        {$IFDEF SynEditMarkupFoldColoringDebug}
    +        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +        {$ENDIF}
           end;
         end;
     
    -    if n < 0 then exit;
    +  // invalidate cache
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
    +    FFirstCharacterColumn[i] := 0;
    +    FEndLine[i] := 0;
    +  end;
     
    -    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);
    +  if lEndLine > EndLine then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    {$ENDIF}
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  end;
    +end;
    +
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
    +var
    +  old: TSynEditStrings;
    +begin
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // change:
    +    // remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // change:
    +    if Assigned(AValue) then begin
    +      // set cache size
    +      SetLength(FFirstCharacterColumn, AValue.Count);
    +      SetLength(FEndLine, AValue.Count);
    +      // add Changehandler
    +      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    +      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    end else begin
    +      // clear cache
    +      SetLength(FFirstCharacterColumn, 0);
    +      SetLength(FEndLine, 0);
         end;
    +  end;
    +end;
     
    -  end;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
     var
    -  EndFoldLine,y : integer;
    +  absCount,
    +  idx, i: 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);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  idx := ToIdx(aIndex);
    +  if aCount < 0 then begin
    +    // lines deleted
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
    +      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
    +      FEndLine[i] := FEndLine[i + absCount];
    +    end;
       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}
    +  SetLength(FFirstCharacterColumn, Sender.Count);
    +  SetLength(FEndLine, Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // lines added
    +      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
    +        FEndLine[i + aCount] := FEndLine[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end else begin
    +      // first lines will be inserted
    +      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
    +  aIndex, aCount: Integer);
    +var
    +  newHighlighter: TSynCustomFoldHighlighter;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  if (aIndex <> -1)
    +  or (aCount <> -1) then
    +    exit;
     
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    -end;
    +  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(newHighlighter)
    +  and not (newHighlighter is TSynCustomFoldHighlighter) then
    +    newHighlighter := nil;
     
    +  if (newHighlighter = FHighlighter) then
    +    exit;
     
    +  FHighlighter := newHighlighter;
     
    +  FreeAndNil(FNestList);
    +  if Assigned(FHighlighter) then begin
    +    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +    FNestList.ResetFilter;
    +    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +    FNestList.IncludeOpeningOnLine := True; //False; //
    +  end;
    +end;
    +
     end.
     
    
  • syneditmarkupfoldcoloring.pas_v7.patch (37,607 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 52808)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -50,12 +50,14 @@
     unit SynEditMarkupFoldColoring;
     
     {$mode objfpc}{$H+}
    +{.$define SynEditMarkupFoldColoringDebug}
     
     interface
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText;
     
     type
     
    @@ -66,37 +68,39 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    +    function GetFirstCharacterColumn(index: Integer): Byte;
    +  private
    +    FHighlighter: TSynCustomFoldHighlighter;
         FNestList: TLazSynEditNestedFoldsList;
    +
    +    // cache
    +    FFirstCharacterColumn: Array of Byte;
    +    FEndLine: Array of Integer;
    +
         FDefaultGroup: integer;
    -     // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FFoldColorInfos: TMarkupFoldColorInfos;
         Colors : array of TColor;
    -
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    -
    +    FPreparedRow: integer;
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
    -
    -    function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
    +    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -115,36 +119,21 @@
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +  , SynHighlighterPas
    +{$ENDIF}
    +  ;
     
    -  {%region Sorting FoldInfo -fold}
    -  function CompareFI(Item1, Item2: Pointer): Integer;
    -  begin
    -    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
    -    if result = 0 then
    -        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
    -    if result = 0 then
    -        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
    -          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
    +  while length(Result) < 17 do Result := Result + ' ';
    +end;
    +{$ENDIF}
     
    -    SetLength(result, Length(a));
    -    for i := 0 to Pred(l.Count) do
    -      result[i] := PMarkupFoldColorInfo(l[i])^;
    -     l.Free;
    -  end;
    -  {%endregion}
    -
     { TSynEditMarkupFoldColors }
     
     constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
    @@ -151,23 +140,36 @@
     begin
       inherited Create(ASynEdit);
     
    -  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
    +  if Assigned(Lines) then begin
    +    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    SetLength(FFirstCharacterColumn, Lines.Count);
    +    SetLength(FEndLine, Lines.Count);
    +  end;
    +
    +  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(FHighlighter)
    +  and not (FHighlighter  is TSynCustomFoldHighlighter) then
    +    FHighlighter := nil;
    +
    +  FDefaultGroup := 0;
    +
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
       FNestList.ResetFilter;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    -  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    -  FNestList.IncludeOpeningOnLine := True; //False; //
    +  FNestList.FoldGroup := FDefaultGroup;
    +  FNestList.FoldFlags :=  [sfbIncludeDisabled];
    +  FNestList.IncludeOpeningOnLine := True;
     
       MarkupInfo.Foreground := clGreen;
    -  MarkupInfo.Background := clNone; //clFuchsia;
    +  MarkupInfo.Background := clNone;
       MarkupInfo.Style := [];
       MarkupInfo.StyleMask := [];
    -  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
    +  MarkupInfo.FrameEdges:= sfeLeft;
     
       SetLength(Colors, 5);
       Colors[0] := clRed;
       Colors[1] := $000098F7; //orange
       Colors[2] := $0022CC40; //green
    -  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
       Colors[3] := $00FF682A; //blue
       Colors[4] := $00CF00C4; //purple
     end;
    @@ -174,6 +176,10 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then begin
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
       FreeAndNil(FNestList);
       inherited Destroy;
     end;
    @@ -182,23 +188,27 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i, x2both: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if not Assigned(FHighlighter) then exit;
    +  if (FPreparedRow = aRow) then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
    +    {$ENDIF}
     
    -    x2both := -3; //flag
    -    for i := 0 to length(FHighlights)-1 do
    -      with FHighlights[i] do
    +    x2both := -3;
    +    for i := 0 to length(FFoldColorInfos)-1 do
    +      with FFoldColorInfos[i] do
             if not Ignore
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    -          //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +        and (aStartCol.Logical < X2) then begin
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
    +          {$ENDIF}
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,25 +219,14 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    +          if Border then begin
                 MarkupInfo.FrameColor:= Colors[ColorIdx];
    -            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    +            MarkupInfo.FrameEdges:= sfeLeft;
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
                 MarkupInfo.Foreground := Colors[ColorIdx];
    -
    -          //MarkupInfo.FrameEdges:= sfeAround; //debug
    -
    -          {//2nd debug
    -          if x > x2 then
    -          begin
    -            MarkupInfo.Background:= clYellow;
    -            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
    -            MarkupInfo.FrameColor:= clBlue; //debug
    -          end;}
    -
    -          //break;
    +          end;
             end;
       end;
     end;
    @@ -237,36 +236,80 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter)
    +  or (FPreparedRow <> aRow) then
    +    exit;
    +
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    -  for i := 0 to length(FHighlights)-1  do
    -    with FHighlights[i] do
    -    begin
    -      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
    -        //continue;
    -      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
    -      begin
    -        ANextLog := FHighlights[i].X;
    +  for i := 0 to length(FFoldColorInfos)-1  do
    +    with FFoldColorInfos[i] do begin
    +      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
    +        ANextLog := FFoldColorInfos[i].X;
             break;
           end;
         end;
     end;
     
    +function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
    +var
    +  l: String;
    +  p: Integer;
    +begin
    +  l := SynEdit.Lines[index];
    +  p := 1;
    +  while not (l[p] in [#13, #10, #0])
    +  and (l[p] = #32) do inc(p);
    +  if p > 255 then p := 255;
    +  Result := p;
    +end;
    +
     procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
     var
    -  i,lvl,z : integer; //iterate parents fold
    +  i,lvl,z: integer;
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    +  var
    +    p, s, l: integer;
       begin
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    // get column of first character in row
    +    s := Length(FFirstCharacterColumn);
    +    if (s <= ANode.LineIndex)
    +    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
    +      p := FirstCharacterColumn[ANode.LineIndex];
    +      if s > ANode.LineIndex then begin
    +        FFirstCharacterColumn[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    s := Length(FEndLine);
    +    if (s <= ANode.LineIndex)
    +    or (FEndLine[ANode.LineIndex] = 0) then begin
    +      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
    +      if s > ANode.LineIndex then begin
    +        FEndLine[ANode.LineIndex] := l;
    +      end else begin
    +        DebugLn('!!! FEndLine-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z+1);
    +    with FFoldColorInfos[z] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    -      Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      if s <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumn[ANode.LineIndex];
    +      Y  := aRow;
    +      X2 := X + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -273,7 +316,9 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
    +      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
         end;
       end;
     
    @@ -281,60 +326,62 @@
       y, lvlB,lvlA: Integer;
       TmpNode: TSynFoldNodeInfo;
       NestCount : integer;
    -  procedure Later(var J:integer);
    -  begin
    -    inc(J);
    -  end;
    -  function Allowed(J: integer):boolean;
    -  begin
    -    result := J < NestCount;
    -  end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
    +  FHighlighter.CurrentLines := Lines;
     
       lvl := 0;
       i := 0;
    -  while Allowed(i) do
    -  begin
    -
    +  while i < NestCount do begin
         TmpNode := FNestList.HLNode[i];
    -    //find till valid
    -    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
    -    begin
    -      Later(i);
    -      TmpNode := FNestList.HLNode[i];
    -    end;
    +    if (sfaOutline in TmpNode.FoldAction)
    +    and not (sfaInvalid in TmpNode.FoldAction) then
    +      //avoid bug of IncludeOpeningOnLine := False;
    +      if (sfaOpen in TmpNode.FoldAction)
    +      and (TmpNode.LineIndex + 1 = aRow) then begin
    +        {do nothing here}
    +      end else begin
    +        lvlB := lvl;
     
    -    if (sfaOutline in TmpNode.FoldAction ) then
    -    //avoid bug of IncludeOpeningOnLine := False;
    -    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
    -    begin {do nothing here} end
    -    else
    -    begin
    -      lvlB := lvl;
    +        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    +          inc(lvl)
    +        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +          dec(lvl);
    +        //if (FLastNode.LineIndex >= 0)
    +        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +        // inc(lvl);
     
    -      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    -        dec(lvl);
    +        AddVerticalLine(TmpNode);
     
    -      AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -        inc(lvl);
    +        //if (z > 0)
    +        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
    +        //  // if child is on same x-pos keep level
    +        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
    +        //    lvl := FFoldColorInfos[z - 1].Level;
    +        //    FFoldColorInfos[z].Level := lvl;
    +        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        //  end;
    +        //end;
     
    -      lvlA := lvl;
    +        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    +          inc(lvl);
     
    -      with FHighlights[z] do begin
    -        LevelBefore := lvlB;
    -        LevelAfter  := lvlA;
    +        if sfaOpen in TmpNode.FoldAction then
    +          FLastNode := TmpNode;
    +
    +        lvlA := lvl;
    +
    +        with FFoldColorInfos[z] do begin
    +          LevelBefore := lvlB;
    +          LevelAfter  := lvlA;
    +        end;
           end;
    -    end;
    -
    -    Later(i);
    -    //break; //debug
    +    inc(i);
       end;
     end;
     
    @@ -345,33 +392,29 @@
       procedure AddHighlight( ANode: TSynFoldNodeInfo );
       var x,j : integer;
       begin
    -        //don't replace; don't add when already found
         x  := ANode.LogXStart + 1;
    -
         if ANode.LogXStart < ANode.LogXEnd then
    -    for j := 0 to Pred(length(FHighlights)) do
    -      if (FHighlights[j].X = x)
    -      and (FHighlights[j].Border)
    -      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
    -      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
    +    for j := 0 to Pred(length(FFoldColorInfos)) do
    +      if (FFoldColorInfos[j].X = x)
    +      and (FFoldColorInfos[j].Border)
    +      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    +      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
           then begin
    -       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
    -       FHighlights[j].Border := False
    -
    +       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    +       FFoldColorInfos[j].Border := False
           end;
     
    -    //exit; //debug
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z + 1);
    +    with FFoldColorInfos[z] do begin
           Border := False;
           SrcNode:= ANode; //needed by close node
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -378,91 +421,105 @@
       end;
     
     var
    -  y,i,j,lvlB,lvlA : integer;
    -  HL: TSynCustomFoldHighlighter;
    +  LineIdx,i,j,lvlB,lvlA : integer;
       NodeList: TLazSynFoldNodeInfoList;
       TmpNode: TSynFoldNodeInfo;
    -  Found : boolean;
    +  Found: boolean;
     begin
    -  y := aRow -1;
    +  LineIdx := ToIdx(aRow);
     
    -  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -  HL.CurrentLines := Lines;
    -  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
    +  FHighlighter.CurrentLines := Lines;
    +  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
     
    -  NodeList := HL.FoldNodeInfo[y];
    +  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
       NodeList.AddReference;
       try
         NodeList.ActionFilter := [sfaOutline];
    -    //NodeList.FoldFlags:= [sfbIncludeDisabled];
         lvl := 0;
    -    J := Length(FHighlights)-1;
    +    J := Length(FFoldColorInfos) - 1;
         if J >=0 then
    -      lvl := max(0,FHighlights[J].LevelAfter);
    +      lvl := max(0,FFoldColorInfos[J].LevelAfter);
         i := 0;
         repeat
           TmpNode := NodeList[i];
     
    -      //find till valid
    -      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
    -      begin
    -        inc(i);
    -        TmpNode := NodeList[i];
    -      end;
    -      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
    -        if sfaOpen in TmpNode.FoldAction then
    -        begin
    +      if not (sfaInvalid in TmpNode.FoldAction)
    +      and (sfaOutline in TmpNode.FoldAction) then begin
    +        if sfaOpen in TmpNode.FoldAction then begin
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          //if (FLastNode.LineIndex >= 0)
    +          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +          // inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          //if (z > 0)
    +          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
    +          //begin
    +          //  // if child is on same x-pos keep level
    +          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
    +          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
    +          //    lvl := FFoldColorInfos[z - 1].Level;
    +          //    FFoldColorInfos[z].Level := lvl;
    +          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +          //  end;
    +          //end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
                 inc(lvl);
    +
               lvlA := lvl;
     
    -          with FHighlights[z] do begin
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
    +          with FFoldColorInfos[z] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
               end;
    -
    -        end
    -        else
    -        if sfaClose in TmpNode.FoldAction then
    -        begin
    +        end else if sfaClose in TmpNode.FoldAction then begin
               Found := False;
    -          for j := Length(FHighlights)-1 downto 0 do begin
    -            with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +          for j := Length(FFoldColorInfos)-1 downto 0 do begin
    +            with FFoldColorInfos[j].SrcNode do begin
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FFoldColorInfos[j].Level;
    +                lvlA := FFoldColorInfos[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            with FFoldColorInfos[z] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
    +              for j := j to z - 1 do begin
    +                FFoldColorInfos[j] := FFoldColorInfos[j+1];
    +              end;
    +              SetLength(FFoldColorInfos, z);
    +            end;
               end;
    -
    -          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -            //inc(lvl);
             end;
           end;
    -
           inc(i);
         until i >= NodeList.Count;
    -
       finally
         NodeList.ReleaseReference;
       end;
    @@ -469,197 +526,277 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, LastX, j: Integer;
     begin
    -  CurrentY := aRow;
    -  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter) then exit;
    +  FPreparedRow := aRow;
    +  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    -  //DoMarkupFoldAtRow(aRow);
    +  // invalidate fLastNode
    +  FLastNode.LineIndex := -1;
    +
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
    -  //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := length(FFoldColorInfos) - 1 downto 0 do begin
    +    if FFoldColorInfos[i].X > LastX then begin
    +      for j := i to length(FFoldColorInfos) - 2 do begin
    +        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
    +      end;
    +      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
    +    end;
    +    LastX := FFoldColorInfos[i].X;
    +  end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('EndMarkup');
    +  {$ENDIF}
       inherited EndMarkup;
       FNestList.Clear; // for next markup start
     end;
     
    -function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
    -begin
    -  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -end;
    -
     procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
     begin
       if FDefaultGroup = AValue then Exit;
       FDefaultGroup := AValue;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldGroup := FDefaultGroup;
     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;
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +    lLineIdx := ToIdx(pLine);
    +    pNestList.Line := lLineIdx;
    +    lCount := pNestList.Count;
    +    SetLength(pList, lCount);
    +    lAnz := 0;
    +    for i := 0 to lCount - 1 do begin
    +      lNode := pNestList.HLNode[i];
    +      if (sfaInvalid in lNode.FoldAction)
    +      or (
    +        (sfaOpen in lNode.FoldAction)
    +        and (lNode.LineIndex = lLineIdx)
    +      ) then
    +        Continue;
     
    -    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?)
    +      pList[i] := lNode;
    +      inc(lAnz);
         end;
    +    SetLength(pList, lAnz);
    +  end;
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    +var
    +  i, lMinAnz, lEndLine, j, l: integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  {$ENDIF}
     
    -    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
    +  if not Assigned(FHighlighter) then exit;
    +  FHighlighter.CurrentLines := Lines;
    +  if EndLine < 0 then
    +    EndLine := StartLine
    +  else
    +    // endline seems to be the first line after the change
    +    EndLine := EndLine - 1;
    +  lEndLine := EndLine;
     
    -    NodeList := HL.FoldNodeInfo[y];
    -    NodeList.AddReference;
    -    try
    -      NodeList.ActionFilter := [sfaOpen];
    -      i := 0;
    -      repeat
    -        TmpNode := NodeList[i];
    +  SetLength(lStartNestList, 0);
    +  SetLength(lEndNestList, 0);
     
    -        if TmpNode.LogXStart < X-1 then
    -        begin
    -          inc(i);
    -          continue;
    -        end;
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at Start:');
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -        //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;
    +  FillNestList(lEndNestList, EndLine + 1, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at End:');
    +  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -        inc(i);
    -      until i >= NodeList.Count;
    -
    -    finally
    -      NodeList.ReleaseReference;
    +  // delete all nodes in lEndNodeList which where active at StartLine
    +  // to get the nodes which reach behind EndLine
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
       end;
     
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   Remaining Nodes:');
    +    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex]]);
    +    {$ENDIF}
    +    // does position of first character change for remaining node?
    +    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
    +      // position of first character changed -> find endline
    +      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
    +  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;
    +  // check for changes of endline for node which are active at StartLine
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    if sfaOutline in FoldAction then begin
    +      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
    +      if l <> FEndLine[LineIndex] then begin
    +        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
    +        FEndLine[LineIndex] := l;
    +        {$IFDEF SynEditMarkupFoldColoringDebug}
    +        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +        {$ENDIF}
           end;
         end;
     
    -    if n < 0 then exit;
    +  // invalidate cache
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
    +    FFirstCharacterColumn[i] := 0;
    +    FEndLine[i] := 0;
    +  end;
     
    -    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);
    +  if lEndLine > EndLine then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    {$ENDIF}
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  end;
    +end;
    +
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
    +var
    +  old: TSynEditStrings;
    +begin
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // change:
    +    // remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // change:
    +    if Assigned(AValue) then begin
    +      // set cache size
    +      SetLength(FFirstCharacterColumn, AValue.Count);
    +      SetLength(FEndLine, AValue.Count);
    +      // add Changehandler
    +      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    +      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    end else begin
    +      // clear cache
    +      SetLength(FFirstCharacterColumn, 0);
    +      SetLength(FEndLine, 0);
         end;
    +  end;
    +end;
     
    -  end;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
     var
    -  EndFoldLine,y : integer;
    +  absCount,
    +  idx, i: 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);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  idx := ToIdx(aIndex);
    +  if aCount < 0 then begin
    +    // lines deleted
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
    +      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
    +      FEndLine[i] := FEndLine[i + absCount];
    +    end;
       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}
    +  SetLength(FFirstCharacterColumn, Sender.Count);
    +  SetLength(FEndLine, Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // lines added
    +      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
    +        FEndLine[i + aCount] := FEndLine[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end else begin
    +      // first lines will be inserted
    +      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
    +  aIndex, aCount: Integer);
    +var
    +  newHighlighter: TSynCustomFoldHighlighter;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  if (aIndex <> -1)
    +  or (aCount <> -1) then
    +    exit;
     
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    -end;
    +  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(newHighlighter)
    +  and not (newHighlighter is TSynCustomFoldHighlighter) then
    +    newHighlighter := nil;
     
    +  if (newHighlighter = FHighlighter) then
    +    exit;
     
    +  FHighlighter := newHighlighter;
     
    +  FreeAndNil(FNestList);
    +  if Assigned(FHighlighter) then begin
    +    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +    FNestList.ResetFilter;
    +    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +    FNestList.IncludeOpeningOnLine := True; //False; //
    +  end;
    +end;
    +
     end.
     
    
  • syneditmarkupfoldcoloring.pas_v8.patch (38,072 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 52808)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -50,12 +50,14 @@
     unit SynEditMarkupFoldColoring;
     
     {$mode objfpc}{$H+}
    +{ $define SynEditMarkupFoldColoringDebug}
     
     interface
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText;
     
     type
     
    @@ -66,37 +68,39 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    +    function GetFirstCharacterColumn(index: Integer): Byte;
    +  private
    +    FHighlighter: TSynCustomFoldHighlighter;
         FNestList: TLazSynEditNestedFoldsList;
    +
    +    // cache
    +    FFirstCharacterColumn: Array of Byte;
    +    FEndLine: Array of Integer;
    +
         FDefaultGroup: integer;
    -     // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FFoldColorInfos: TMarkupFoldColorInfos;
         Colors : array of TColor;
    -
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    -
    +    FPreparedRow: integer;
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
    -
    -    function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
    +    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -115,36 +119,21 @@
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +  , SynHighlighterPas
    +{$ENDIF}
    +  ;
     
    -  {%region Sorting FoldInfo -fold}
    -  function CompareFI(Item1, Item2: Pointer): Integer;
    -  begin
    -    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
    -    if result = 0 then
    -        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
    -    if result = 0 then
    -        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
    -          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
    +  while length(Result) < 17 do Result := Result + ' ';
    +end;
    +{$ENDIF}
     
    -    SetLength(result, Length(a));
    -    for i := 0 to Pred(l.Count) do
    -      result[i] := PMarkupFoldColorInfo(l[i])^;
    -     l.Free;
    -  end;
    -  {%endregion}
    -
     { TSynEditMarkupFoldColors }
     
     constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
    @@ -151,23 +140,36 @@
     begin
       inherited Create(ASynEdit);
     
    -  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
    +  if Assigned(Lines) then begin
    +    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    SetLength(FFirstCharacterColumn, Lines.Count);
    +    SetLength(FEndLine, Lines.Count);
    +  end;
    +
    +  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(FHighlighter)
    +  and not (FHighlighter  is TSynCustomFoldHighlighter) then
    +    FHighlighter := nil;
    +
    +  FDefaultGroup := 0;
    +
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
       FNestList.ResetFilter;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    -  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    -  FNestList.IncludeOpeningOnLine := True; //False; //
    +  FNestList.FoldGroup := FDefaultGroup;
    +  FNestList.FoldFlags :=  [sfbIncludeDisabled];
    +  FNestList.IncludeOpeningOnLine := True;
     
       MarkupInfo.Foreground := clGreen;
    -  MarkupInfo.Background := clNone; //clFuchsia;
    +  MarkupInfo.Background := clNone;
       MarkupInfo.Style := [];
       MarkupInfo.StyleMask := [];
    -  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
    +  MarkupInfo.FrameEdges:= sfeLeft;
     
       SetLength(Colors, 5);
       Colors[0] := clRed;
       Colors[1] := $000098F7; //orange
       Colors[2] := $0022CC40; //green
    -  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
       Colors[3] := $00FF682A; //blue
       Colors[4] := $00CF00C4; //purple
     end;
    @@ -174,6 +176,10 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then begin
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
       FreeAndNil(FNestList);
       inherited Destroy;
     end;
    @@ -182,23 +188,27 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i, x2both: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if not Assigned(FHighlighter) then exit;
    +  if (FPreparedRow = aRow) then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
    +    {$ENDIF}
     
    -    x2both := -3; //flag
    -    for i := 0 to length(FHighlights)-1 do
    -      with FHighlights[i] do
    +    x2both := -3;
    +    for i := 0 to length(FFoldColorInfos)-1 do
    +      with FFoldColorInfos[i] do
             if not Ignore
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    -          //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +        and (aStartCol.Logical < X2) then begin
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
    +          {$ENDIF}
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,25 +219,14 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    +          if Border then begin
                 MarkupInfo.FrameColor:= Colors[ColorIdx];
    -            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    +            MarkupInfo.FrameEdges:= sfeLeft;
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
                 MarkupInfo.Foreground := Colors[ColorIdx];
    -
    -          //MarkupInfo.FrameEdges:= sfeAround; //debug
    -
    -          {//2nd debug
    -          if x > x2 then
    -          begin
    -            MarkupInfo.Background:= clYellow;
    -            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
    -            MarkupInfo.FrameColor:= clBlue; //debug
    -          end;}
    -
    -          //break;
    +          end;
             end;
       end;
     end;
    @@ -237,36 +236,80 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter)
    +  or (FPreparedRow <> aRow) then
    +    exit;
    +
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    -  for i := 0 to length(FHighlights)-1  do
    -    with FHighlights[i] do
    -    begin
    -      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
    -        //continue;
    -      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
    -      begin
    -        ANextLog := FHighlights[i].X;
    +  for i := 0 to length(FFoldColorInfos)-1  do
    +    with FFoldColorInfos[i] do begin
    +      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
    +        ANextLog := FFoldColorInfos[i].X;
             break;
           end;
         end;
     end;
     
    +function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
    +var
    +  l: String;
    +  p: Integer;
    +begin
    +  l := SynEdit.Lines[index];
    +  p := 1;
    +  while not (l[p] in [#13, #10, #0])
    +  and (l[p] = #32) do inc(p);
    +  if p > 255 then p := 255;
    +  Result := p;
    +end;
    +
     procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
     var
    -  i,lvl,z : integer; //iterate parents fold
    +  i,lvl,z: integer;
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    +  var
    +    p, s, l: integer;
       begin
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    // get column of first character in row
    +    s := Length(FFirstCharacterColumn);
    +    if (s <= ANode.LineIndex)
    +    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
    +      p := FirstCharacterColumn[ANode.LineIndex];
    +      if s > ANode.LineIndex then begin
    +        FFirstCharacterColumn[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    s := Length(FEndLine);
    +    if (s <= ANode.LineIndex)
    +    or (FEndLine[ANode.LineIndex] = 0) then begin
    +      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
    +      if s > ANode.LineIndex then begin
    +        FEndLine[ANode.LineIndex] := l;
    +      end else begin
    +        DebugLn('!!! FEndLine-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z+1);
    +    with FFoldColorInfos[z] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    -      Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      if s <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumn[ANode.LineIndex];
    +      Y  := aRow;
    +      X2 := X + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -273,7 +316,9 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
    +      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
         end;
       end;
     
    @@ -281,60 +326,62 @@
       y, lvlB,lvlA: Integer;
       TmpNode: TSynFoldNodeInfo;
       NestCount : integer;
    -  procedure Later(var J:integer);
    -  begin
    -    inc(J);
    -  end;
    -  function Allowed(J: integer):boolean;
    -  begin
    -    result := J < NestCount;
    -  end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
    +  FHighlighter.CurrentLines := Lines;
     
       lvl := 0;
       i := 0;
    -  while Allowed(i) do
    -  begin
    -
    +  while i < NestCount do begin
         TmpNode := FNestList.HLNode[i];
    -    //find till valid
    -    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
    -    begin
    -      Later(i);
    -      TmpNode := FNestList.HLNode[i];
    -    end;
    +    if (sfaOutline in TmpNode.FoldAction)
    +    and not (sfaInvalid in TmpNode.FoldAction) then
    +      //avoid bug of IncludeOpeningOnLine := False;
    +      if (sfaOpen in TmpNode.FoldAction)
    +      and (TmpNode.LineIndex + 1 = aRow) then begin
    +        {do nothing here}
    +      end else begin
    +        lvlB := lvl;
     
    -    if (sfaOutline in TmpNode.FoldAction ) then
    -    //avoid bug of IncludeOpeningOnLine := False;
    -    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
    -    begin {do nothing here} end
    -    else
    -    begin
    -      lvlB := lvl;
    +        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    +          inc(lvl)
    +        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +          dec(lvl);
    +        //if (FLastNode.LineIndex >= 0)
    +        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +        // inc(lvl);
     
    -      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    -        dec(lvl);
    +        AddVerticalLine(TmpNode);
     
    -      AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -        inc(lvl);
    +        //if (z > 0)
    +        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
    +        //  // if child is on same x-pos keep level
    +        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
    +        //    lvl := FFoldColorInfos[z - 1].Level;
    +        //    FFoldColorInfos[z].Level := lvl;
    +        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        //  end;
    +        //end;
     
    -      lvlA := lvl;
    +        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    +          inc(lvl);
     
    -      with FHighlights[z] do begin
    -        LevelBefore := lvlB;
    -        LevelAfter  := lvlA;
    +        if sfaOpen in TmpNode.FoldAction then
    +          FLastNode := TmpNode;
    +
    +        lvlA := lvl;
    +
    +        with FFoldColorInfos[z] do begin
    +          LevelBefore := lvlB;
    +          LevelAfter  := lvlA;
    +        end;
           end;
    -    end;
    -
    -    Later(i);
    -    //break; //debug
    +    inc(i);
       end;
     end;
     
    @@ -345,33 +392,29 @@
       procedure AddHighlight( ANode: TSynFoldNodeInfo );
       var x,j : integer;
       begin
    -        //don't replace; don't add when already found
         x  := ANode.LogXStart + 1;
    -
         if ANode.LogXStart < ANode.LogXEnd then
    -    for j := 0 to Pred(length(FHighlights)) do
    -      if (FHighlights[j].X = x)
    -      and (FHighlights[j].Border)
    -      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
    -      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
    +    for j := 0 to Pred(length(FFoldColorInfos)) do
    +      if (FFoldColorInfos[j].X = x)
    +      and (FFoldColorInfos[j].Border)
    +      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    +      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
           then begin
    -       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
    -       FHighlights[j].Border := False
    -
    +       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    +       FFoldColorInfos[j].Border := False
           end;
     
    -    //exit; //debug
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z + 1);
    +    with FFoldColorInfos[z] do begin
           Border := False;
           SrcNode:= ANode; //needed by close node
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -378,91 +421,105 @@
       end;
     
     var
    -  y,i,j,lvlB,lvlA : integer;
    -  HL: TSynCustomFoldHighlighter;
    +  LineIdx,i,j,lvlB,lvlA : integer;
       NodeList: TLazSynFoldNodeInfoList;
       TmpNode: TSynFoldNodeInfo;
    -  Found : boolean;
    +  Found: boolean;
     begin
    -  y := aRow -1;
    +  LineIdx := ToIdx(aRow);
     
    -  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -  HL.CurrentLines := Lines;
    -  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
    +  FHighlighter.CurrentLines := Lines;
    +  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
     
    -  NodeList := HL.FoldNodeInfo[y];
    +  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
       NodeList.AddReference;
       try
         NodeList.ActionFilter := [sfaOutline];
    -    //NodeList.FoldFlags:= [sfbIncludeDisabled];
         lvl := 0;
    -    J := Length(FHighlights)-1;
    +    J := Length(FFoldColorInfos) - 1;
         if J >=0 then
    -      lvl := max(0,FHighlights[J].LevelAfter);
    +      lvl := max(0,FFoldColorInfos[J].LevelAfter);
         i := 0;
         repeat
           TmpNode := NodeList[i];
     
    -      //find till valid
    -      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
    -      begin
    -        inc(i);
    -        TmpNode := NodeList[i];
    -      end;
    -      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
    -        if sfaOpen in TmpNode.FoldAction then
    -        begin
    +      if not (sfaInvalid in TmpNode.FoldAction)
    +      and (sfaOutline in TmpNode.FoldAction) then begin
    +        if sfaOpen in TmpNode.FoldAction then begin
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          //if (FLastNode.LineIndex >= 0)
    +          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +          // inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          //if (z > 0)
    +          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
    +          //begin
    +          //  // if child is on same x-pos keep level
    +          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
    +          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
    +          //    lvl := FFoldColorInfos[z - 1].Level;
    +          //    FFoldColorInfos[z].Level := lvl;
    +          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +          //  end;
    +          //end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
                 inc(lvl);
    +
               lvlA := lvl;
     
    -          with FHighlights[z] do begin
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
    +          with FFoldColorInfos[z] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
               end;
    -
    -        end
    -        else
    -        if sfaClose in TmpNode.FoldAction then
    -        begin
    +        end else if sfaClose in TmpNode.FoldAction then begin
               Found := False;
    -          for j := Length(FHighlights)-1 downto 0 do begin
    -            with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +          for j := Length(FFoldColorInfos)-1 downto 0 do begin
    +            with FFoldColorInfos[j].SrcNode do begin
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FFoldColorInfos[j].Level;
    +                lvlA := FFoldColorInfos[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            with FFoldColorInfos[z] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
    +              for j := j to z - 1 do begin
    +                FFoldColorInfos[j] := FFoldColorInfos[j+1];
    +              end;
    +              SetLength(FFoldColorInfos, z);
    +            end;
               end;
    -
    -          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -            //inc(lvl);
             end;
           end;
    -
           inc(i);
         until i >= NodeList.Count;
    -
       finally
         NodeList.ReleaseReference;
       end;
    @@ -469,197 +526,286 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, LastX, j: Integer;
     begin
    -  CurrentY := aRow;
    -  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter) then exit;
    +  FPreparedRow := aRow;
    +  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    -  //DoMarkupFoldAtRow(aRow);
    +  // invalidate fLastNode
    +  FLastNode.LineIndex := -1;
    +
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
    -  //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := length(FFoldColorInfos) - 1 downto 0 do begin
    +    if FFoldColorInfos[i].X > LastX then begin
    +      for j := i to length(FFoldColorInfos) - 2 do begin
    +        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
    +      end;
    +      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
    +    end;
    +    LastX := FFoldColorInfos[i].X;
    +  end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('EndMarkup');
    +  {$ENDIF}
       inherited EndMarkup;
       FNestList.Clear; // for next markup start
     end;
     
    -function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
    -begin
    -  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -end;
    -
     procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
     begin
       if FDefaultGroup = AValue then Exit;
       FDefaultGroup := AValue;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldGroup := FDefaultGroup;
     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;
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +    lLineIdx := ToIdx(pLine);
    +    pNestList.Line := lLineIdx;
    +    lCount := pNestList.Count;
    +    SetLength(pList, lCount);
    +    lAnz := 0;
    +    for i := 0 to lCount - 1 do begin
    +      lNode := pNestList.HLNode[i];
    +      if (sfaInvalid in lNode.FoldAction)
    +      or (
    +        (sfaOpen in lNode.FoldAction)
    +        and (lNode.LineIndex = lLineIdx)
    +      ) then
    +        Continue;
     
    -    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;
    +      pList[i] := lNode;
    +      inc(lAnz);
    +    end;
    +    SetLength(pList, lAnz);
    +  end;
     
    -    begin
    -      Result := SearchLine(YIndex, NIndex);
    -      if not (sfaInvalid in Result.FoldAction) then
    -        exit;
    +var
    +  i, lMinAnz, lEndLine, j, l, lEnd, lEndLine2: integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  {$ENDIF}
     
    -      inc(YIndex);
    -      while (YIndex < LCnt) and
    -            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
    -             > StartNode.NestLvlStart)
    -      do
    -        inc(YIndex);
    -      if YIndex = LCnt then
    -        exit;
    +  if not Assigned(FHighlighter) then exit;
    +  FHighlighter.CurrentLines := Lines;
    +  if EndLine < 0 then
    +    EndLine := StartLine
    +  else
    +    // endline seems to be the first line after the change
    +    EndLine := EndLine - 1;
    +  lEndLine := EndLine;
     
    -      NIndex := -1;
    -      Result := SearchLine(YIndex, NIndex);
    +  SetLength(lStartNestList, 0);
    +  SetLength(lEndNestList, 0);
     
    -      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
    -        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
    -    end;
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at Start:');
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    +  FillNestList(lEndNestList, EndLine + 1, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at End:');
    +  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -    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
    +  // delete all nodes in lEndNodeList which where active at StartLine
    +  // to get the nodes which reach behind EndLine
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
     
    -    NodeList := HL.FoldNodeInfo[y];
    -    NodeList.AddReference;
    -    try
    -      NodeList.ActionFilter := [sfaOpen];
    -      i := 0;
    -      repeat
    -        TmpNode := NodeList[i];
    +      // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
    +      lEnd := FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0);
    +      if lEnd >= 0 then lEndLine2 := ToPos(lEnd);
     
    -        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;
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
       end;
     
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   Remaining Nodes:');
    +    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +    {$ENDIF}
    +    // does position of first character change for remaining node?
    +    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
    +      // position of first character changed -> find endline
    +      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
     
    -  function IsFoldMoved( aRow: Integer ): integer;
    -  var S : string;
    -    i,n : integer;
    -  begin
    -    Result := -1;
    -    n := -1;
    +    // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
    +    if lEndLine = 0 then
    +      lEndLine := lEndLine2;
    +  end;
     
    -    S := Caret.LineText;
    -    for i := 1 to Min(Length(S), Length(FPrevCaretText)) do
    -    begin
    -      if S[i] <> FPrevCaretText[i] then
    -      begin
    -        n := i;
    -        break;
    +  // check for changes of endline for node which are active at StartLine
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    if sfaOutline in FoldAction then begin
    +      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
    +      if l <> FEndLine[LineIndex] then begin
    +        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
    +        FEndLine[LineIndex] := l;
    +        {$IFDEF SynEditMarkupFoldColoringDebug}
    +        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +        {$ENDIF}
           end;
         end;
     
    -    if n < 0 then exit;
    +  // invalidate cache
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
    +    FFirstCharacterColumn[i] := 0;
    +    FEndLine[i] := 0;
    +  end;
     
    -    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);
    +  if lEndLine > EndLine then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    {$ENDIF}
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  end;
    +end;
    +
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
    +var
    +  old: TSynEditStrings;
    +begin
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // change:
    +    // remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // change:
    +    if Assigned(AValue) then begin
    +      // set cache size
    +      SetLength(FFirstCharacterColumn, AValue.Count);
    +      SetLength(FEndLine, AValue.Count);
    +      // add Changehandler
    +      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    +      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    end else begin
    +      // clear cache
    +      SetLength(FFirstCharacterColumn, 0);
    +      SetLength(FEndLine, 0);
         end;
    +  end;
    +end;
     
    -  end;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
     var
    -  EndFoldLine,y : integer;
    +  absCount,
    +  idx, i: 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);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  idx := ToIdx(aIndex);
    +  if aCount < 0 then begin
    +    // lines deleted
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
    +      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
    +      FEndLine[i] := FEndLine[i + absCount];
    +    end;
       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}
    +  SetLength(FFirstCharacterColumn, Sender.Count);
    +  SetLength(FEndLine, Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // lines added
    +      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
    +        FEndLine[i + aCount] := FEndLine[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end else begin
    +      // first lines will be inserted
    +      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
    +  aIndex, aCount: Integer);
    +var
    +  newHighlighter: TSynCustomFoldHighlighter;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  if (aIndex <> -1)
    +  or (aCount <> -1) then
    +    exit;
     
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    -end;
    +  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(newHighlighter)
    +  and not (newHighlighter is TSynCustomFoldHighlighter) then
    +    newHighlighter := nil;
     
    +  if (newHighlighter = FHighlighter) then
    +    exit;
     
    +  FHighlighter := newHighlighter;
     
    +  FreeAndNil(FNestList);
    +  if Assigned(FHighlighter) then begin
    +    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +    FNestList.ResetFilter;
    +    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +    FNestList.IncludeOpeningOnLine := True; //False; //
    +  end;
    +end;
    +
     end.
     
    
  • syneditmarkupfoldcoloring.pas_v9.patch (38,461 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 52919)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -50,12 +50,14 @@
     unit SynEditMarkupFoldColoring;
     
     {$mode objfpc}{$H+}
    +{ $define SynEditMarkupFoldColoringDebug}
     
     interface
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText;
     
     type
     
    @@ -66,37 +68,39 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    +    function GetFirstCharacterColumn(index: Integer): Byte;
    +  private
    +    FHighlighter: TSynCustomFoldHighlighter;
         FNestList: TLazSynEditNestedFoldsList;
    +
    +    // cache
    +    FFirstCharacterColumn: Array of Byte;
    +    FEndLine: Array of Integer;
    +
         FDefaultGroup: integer;
    -     // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FFoldColorInfos: TMarkupFoldColorInfos;
         Colors : array of TColor;
    -
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    -
    +    FPreparedRow: integer;
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
    -
    -    function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
    +    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -115,36 +119,21 @@
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +  , SynHighlighterPas
    +{$ENDIF}
    +  ;
     
    -  {%region Sorting FoldInfo -fold}
    -  function CompareFI(Item1, Item2: Pointer): Integer;
    -  begin
    -    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
    -    if result = 0 then
    -        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
    -    if result = 0 then
    -        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
    -          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
    +  while length(Result) < 17 do Result := Result + ' ';
    +end;
    +{$ENDIF}
     
    -    SetLength(result, Length(a));
    -    for i := 0 to Pred(l.Count) do
    -      result[i] := PMarkupFoldColorInfo(l[i])^;
    -     l.Free;
    -  end;
    -  {%endregion}
    -
     { TSynEditMarkupFoldColors }
     
     constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
    @@ -151,23 +140,36 @@
     begin
       inherited Create(ASynEdit);
     
    -  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
    +  if Assigned(Lines) then begin
    +    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    SetLength(FFirstCharacterColumn, Lines.Count);
    +    SetLength(FEndLine, Lines.Count);
    +  end;
    +
    +  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(FHighlighter)
    +  and not (FHighlighter  is TSynCustomFoldHighlighter) then
    +    FHighlighter := nil;
    +
    +  FDefaultGroup := 0;
    +
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
       FNestList.ResetFilter;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    -  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    -  FNestList.IncludeOpeningOnLine := True; //False; //
    +  FNestList.FoldGroup := FDefaultGroup;
    +  FNestList.FoldFlags :=  [sfbIncludeDisabled];
    +  FNestList.IncludeOpeningOnLine := True;
     
       MarkupInfo.Foreground := clGreen;
    -  MarkupInfo.Background := clNone; //clFuchsia;
    +  MarkupInfo.Background := clNone;
       MarkupInfo.Style := [];
       MarkupInfo.StyleMask := [];
    -  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
    +  MarkupInfo.FrameEdges:= sfeLeft;
     
       SetLength(Colors, 5);
       Colors[0] := clRed;
       Colors[1] := $000098F7; //orange
       Colors[2] := $0022CC40; //green
    -  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
       Colors[3] := $00FF682A; //blue
       Colors[4] := $00CF00C4; //purple
     end;
    @@ -174,6 +176,10 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then begin
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
       FreeAndNil(FNestList);
       inherited Destroy;
     end;
    @@ -182,23 +188,27 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i, x2both: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if not Assigned(FHighlighter) then exit;
    +  if (FPreparedRow = aRow) then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
    +    {$ENDIF}
     
    -    x2both := -3; //flag
    -    for i := 0 to length(FHighlights)-1 do
    -      with FHighlights[i] do
    +    x2both := -3;
    +    for i := 0 to length(FFoldColorInfos)-1 do
    +      with FFoldColorInfos[i] do
             if not Ignore
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    -          //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +        and (aStartCol.Logical < X2) then begin
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
    +          {$ENDIF}
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,25 +219,14 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    +          if Border then begin
                 MarkupInfo.FrameColor:= Colors[ColorIdx];
    -            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    +            MarkupInfo.FrameEdges:= sfeLeft;
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
                 MarkupInfo.Foreground := Colors[ColorIdx];
    -
    -          //MarkupInfo.FrameEdges:= sfeAround; //debug
    -
    -          {//2nd debug
    -          if x > x2 then
    -          begin
    -            MarkupInfo.Background:= clYellow;
    -            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
    -            MarkupInfo.FrameColor:= clBlue; //debug
    -          end;}
    -
    -          //break;
    +          end;
             end;
       end;
     end;
    @@ -237,36 +236,80 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter)
    +  or (FPreparedRow <> aRow) then
    +    exit;
    +
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    -  for i := 0 to length(FHighlights)-1  do
    -    with FHighlights[i] do
    -    begin
    -      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
    -        //continue;
    -      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
    -      begin
    -        ANextLog := FHighlights[i].X;
    +  for i := 0 to length(FFoldColorInfos)-1  do
    +    with FFoldColorInfos[i] do begin
    +      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
    +        ANextLog := FFoldColorInfos[i].X;
             break;
           end;
         end;
     end;
     
    +function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
    +var
    +  l: String;
    +  p: Integer;
    +begin
    +  l := SynEdit.Lines[index];
    +  p := 1;
    +  while not (l[p] in [#13, #10, #0])
    +  and (l[p] = #32) do inc(p);
    +  if p > 255 then p := 255;
    +  Result := p;
    +end;
    +
     procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
     var
    -  i,lvl,z : integer; //iterate parents fold
    +  i,lvl,z: integer;
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    +  var
    +    p, s, l: integer;
       begin
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    // get column of first character in row
    +    s := Length(FFirstCharacterColumn);
    +    if (s <= ANode.LineIndex)
    +    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
    +      p := FirstCharacterColumn[ANode.LineIndex];
    +      if s > ANode.LineIndex then begin
    +        FFirstCharacterColumn[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    s := Length(FEndLine);
    +    if (s <= ANode.LineIndex)
    +    or (FEndLine[ANode.LineIndex] = 0) then begin
    +      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
    +      if s > ANode.LineIndex then begin
    +        FEndLine[ANode.LineIndex] := l;
    +      end else begin
    +        DebugLn('!!! FEndLine-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z+1);
    +    with FFoldColorInfos[z] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    -      Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      if s <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumn[ANode.LineIndex];
    +      Y  := aRow;
    +      X2 := X + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -273,7 +316,9 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
    +      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
         end;
       end;
     
    @@ -281,60 +326,62 @@
       y, lvlB,lvlA: Integer;
       TmpNode: TSynFoldNodeInfo;
       NestCount : integer;
    -  procedure Later(var J:integer);
    -  begin
    -    inc(J);
    -  end;
    -  function Allowed(J: integer):boolean;
    -  begin
    -    result := J < NestCount;
    -  end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
    +  FHighlighter.CurrentLines := Lines;
     
       lvl := 0;
       i := 0;
    -  while Allowed(i) do
    -  begin
    -
    +  while i < NestCount do begin
         TmpNode := FNestList.HLNode[i];
    -    //find till valid
    -    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
    -    begin
    -      Later(i);
    -      TmpNode := FNestList.HLNode[i];
    -    end;
    +    if (sfaOutline in TmpNode.FoldAction)
    +    and not (sfaInvalid in TmpNode.FoldAction) then
    +      //avoid bug of IncludeOpeningOnLine := False;
    +      if (sfaOpen in TmpNode.FoldAction)
    +      and (TmpNode.LineIndex + 1 = aRow) then begin
    +        {do nothing here}
    +      end else begin
    +        lvlB := lvl;
     
    -    if (sfaOutline in TmpNode.FoldAction ) then
    -    //avoid bug of IncludeOpeningOnLine := False;
    -    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
    -    begin {do nothing here} end
    -    else
    -    begin
    -      lvlB := lvl;
    +        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    +          inc(lvl)
    +        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +          dec(lvl);
    +        //if (FLastNode.LineIndex >= 0)
    +        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +        // inc(lvl);
     
    -      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    -        dec(lvl);
    +        AddVerticalLine(TmpNode);
     
    -      AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -        inc(lvl);
    +        //if (z > 0)
    +        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
    +        //  // if child is on same x-pos keep level
    +        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
    +        //    lvl := FFoldColorInfos[z - 1].Level;
    +        //    FFoldColorInfos[z].Level := lvl;
    +        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        //  end;
    +        //end;
     
    -      lvlA := lvl;
    +        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    +          inc(lvl);
     
    -      with FHighlights[z] do begin
    -        LevelBefore := lvlB;
    -        LevelAfter  := lvlA;
    +        if sfaOpen in TmpNode.FoldAction then
    +          FLastNode := TmpNode;
    +
    +        lvlA := lvl;
    +
    +        with FFoldColorInfos[z] do begin
    +          LevelBefore := lvlB;
    +          LevelAfter  := lvlA;
    +        end;
           end;
    -    end;
    -
    -    Later(i);
    -    //break; //debug
    +    inc(i);
       end;
     end;
     
    @@ -345,33 +392,29 @@
       procedure AddHighlight( ANode: TSynFoldNodeInfo );
       var x,j : integer;
       begin
    -        //don't replace; don't add when already found
         x  := ANode.LogXStart + 1;
    -
         if ANode.LogXStart < ANode.LogXEnd then
    -    for j := 0 to Pred(length(FHighlights)) do
    -      if (FHighlights[j].X = x)
    -      and (FHighlights[j].Border)
    -      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
    -      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
    +    for j := 0 to Pred(length(FFoldColorInfos)) do
    +      if (FFoldColorInfos[j].X = x)
    +      and (FFoldColorInfos[j].Border)
    +      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    +      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
           then begin
    -       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
    -       FHighlights[j].Border := False
    -
    +       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    +       FFoldColorInfos[j].Border := False
           end;
     
    -    //exit; //debug
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z + 1);
    +    with FFoldColorInfos[z] do begin
           Border := False;
           SrcNode:= ANode; //needed by close node
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -378,91 +421,105 @@
       end;
     
     var
    -  y,i,j,lvlB,lvlA : integer;
    -  HL: TSynCustomFoldHighlighter;
    +  LineIdx,i,j,lvlB,lvlA : integer;
       NodeList: TLazSynFoldNodeInfoList;
       TmpNode: TSynFoldNodeInfo;
    -  Found : boolean;
    +  Found: boolean;
     begin
    -  y := aRow -1;
    +  LineIdx := ToIdx(aRow);
     
    -  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -  HL.CurrentLines := Lines;
    -  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
    +  FHighlighter.CurrentLines := Lines;
    +  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
     
    -  NodeList := HL.FoldNodeInfo[y];
    +  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
       NodeList.AddReference;
       try
         NodeList.ActionFilter := [sfaOutline];
    -    //NodeList.FoldFlags:= [sfbIncludeDisabled];
         lvl := 0;
    -    J := Length(FHighlights)-1;
    +    J := Length(FFoldColorInfos) - 1;
         if J >=0 then
    -      lvl := max(0,FHighlights[J].LevelAfter);
    +      lvl := max(0,FFoldColorInfos[J].LevelAfter);
         i := 0;
         repeat
           TmpNode := NodeList[i];
     
    -      //find till valid
    -      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
    -      begin
    -        inc(i);
    -        TmpNode := NodeList[i];
    -      end;
    -      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
    -        if sfaOpen in TmpNode.FoldAction then
    -        begin
    +      if not (sfaInvalid in TmpNode.FoldAction)
    +      and (sfaOutline in TmpNode.FoldAction) then begin
    +        if sfaOpen in TmpNode.FoldAction then begin
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          //if (FLastNode.LineIndex >= 0)
    +          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +          // inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          //if (z > 0)
    +          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
    +          //begin
    +          //  // if child is on same x-pos keep level
    +          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
    +          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
    +          //    lvl := FFoldColorInfos[z - 1].Level;
    +          //    FFoldColorInfos[z].Level := lvl;
    +          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +          //  end;
    +          //end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
                 inc(lvl);
    +
               lvlA := lvl;
     
    -          with FHighlights[z] do begin
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
    +          with FFoldColorInfos[z] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
               end;
    -
    -        end
    -        else
    -        if sfaClose in TmpNode.FoldAction then
    -        begin
    +        end else if sfaClose in TmpNode.FoldAction then begin
               Found := False;
    -          for j := Length(FHighlights)-1 downto 0 do begin
    -            with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +          for j := Length(FFoldColorInfos)-1 downto 0 do begin
    +            with FFoldColorInfos[j].SrcNode do begin
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FFoldColorInfos[j].Level;
    +                lvlA := FFoldColorInfos[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            with FFoldColorInfos[z] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
    +              for j := j to z - 1 do begin
    +                FFoldColorInfos[j] := FFoldColorInfos[j+1];
    +              end;
    +              SetLength(FFoldColorInfos, z);
    +            end;
               end;
    -
    -          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -            //inc(lvl);
             end;
           end;
    -
           inc(i);
         until i >= NodeList.Count;
    -
       finally
         NodeList.ReleaseReference;
       end;
    @@ -469,197 +526,294 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, LastX, j: Integer;
     begin
    -  CurrentY := aRow;
    -  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter) then exit;
    +  FPreparedRow := aRow;
    +  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    -  //DoMarkupFoldAtRow(aRow);
    +  // invalidate fLastNode
    +  FLastNode.LineIndex := -1;
    +
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
    -  //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := length(FFoldColorInfos) - 1 downto 0 do begin
    +    if FFoldColorInfos[i].X > LastX then begin
    +      for j := i to length(FFoldColorInfos) - 2 do begin
    +        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
    +      end;
    +      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
    +    end;
    +    LastX := FFoldColorInfos[i].X;
    +  end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('EndMarkup');
    +  {$ENDIF}
       inherited EndMarkup;
       FNestList.Clear; // for next markup start
     end;
     
    -function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
    -begin
    -  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -end;
    -
     procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
     begin
       if FDefaultGroup = AValue then Exit;
       FDefaultGroup := AValue;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldGroup := FDefaultGroup;
     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;
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +    lLineIdx := ToIdx(pLine);
    +    pNestList.Line := lLineIdx;
    +    lCount := pNestList.Count;
    +    SetLength(pList, lCount);
    +    lAnz := 0;
    +    for i := 0 to lCount - 1 do begin
    +      lNode := pNestList.HLNode[i];
    +      if (sfaInvalid in lNode.FoldAction)
    +      or (
    +        (sfaOpen in lNode.FoldAction)
    +        and (lNode.LineIndex = lLineIdx)
    +      ) then
    +        Continue;
     
    -    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;
    +      pList[i] := lNode;
    +      inc(lAnz);
    +    end;
    +    SetLength(pList, lAnz);
    +  end;
     
    -    begin
    -      Result := SearchLine(YIndex, NIndex);
    -      if not (sfaInvalid in Result.FoldAction) then
    -        exit;
    +var
    +  i, lMinAnz, lEndLine, j, l, lEnd, lEndLine2: integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  {$ENDIF}
     
    -      inc(YIndex);
    -      while (YIndex < LCnt) and
    -            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
    -             > StartNode.NestLvlStart)
    -      do
    -        inc(YIndex);
    -      if YIndex = LCnt then
    -        exit;
    +  if not Assigned(FHighlighter) then exit;
    +  FHighlighter.CurrentLines := Lines;
    +  if EndLine < 0 then
    +    EndLine := StartLine
    +  else
    +    // endline seems to be the first line after the change
    +    EndLine := EndLine - 1;
    +  lEndLine := EndLine;
     
    -      NIndex := -1;
    -      Result := SearchLine(YIndex, NIndex);
    +  SetLength(lStartNestList, 0);
    +  SetLength(lEndNestList, 0);
     
    -      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
    -        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
    -    end;
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at Start:');
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    +  FillNestList(lEndNestList, EndLine + 1, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at End:');
    +  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -    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
    +  // delete all nodes in lEndNodeList which where active at StartLine
    +  // to get the nodes which reach behind EndLine
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
     
    -    NodeList := HL.FoldNodeInfo[y];
    -    NodeList.AddReference;
    -    try
    -      NodeList.ActionFilter := [sfaOpen];
    -      i := 0;
    -      repeat
    -        TmpNode := NodeList[i];
    +      // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
    +      lEnd := FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0);
    +      if lEnd >= 0 then lEndLine2 := ToPos(lEnd);
     
    -        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;
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
       end;
     
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   Remaining Nodes:');
    +    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +    {$ENDIF}
    +    // does position of first character change for remaining node?
    +    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
    +      // position of first character changed -> find endline
    +      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
     
    -  function IsFoldMoved( aRow: Integer ): integer;
    -  var S : string;
    -    i,n : integer;
    -  begin
    -    Result := -1;
    -    n := -1;
    +    // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
    +    if lEndLine = 0 then
    +      lEndLine := lEndLine2;
    +  end;
     
    -    S := Caret.LineText;
    -    for i := 1 to Min(Length(S), Length(FPrevCaretText)) do
    -    begin
    -      if S[i] <> FPrevCaretText[i] then
    -      begin
    -        n := i;
    -        break;
    +  // check for changes of endline for node which are active at StartLine
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    if sfaOutline in FoldAction then begin
    +      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
    +      if l <> FEndLine[LineIndex] then begin
    +        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
    +        FEndLine[LineIndex] := l;
    +        {$IFDEF SynEditMarkupFoldColoringDebug}
    +        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +        {$ENDIF}
           end;
         end;
     
    -    if n < 0 then exit;
    +  // invalidate cache
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
    +    FFirstCharacterColumn[i] := 0;
    +    FEndLine[i] := 0;
    +  end;
     
    -    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);
    +  if lEndLine > EndLine then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    {$ENDIF}
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  end;
    +end;
    +
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
    +var
    +  old: TSynEditStrings;
    +begin
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // change:
    +    // remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    FreeAndNil(FNestList);
    +  end;
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // change:
    +    if Assigned(AValue) then begin
    +      // set cache size
    +      SetLength(FFirstCharacterColumn, AValue.Count);
    +      SetLength(FEndLine, AValue.Count);
    +      // add Changehandler
    +      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    +      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +      if Assigned(FHighlighter) then begin
    +        FNestList := TLazSynEditNestedFoldsList.Create(AValue, FHighlighter);
    +        FNestList.ResetFilter;
    +        FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +        FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +        FNestList.IncludeOpeningOnLine := True; //False; //
    +      end;
    +    end else begin
    +      // clear cache
    +      SetLength(FFirstCharacterColumn, 0);
    +      SetLength(FEndLine, 0);
         end;
    +  end;
    +end;
     
    -  end;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
     var
    -  EndFoldLine,y : integer;
    +  absCount,
    +  idx, i: 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);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  idx := ToIdx(aIndex);
    +  if aCount < 0 then begin
    +    // lines deleted
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
    +      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
    +      FEndLine[i] := FEndLine[i + absCount];
    +    end;
       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}
    +  SetLength(FFirstCharacterColumn, Sender.Count);
    +  SetLength(FEndLine, Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // lines added
    +      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
    +        FEndLine[i + aCount] := FEndLine[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end else begin
    +      // first lines will be inserted
    +      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
    +  aIndex, aCount: Integer);
    +var
    +  newHighlighter: TSynCustomFoldHighlighter;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  if (aIndex <> -1)
    +  or (aCount <> -1) then
    +    exit;
     
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    -end;
    +  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(newHighlighter)
    +  and not (newHighlighter is TSynCustomFoldHighlighter) then
    +    newHighlighter := nil;
     
    +  if (newHighlighter = FHighlighter) then
    +    exit;
     
    +  FHighlighter := newHighlighter;
     
    +  FreeAndNil(FNestList);
    +  if Assigned(FHighlighter) then begin
    +    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +    FNestList.ResetFilter;
    +    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +    FNestList.IncludeOpeningOnLine := True; //False; //
    +  end;
    +end;
    +
     end.
     
    
  • syneditmarkupfoldcoloring.pas_v10.patch (38,698 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 52956)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -50,12 +50,14 @@
     unit SynEditMarkupFoldColoring;
     
     {$mode objfpc}{$H+}
    +{$ define SynEditMarkupFoldColoringDebug}
     
     interface
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText;
     
     type
     
    @@ -66,37 +68,39 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    +    function GetFirstCharacterColumn(index: Integer): Byte;
    +  private
    +    FHighlighter: TSynCustomFoldHighlighter;
         FNestList: TLazSynEditNestedFoldsList;
    +
    +    // cache
    +    FFirstCharacterColumn: Array of Byte;
    +    FEndLine: Array of Integer;
    +
         FDefaultGroup: integer;
    -     // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FFoldColorInfos: TMarkupFoldColorInfos;
         Colors : array of TColor;
    -
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    -
    +    FPreparedRow: integer;
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
    -
    -    function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
    +    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -115,36 +119,21 @@
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +  , SynHighlighterPas
    +{$ENDIF}
    +  ;
     
    -  {%region Sorting FoldInfo -fold}
    -  function CompareFI(Item1, Item2: Pointer): Integer;
    -  begin
    -    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
    -    if result = 0 then
    -        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
    -    if result = 0 then
    -        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
    -          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
    +  while length(Result) < 17 do Result := Result + ' ';
    +end;
    +{$ENDIF}
     
    -    SetLength(result, Length(a));
    -    for i := 0 to Pred(l.Count) do
    -      result[i] := PMarkupFoldColorInfo(l[i])^;
    -     l.Free;
    -  end;
    -  {%endregion}
    -
     { TSynEditMarkupFoldColors }
     
     constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
    @@ -151,23 +140,36 @@
     begin
       inherited Create(ASynEdit);
     
    -  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
    +  if Assigned(Lines) then begin
    +    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    SetLength(FFirstCharacterColumn, Lines.Count);
    +    SetLength(FEndLine, Lines.Count);
    +  end;
    +
    +  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(FHighlighter)
    +  and not (FHighlighter  is TSynCustomFoldHighlighter) then
    +    FHighlighter := nil;
    +
    +  FDefaultGroup := 0;
    +
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
       FNestList.ResetFilter;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    -  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    -  FNestList.IncludeOpeningOnLine := True; //False; //
    +  FNestList.FoldGroup := FDefaultGroup;
    +  FNestList.FoldFlags :=  [sfbIncludeDisabled];
    +  FNestList.IncludeOpeningOnLine := True;
     
       MarkupInfo.Foreground := clGreen;
    -  MarkupInfo.Background := clNone; //clFuchsia;
    +  MarkupInfo.Background := clNone;
       MarkupInfo.Style := [];
       MarkupInfo.StyleMask := [];
    -  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
    +  MarkupInfo.FrameEdges:= sfeLeft;
     
       SetLength(Colors, 5);
       Colors[0] := clRed;
       Colors[1] := $000098F7; //orange
       Colors[2] := $0022CC40; //green
    -  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
       Colors[3] := $00FF682A; //blue
       Colors[4] := $00CF00C4; //purple
     end;
    @@ -174,6 +176,10 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then begin
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
       FreeAndNil(FNestList);
       inherited Destroy;
     end;
    @@ -182,23 +188,27 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i, x2both: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if not Assigned(FHighlighter) then exit;
    +  if (FPreparedRow = aRow) then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
    +    {$ENDIF}
     
    -    x2both := -3; //flag
    -    for i := 0 to length(FHighlights)-1 do
    -      with FHighlights[i] do
    +    x2both := -3;
    +    for i := 0 to length(FFoldColorInfos)-1 do
    +      with FFoldColorInfos[i] do
             if not Ignore
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    -          //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +        and (aStartCol.Logical < X2) then begin
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
    +          {$ENDIF}
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,25 +219,14 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    +          if Border then begin
                 MarkupInfo.FrameColor:= Colors[ColorIdx];
    -            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    +            MarkupInfo.FrameEdges:= sfeLeft;
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
                 MarkupInfo.Foreground := Colors[ColorIdx];
    -
    -          //MarkupInfo.FrameEdges:= sfeAround; //debug
    -
    -          {//2nd debug
    -          if x > x2 then
    -          begin
    -            MarkupInfo.Background:= clYellow;
    -            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
    -            MarkupInfo.FrameColor:= clBlue; //debug
    -          end;}
    -
    -          //break;
    +          end;
             end;
       end;
     end;
    @@ -237,36 +236,82 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter)
    +  or (FPreparedRow <> aRow) then
    +    exit;
    +
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    -  for i := 0 to length(FHighlights)-1  do
    -    with FHighlights[i] do
    -    begin
    -      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
    -        //continue;
    -      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
    -      begin
    -        ANextLog := FHighlights[i].X;
    +  for i := 0 to length(FFoldColorInfos)-1  do
    +    with FFoldColorInfos[i] do begin
    +      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
    +        ANextLog := FFoldColorInfos[i].X;
             break;
           end;
         end;
     end;
     
    +function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
    +var
    +  l: String;
    +  p: Integer;
    +begin
    +  l := SynEdit.Lines[index];
    +  p := 1;
    +  while not (l[p] in [#13, #10, #0])
    +  and (l[p] = #32) do inc(p);
    +  if p > 255 then p := 255;
    +  Result := p;
    +end;
    +
     procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
     var
    -  i,lvl,z : integer; //iterate parents fold
    +  i,lvl,z: integer;
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    +  var
    +    p, s, l: integer;
       begin
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    // get column of first character in row
    +    s := Length(FFirstCharacterColumn);
    +    if (s <= ANode.LineIndex)
    +    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
    +      p := FirstCharacterColumn[ANode.LineIndex];
    +      if s > ANode.LineIndex then begin
    +        FFirstCharacterColumn[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    //DebugLn('  FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), FFirstCharacterColumn[ANode.LineIndex]]);
    +    s := Length(FEndLine);
    +    if (s <= ANode.LineIndex)
    +    or (FEndLine[ANode.LineIndex] = 0) then begin
    +      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
    +      if s > ANode.LineIndex then begin
    +        FEndLine[ANode.LineIndex] := l;
    +      end else begin
    +        DebugLn('!!! FEndLine-Array too small !!!');
    +      end;
    +      //DebugLn('  Find Endline: %d: %d', [ToPos(ANode.LineIndex), l]);
    +    end;
    +    //DebugLn('  EndLine: %d: %d', [ToPos(ANode.LineIndex), FEndLine[ANode.LineIndex]]);
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z+1);
    +    with FFoldColorInfos[z] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    -      Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      if s <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumn[ANode.LineIndex];
    +      Y  := aRow;
    +      X2 := X + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -273,7 +318,9 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
    +      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
         end;
       end;
     
    @@ -281,60 +328,62 @@
       y, lvlB,lvlA: Integer;
       TmpNode: TSynFoldNodeInfo;
       NestCount : integer;
    -  procedure Later(var J:integer);
    -  begin
    -    inc(J);
    -  end;
    -  function Allowed(J: integer):boolean;
    -  begin
    -    result := J < NestCount;
    -  end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
    +  FHighlighter.CurrentLines := Lines;
     
       lvl := 0;
       i := 0;
    -  while Allowed(i) do
    -  begin
    -
    +  while i < NestCount do begin
         TmpNode := FNestList.HLNode[i];
    -    //find till valid
    -    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
    -    begin
    -      Later(i);
    -      TmpNode := FNestList.HLNode[i];
    -    end;
    +    if (sfaOutline in TmpNode.FoldAction)
    +    and not (sfaInvalid in TmpNode.FoldAction) then
    +      //avoid bug of IncludeOpeningOnLine := False;
    +      if (sfaOpen in TmpNode.FoldAction)
    +      and (TmpNode.LineIndex + 1 = aRow) then begin
    +        {do nothing here}
    +      end else begin
    +        lvlB := lvl;
     
    -    if (sfaOutline in TmpNode.FoldAction ) then
    -    //avoid bug of IncludeOpeningOnLine := False;
    -    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
    -    begin {do nothing here} end
    -    else
    -    begin
    -      lvlB := lvl;
    +        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    +          inc(lvl)
    +        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +          dec(lvl);
    +        //if (FLastNode.LineIndex >= 0)
    +        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +        // inc(lvl);
     
    -      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    -        dec(lvl);
    +        AddVerticalLine(TmpNode);
     
    -      AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -        inc(lvl);
    +        //if (z > 0)
    +        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
    +        //  // if child is on same x-pos keep level
    +        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
    +        //    lvl := FFoldColorInfos[z - 1].Level;
    +        //    FFoldColorInfos[z].Level := lvl;
    +        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        //  end;
    +        //end;
     
    -      lvlA := lvl;
    +        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    +          inc(lvl);
     
    -      with FHighlights[z] do begin
    -        LevelBefore := lvlB;
    -        LevelAfter  := lvlA;
    +        if sfaOpen in TmpNode.FoldAction then
    +          FLastNode := TmpNode;
    +
    +        lvlA := lvl;
    +
    +        with FFoldColorInfos[z] do begin
    +          LevelBefore := lvlB;
    +          LevelAfter  := lvlA;
    +        end;
           end;
    -    end;
    -
    -    Later(i);
    -    //break; //debug
    +    inc(i);
       end;
     end;
     
    @@ -345,33 +394,29 @@
       procedure AddHighlight( ANode: TSynFoldNodeInfo );
       var x,j : integer;
       begin
    -        //don't replace; don't add when already found
         x  := ANode.LogXStart + 1;
    -
         if ANode.LogXStart < ANode.LogXEnd then
    -    for j := 0 to Pred(length(FHighlights)) do
    -      if (FHighlights[j].X = x)
    -      and (FHighlights[j].Border)
    -      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
    -      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
    +    for j := 0 to Pred(length(FFoldColorInfos)) do
    +      if (FFoldColorInfos[j].X = x)
    +      and (FFoldColorInfos[j].Border)
    +      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    +      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
           then begin
    -       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
    -       FHighlights[j].Border := False
    -
    +       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    +       FFoldColorInfos[j].Border := False
           end;
     
    -    //exit; //debug
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z + 1);
    +    with FFoldColorInfos[z] do begin
           Border := False;
           SrcNode:= ANode; //needed by close node
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -378,91 +423,105 @@
       end;
     
     var
    -  y,i,j,lvlB,lvlA : integer;
    -  HL: TSynCustomFoldHighlighter;
    +  LineIdx,i,j,lvlB,lvlA : integer;
       NodeList: TLazSynFoldNodeInfoList;
       TmpNode: TSynFoldNodeInfo;
    -  Found : boolean;
    +  Found: boolean;
     begin
    -  y := aRow -1;
    +  LineIdx := ToIdx(aRow);
     
    -  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -  HL.CurrentLines := Lines;
    -  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
    +  FHighlighter.CurrentLines := Lines;
    +  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
     
    -  NodeList := HL.FoldNodeInfo[y];
    +  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
       NodeList.AddReference;
       try
         NodeList.ActionFilter := [sfaOutline];
    -    //NodeList.FoldFlags:= [sfbIncludeDisabled];
         lvl := 0;
    -    J := Length(FHighlights)-1;
    +    J := Length(FFoldColorInfos) - 1;
         if J >=0 then
    -      lvl := max(0,FHighlights[J].LevelAfter);
    +      lvl := max(0,FFoldColorInfos[J].LevelAfter);
         i := 0;
         repeat
           TmpNode := NodeList[i];
     
    -      //find till valid
    -      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
    -      begin
    -        inc(i);
    -        TmpNode := NodeList[i];
    -      end;
    -      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
    -        if sfaOpen in TmpNode.FoldAction then
    -        begin
    +      if not (sfaInvalid in TmpNode.FoldAction)
    +      and (sfaOutline in TmpNode.FoldAction) then begin
    +        if sfaOpen in TmpNode.FoldAction then begin
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          //if (FLastNode.LineIndex >= 0)
    +          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +          // inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          //if (z > 0)
    +          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
    +          //begin
    +          //  // if child is on same x-pos keep level
    +          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
    +          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
    +          //    lvl := FFoldColorInfos[z - 1].Level;
    +          //    FFoldColorInfos[z].Level := lvl;
    +          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +          //  end;
    +          //end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
                 inc(lvl);
    +
               lvlA := lvl;
     
    -          with FHighlights[z] do begin
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
    +          with FFoldColorInfos[z] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
               end;
    -
    -        end
    -        else
    -        if sfaClose in TmpNode.FoldAction then
    -        begin
    +        end else if sfaClose in TmpNode.FoldAction then begin
               Found := False;
    -          for j := Length(FHighlights)-1 downto 0 do begin
    -            with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +          for j := Length(FFoldColorInfos)-1 downto 0 do begin
    +            with FFoldColorInfos[j].SrcNode do begin
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FFoldColorInfos[j].Level;
    +                lvlA := FFoldColorInfos[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            with FFoldColorInfos[z] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
    +              for j := j to z - 1 do begin
    +                FFoldColorInfos[j] := FFoldColorInfos[j+1];
    +              end;
    +              SetLength(FFoldColorInfos, z);
    +            end;
               end;
    -
    -          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -            //inc(lvl);
             end;
           end;
    -
           inc(i);
         until i >= NodeList.Count;
    -
       finally
         NodeList.ReleaseReference;
       end;
    @@ -469,197 +528,295 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, LastX, j: Integer;
     begin
    -  CurrentY := aRow;
    -  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter) then exit;
    +  FPreparedRow := aRow;
    +  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    -  //DoMarkupFoldAtRow(aRow);
    +  // invalidate fLastNode
    +  FLastNode.LineIndex := -1;
    +
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
    -  //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := length(FFoldColorInfos) - 1 downto 0 do begin
    +    if FFoldColorInfos[i].X > LastX then begin
    +      for j := i to length(FFoldColorInfos) - 2 do begin
    +        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
    +      end;
    +      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
    +    end;
    +    LastX := FFoldColorInfos[i].X;
    +  end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('EndMarkup');
    +  {$ENDIF}
       inherited EndMarkup;
    +  if not Assigned(FHighlighter) then exit;
       FNestList.Clear; // for next markup start
     end;
     
    -function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
    -begin
    -  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -end;
    -
     procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
     begin
       if FDefaultGroup = AValue then Exit;
       FDefaultGroup := AValue;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldGroup := FDefaultGroup;
     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;
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +    lLineIdx := ToIdx(pLine);
    +    pNestList.Line := lLineIdx;
    +    lCount := pNestList.Count;
    +    SetLength(pList, lCount);
    +    lAnz := 0;
    +    for i := 0 to lCount - 1 do begin
    +      lNode := pNestList.HLNode[i];
    +      if (sfaInvalid in lNode.FoldAction)
    +      or (
    +        (sfaOpen in lNode.FoldAction)
    +        and (lNode.LineIndex = lLineIdx)
    +      ) then
    +        Continue;
     
    -    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;
    +      pList[i] := lNode;
    +      inc(lAnz);
    +    end;
    +    SetLength(pList, lAnz);
    +  end;
     
    -    begin
    -      Result := SearchLine(YIndex, NIndex);
    -      if not (sfaInvalid in Result.FoldAction) then
    -        exit;
    +var
    +  i, lMinAnz, lEndLine, j, l, lEnd, lEndLine2: integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  {$ENDIF}
     
    -      inc(YIndex);
    -      while (YIndex < LCnt) and
    -            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
    -             > StartNode.NestLvlStart)
    -      do
    -        inc(YIndex);
    -      if YIndex = LCnt then
    -        exit;
    +  if not Assigned(FHighlighter) then exit;
    +  FHighlighter.CurrentLines := Lines;
    +  if EndLine < 0 then
    +    EndLine := StartLine
    +  else
    +    // endline seems to be the first line after the change
    +    EndLine := EndLine - 1;
    +  lEndLine := EndLine;
     
    -      NIndex := -1;
    -      Result := SearchLine(YIndex, NIndex);
    +  SetLength(lStartNestList, 0);
    +  SetLength(lEndNestList, 0);
     
    -      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
    -        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
    -    end;
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at Start:');
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    +  FillNestList(lEndNestList, EndLine + 1, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at End:');
    +  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -    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
    +  // delete all nodes in lEndNodeList which where active at StartLine
    +  // to get the nodes which reach behind EndLine
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
     
    -    NodeList := HL.FoldNodeInfo[y];
    -    NodeList.AddReference;
    -    try
    -      NodeList.ActionFilter := [sfaOpen];
    -      i := 0;
    -      repeat
    -        TmpNode := NodeList[i];
    +      // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
    +      lEnd := FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0);
    +      if lEnd >= 0 then lEndLine2 := ToPos(lEnd);
     
    -        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;
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
       end;
     
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   Remaining Nodes:');
    +    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +    {$ENDIF}
    +    // does position of first character change for remaining node?
    +    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
    +      // position of first character changed -> find endline
    +      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
     
    -  function IsFoldMoved( aRow: Integer ): integer;
    -  var S : string;
    -    i,n : integer;
    -  begin
    -    Result := -1;
    -    n := -1;
    +    // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
    +    if lEndLine = 0 then
    +      lEndLine := lEndLine2;
    +  end;
     
    -    S := Caret.LineText;
    -    for i := 1 to Min(Length(S), Length(FPrevCaretText)) do
    -    begin
    -      if S[i] <> FPrevCaretText[i] then
    -      begin
    -        n := i;
    -        break;
    +  // check for changes of endline for node which are active at StartLine
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    if sfaOutline in FoldAction then begin
    +      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
    +      if l <> FEndLine[LineIndex] then begin
    +        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
    +        FEndLine[LineIndex] := l;
    +        {$IFDEF SynEditMarkupFoldColoringDebug}
    +        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +        {$ENDIF}
           end;
         end;
     
    -    if n < 0 then exit;
    +  // invalidate cache
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
    +    FFirstCharacterColumn[i] := 0;
    +    FEndLine[i] := 0;
    +  end;
     
    -    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);
    +  if lEndLine > EndLine then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    {$ENDIF}
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  end;
    +end;
    +
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
    +var
    +  old: TSynEditStrings;
    +begin
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // change:
    +    // remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    FreeAndNil(FNestList);
    +  end;
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // change:
    +    if Assigned(AValue) then begin
    +      // set cache size
    +      SetLength(FFirstCharacterColumn, AValue.Count);
    +      SetLength(FEndLine, AValue.Count);
    +      // add Changehandler
    +      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    +      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +      if Assigned(FHighlighter) then begin
    +        FNestList := TLazSynEditNestedFoldsList.Create(AValue, FHighlighter);
    +        FNestList.ResetFilter;
    +        FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +        FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +        FNestList.IncludeOpeningOnLine := True; //False; //
    +      end;
    +    end else begin
    +      // clear cache
    +      SetLength(FFirstCharacterColumn, 0);
    +      SetLength(FEndLine, 0);
         end;
    +  end;
    +end;
     
    -  end;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
     var
    -  EndFoldLine,y : integer;
    +  absCount,
    +  idx, i: 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);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  idx := ToIdx(aIndex);
    +  if aCount < 0 then begin
    +    // lines deleted
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
    +      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
    +      FEndLine[i] := FEndLine[i + absCount];
    +    end;
       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}
    +  SetLength(FFirstCharacterColumn, Sender.Count);
    +  SetLength(FEndLine, Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // lines added
    +      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
    +        FEndLine[i + aCount] := FEndLine[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end else begin
    +      // first lines will be inserted
    +      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
    +  aIndex, aCount: Integer);
    +var
    +  newHighlighter: TSynCustomFoldHighlighter;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  if (aIndex <> -1)
    +  or (aCount <> -1) then
    +    exit;
     
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    -end;
    +  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(newHighlighter)
    +  and not (newHighlighter is TSynCustomFoldHighlighter) then
    +    newHighlighter := nil;
     
    +  if (newHighlighter = FHighlighter) then
    +    exit;
     
    +  FHighlighter := newHighlighter;
     
    +  FreeAndNil(FNestList);
    +  if Assigned(FHighlighter) then begin
    +    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +    FNestList.ResetFilter;
    +    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +    FNestList.IncludeOpeningOnLine := True; //False; //
    +  end;
    +end;
    +
     end.
     
    
  • syneditmarkupfoldcoloring.pas_v11.patch (38,997 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 52981)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -50,12 +50,14 @@
     unit SynEditMarkupFoldColoring;
     
     {$mode objfpc}{$H+}
    +{$ define SynEditMarkupFoldColoringDebug}
     
     interface
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText;
     
     type
     
    @@ -66,37 +68,39 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    +    function GetFirstCharacterColumn(index: Integer): Byte;
    +  private
    +    FHighlighter: TSynCustomFoldHighlighter;
         FNestList: TLazSynEditNestedFoldsList;
    +
    +    // cache
    +    FFirstCharacterColumn: Array of Byte;
    +    FEndLine: Array of Integer;
    +
         FDefaultGroup: integer;
    -     // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FFoldColorInfos: TMarkupFoldColorInfos;
         Colors : array of TColor;
    -
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    -
    +    FPreparedRow: integer;
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
    -
    -    function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
    +    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -115,36 +119,21 @@
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +  , SynHighlighterPas
    +{$ENDIF}
    +  ;
     
    -  {%region Sorting FoldInfo -fold}
    -  function CompareFI(Item1, Item2: Pointer): Integer;
    -  begin
    -    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
    -    if result = 0 then
    -        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
    -    if result = 0 then
    -        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
    -          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
    +  while length(Result) < 17 do Result := Result + ' ';
    +end;
    +{$ENDIF}
     
    -    SetLength(result, Length(a));
    -    for i := 0 to Pred(l.Count) do
    -      result[i] := PMarkupFoldColorInfo(l[i])^;
    -     l.Free;
    -  end;
    -  {%endregion}
    -
     { TSynEditMarkupFoldColors }
     
     constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
    @@ -151,23 +140,36 @@
     begin
       inherited Create(ASynEdit);
     
    -  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
    +  if Assigned(Lines) then begin
    +    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    SetLength(FFirstCharacterColumn, Lines.Count);
    +    SetLength(FEndLine, Lines.Count);
    +  end;
    +
    +  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(FHighlighter)
    +  and not (FHighlighter  is TSynCustomFoldHighlighter) then
    +    FHighlighter := nil;
    +
    +  FDefaultGroup := 0;
    +
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
       FNestList.ResetFilter;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    -  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    -  FNestList.IncludeOpeningOnLine := True; //False; //
    +  FNestList.FoldGroup := FDefaultGroup;
    +  FNestList.FoldFlags :=  [sfbIncludeDisabled];
    +  FNestList.IncludeOpeningOnLine := True;
     
       MarkupInfo.Foreground := clGreen;
    -  MarkupInfo.Background := clNone; //clFuchsia;
    +  MarkupInfo.Background := clNone;
       MarkupInfo.Style := [];
       MarkupInfo.StyleMask := [];
    -  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
    +  MarkupInfo.FrameEdges:= sfeLeft;
     
       SetLength(Colors, 5);
       Colors[0] := clRed;
       Colors[1] := $000098F7; //orange
       Colors[2] := $0022CC40; //green
    -  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
       Colors[3] := $00FF682A; //blue
       Colors[4] := $00CF00C4; //purple
     end;
    @@ -174,6 +176,10 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then begin
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
       FreeAndNil(FNestList);
       inherited Destroy;
     end;
    @@ -182,23 +188,27 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i, x2both: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if not Assigned(FHighlighter) then exit;
    +  if (FPreparedRow = aRow) then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
    +    {$ENDIF}
     
    -    x2both := -3; //flag
    -    for i := 0 to length(FHighlights)-1 do
    -      with FHighlights[i] do
    +    x2both := -3;
    +    for i := 0 to length(FFoldColorInfos)-1 do
    +      with FFoldColorInfos[i] do
             if not Ignore
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    -          //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +        and (aStartCol.Logical < X2) then begin
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
    +          {$ENDIF}
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,25 +219,14 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    +          if Border then begin
                 MarkupInfo.FrameColor:= Colors[ColorIdx];
    -            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    +            MarkupInfo.FrameEdges:= sfeLeft;
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
                 MarkupInfo.Foreground := Colors[ColorIdx];
    -
    -          //MarkupInfo.FrameEdges:= sfeAround; //debug
    -
    -          {//2nd debug
    -          if x > x2 then
    -          begin
    -            MarkupInfo.Background:= clYellow;
    -            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
    -            MarkupInfo.FrameColor:= clBlue; //debug
    -          end;}
    -
    -          //break;
    +          end;
             end;
       end;
     end;
    @@ -237,36 +236,82 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter)
    +  or (FPreparedRow <> aRow) then
    +    exit;
    +
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    -  for i := 0 to length(FHighlights)-1  do
    -    with FHighlights[i] do
    -    begin
    -      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
    -        //continue;
    -      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
    -      begin
    -        ANextLog := FHighlights[i].X;
    +  for i := 0 to length(FFoldColorInfos)-1  do
    +    with FFoldColorInfos[i] do begin
    +      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
    +        ANextLog := FFoldColorInfos[i].X;
             break;
           end;
         end;
     end;
     
    +function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
    +var
    +  l: String;
    +  p: Integer;
    +begin
    +  l := SynEdit.Lines[index];
    +  p := 1;
    +  while not (l[p] in [#13, #10, #0])
    +  and (l[p] = #32) do inc(p);
    +  if p > 255 then p := 255;
    +  Result := p;
    +end;
    +
     procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
     var
    -  i,lvl,z : integer; //iterate parents fold
    +  i,lvl,z: integer;
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    +  var
    +    p, s, l: integer;
       begin
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    // get column of first character in row
    +    s := Length(FFirstCharacterColumn);
    +    if (s <= ANode.LineIndex)
    +    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
    +      p := FirstCharacterColumn[ANode.LineIndex];
    +      if s > ANode.LineIndex then begin
    +        FFirstCharacterColumn[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    //DebugLn('  FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), FFirstCharacterColumn[ANode.LineIndex]]);
    +    s := Length(FEndLine);
    +    if (s <= ANode.LineIndex)
    +    or (FEndLine[ANode.LineIndex] = 0) then begin
    +      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
    +      if s > ANode.LineIndex then begin
    +        FEndLine[ANode.LineIndex] := l;
    +      end else begin
    +        DebugLn('!!! FEndLine-Array too small !!!');
    +      end;
    +      //DebugLn('  Find Endline: %d: %d', [ToPos(ANode.LineIndex), l]);
    +    end;
    +    //DebugLn('  EndLine: %d: %d', [ToPos(ANode.LineIndex), FEndLine[ANode.LineIndex]]);
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z+1);
    +    with FFoldColorInfos[z] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    -      Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      if s <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumn[ANode.LineIndex];
    +      Y  := aRow;
    +      X2 := X + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -273,7 +318,9 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
    +      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
         end;
       end;
     
    @@ -281,60 +328,62 @@
       y, lvlB,lvlA: Integer;
       TmpNode: TSynFoldNodeInfo;
       NestCount : integer;
    -  procedure Later(var J:integer);
    -  begin
    -    inc(J);
    -  end;
    -  function Allowed(J: integer):boolean;
    -  begin
    -    result := J < NestCount;
    -  end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
    +  FHighlighter.CurrentLines := Lines;
     
       lvl := 0;
       i := 0;
    -  while Allowed(i) do
    -  begin
    -
    +  while i < NestCount do begin
         TmpNode := FNestList.HLNode[i];
    -    //find till valid
    -    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
    -    begin
    -      Later(i);
    -      TmpNode := FNestList.HLNode[i];
    -    end;
    +    if (sfaOutline in TmpNode.FoldAction)
    +    and not (sfaInvalid in TmpNode.FoldAction) then
    +      //avoid bug of IncludeOpeningOnLine := False;
    +      if (sfaOpen in TmpNode.FoldAction)
    +      and (TmpNode.LineIndex + 1 = aRow) then begin
    +        {do nothing here}
    +      end else begin
    +        lvlB := lvl;
     
    -    if (sfaOutline in TmpNode.FoldAction ) then
    -    //avoid bug of IncludeOpeningOnLine := False;
    -    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
    -    begin {do nothing here} end
    -    else
    -    begin
    -      lvlB := lvl;
    +        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    +          inc(lvl)
    +        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +          dec(lvl);
    +        //if (FLastNode.LineIndex >= 0)
    +        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +        // inc(lvl);
     
    -      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    -        dec(lvl);
    +        AddVerticalLine(TmpNode);
     
    -      AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -        inc(lvl);
    +        //if (z > 0)
    +        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
    +        //  // if child is on same x-pos keep level
    +        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
    +        //    lvl := FFoldColorInfos[z - 1].Level;
    +        //    FFoldColorInfos[z].Level := lvl;
    +        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        //  end;
    +        //end;
     
    -      lvlA := lvl;
    +        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    +          inc(lvl);
     
    -      with FHighlights[z] do begin
    -        LevelBefore := lvlB;
    -        LevelAfter  := lvlA;
    +        if sfaOpen in TmpNode.FoldAction then
    +          FLastNode := TmpNode;
    +
    +        lvlA := lvl;
    +
    +        with FFoldColorInfos[z] do begin
    +          LevelBefore := lvlB;
    +          LevelAfter  := lvlA;
    +        end;
           end;
    -    end;
    -
    -    Later(i);
    -    //break; //debug
    +    inc(i);
       end;
     end;
     
    @@ -345,33 +394,29 @@
       procedure AddHighlight( ANode: TSynFoldNodeInfo );
       var x,j : integer;
       begin
    -        //don't replace; don't add when already found
         x  := ANode.LogXStart + 1;
    -
         if ANode.LogXStart < ANode.LogXEnd then
    -    for j := 0 to Pred(length(FHighlights)) do
    -      if (FHighlights[j].X = x)
    -      and (FHighlights[j].Border)
    -      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
    -      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
    +    for j := 0 to Pred(length(FFoldColorInfos)) do
    +      if (FFoldColorInfos[j].X = x)
    +      and (FFoldColorInfos[j].Border)
    +      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    +      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
           then begin
    -       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
    -       FHighlights[j].Border := False
    -
    +       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    +       FFoldColorInfos[j].Border := False
           end;
     
    -    //exit; //debug
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z + 1);
    +    with FFoldColorInfos[z] do begin
           Border := False;
           SrcNode:= ANode; //needed by close node
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -378,91 +423,105 @@
       end;
     
     var
    -  y,i,j,lvlB,lvlA : integer;
    -  HL: TSynCustomFoldHighlighter;
    +  LineIdx,i,j,lvlB,lvlA : integer;
       NodeList: TLazSynFoldNodeInfoList;
       TmpNode: TSynFoldNodeInfo;
    -  Found : boolean;
    +  Found: boolean;
     begin
    -  y := aRow -1;
    +  LineIdx := ToIdx(aRow);
     
    -  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -  HL.CurrentLines := Lines;
    -  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
    +  FHighlighter.CurrentLines := Lines;
    +  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
     
    -  NodeList := HL.FoldNodeInfo[y];
    +  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
       NodeList.AddReference;
       try
         NodeList.ActionFilter := [sfaOutline];
    -    //NodeList.FoldFlags:= [sfbIncludeDisabled];
         lvl := 0;
    -    J := Length(FHighlights)-1;
    +    J := Length(FFoldColorInfos) - 1;
         if J >=0 then
    -      lvl := max(0,FHighlights[J].LevelAfter);
    +      lvl := max(0,FFoldColorInfos[J].LevelAfter);
         i := 0;
         repeat
           TmpNode := NodeList[i];
     
    -      //find till valid
    -      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
    -      begin
    -        inc(i);
    -        TmpNode := NodeList[i];
    -      end;
    -      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
    -        if sfaOpen in TmpNode.FoldAction then
    -        begin
    +      if not (sfaInvalid in TmpNode.FoldAction)
    +      and (sfaOutline in TmpNode.FoldAction) then begin
    +        if sfaOpen in TmpNode.FoldAction then begin
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          //if (FLastNode.LineIndex >= 0)
    +          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +          // inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          //if (z > 0)
    +          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
    +          //begin
    +          //  // if child is on same x-pos keep level
    +          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
    +          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
    +          //    lvl := FFoldColorInfos[z - 1].Level;
    +          //    FFoldColorInfos[z].Level := lvl;
    +          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +          //  end;
    +          //end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
                 inc(lvl);
    +
               lvlA := lvl;
     
    -          with FHighlights[z] do begin
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
    +          with FFoldColorInfos[z] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
               end;
    -
    -        end
    -        else
    -        if sfaClose in TmpNode.FoldAction then
    -        begin
    +        end else if sfaClose in TmpNode.FoldAction then begin
               Found := False;
    -          for j := Length(FHighlights)-1 downto 0 do begin
    -            with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +          for j := Length(FFoldColorInfos)-1 downto 0 do begin
    +            with FFoldColorInfos[j].SrcNode do begin
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FFoldColorInfos[j].Level;
    +                lvlA := FFoldColorInfos[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            with FFoldColorInfos[z] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
    +              for j := j to z - 1 do begin
    +                FFoldColorInfos[j] := FFoldColorInfos[j+1];
    +              end;
    +              SetLength(FFoldColorInfos, z);
    +            end;
               end;
    -
    -          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -            //inc(lvl);
             end;
           end;
    -
           inc(i);
         until i >= NodeList.Count;
    -
       finally
         NodeList.ReleaseReference;
       end;
    @@ -469,197 +528,301 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, LastX, j: Integer;
     begin
    -  CurrentY := aRow;
    -  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter) then exit;
    +  FPreparedRow := aRow;
    +  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    -  //DoMarkupFoldAtRow(aRow);
    +  // invalidate fLastNode
    +  FLastNode.LineIndex := -1;
    +
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
    -  //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := length(FFoldColorInfos) - 1 downto 0 do begin
    +    if FFoldColorInfos[i].X > LastX then begin
    +      for j := i to length(FFoldColorInfos) - 2 do begin
    +        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
    +      end;
    +      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
    +    end;
    +    LastX := FFoldColorInfos[i].X;
    +  end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('EndMarkup');
    +  {$ENDIF}
       inherited EndMarkup;
    +  if not Assigned(FHighlighter) then exit;
       FNestList.Clear; // for next markup start
     end;
     
    -function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
    -begin
    -  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -end;
    -
     procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
     begin
       if FDefaultGroup = AValue then Exit;
       FDefaultGroup := AValue;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldGroup := FDefaultGroup;
     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;
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +    lLineIdx := ToIdx(pLine);
    +    pNestList.Line := lLineIdx;
    +    lCount := pNestList.Count;
    +    SetLength(pList, lCount);
    +    lAnz := 0;
    +    for i := 0 to lCount - 1 do begin
    +      lNode := pNestList.HLNode[i];
    +      if (sfaInvalid in lNode.FoldAction)
    +      or (
    +        (sfaOpen in lNode.FoldAction)
    +        and (lNode.LineIndex = lLineIdx)
    +      ) then
    +        Continue;
     
    -    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;
    +      pList[i] := lNode;
    +      inc(lAnz);
    +    end;
    +    SetLength(pList, lAnz);
    +  end;
     
    -    begin
    -      Result := SearchLine(YIndex, NIndex);
    -      if not (sfaInvalid in Result.FoldAction) then
    -        exit;
    +var
    +  i, lMinAnz, lEndLine, j, l, lEnd, lEndLine2: integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  {$ENDIF}
     
    -      inc(YIndex);
    -      while (YIndex < LCnt) and
    -            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
    -             > StartNode.NestLvlStart)
    -      do
    -        inc(YIndex);
    -      if YIndex = LCnt then
    -        exit;
    +  if not Assigned(FHighlighter) then exit;
    +  FHighlighter.CurrentLines := Lines;
    +  if EndLine < 0 then
    +    EndLine := StartLine
    +  else
    +    // endline seems to be the first line after the change
    +    EndLine := EndLine - 1;
    +  if StartLine = 0 then begin
    +    // sometimes seems to be called with linenumbers as index instead of linennumbers
    +    inc(StartLine);
    +    inc(EndLine);
    +  end;
    +  lEndLine := EndLine;
     
    -      NIndex := -1;
    -      Result := SearchLine(YIndex, NIndex);
    +  SetLength(lStartNestList, 0);
    +  SetLength(lEndNestList, 0);
     
    -      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
    -        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
    -    end;
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at Start:');
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    +  FillNestList(lEndNestList, EndLine + 1, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at End:');
    +  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -    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
    +  // delete all nodes in lEndNodeList which where active at StartLine
    +  // to get the nodes which reach behind EndLine
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
     
    -    NodeList := HL.FoldNodeInfo[y];
    -    NodeList.AddReference;
    -    try
    -      NodeList.ActionFilter := [sfaOpen];
    -      i := 0;
    -      repeat
    -        TmpNode := NodeList[i];
    +      // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
    +      lEnd := FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0);
    +      if lEnd >= 0 then lEndLine2 := ToPos(lEnd);
     
    -        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;
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
       end;
     
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   Remaining Nodes:');
    +    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +    {$ENDIF}
    +    // does position of first character change for remaining node?
    +    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
    +      // position of first character changed -> find endline
    +      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
     
    -  function IsFoldMoved( aRow: Integer ): integer;
    -  var S : string;
    -    i,n : integer;
    -  begin
    -    Result := -1;
    -    n := -1;
    +    // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
    +    if lEndLine = 0 then
    +      lEndLine := lEndLine2;
    +  end;
     
    -    S := Caret.LineText;
    -    for i := 1 to Min(Length(S), Length(FPrevCaretText)) do
    -    begin
    -      if S[i] <> FPrevCaretText[i] then
    -      begin
    -        n := i;
    -        break;
    +  // check for changes of endline for node which are active at StartLine
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    if sfaOutline in FoldAction then begin
    +      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
    +      if l <> FEndLine[LineIndex] then begin
    +        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
    +        FEndLine[LineIndex] := l;
    +        {$IFDEF SynEditMarkupFoldColoringDebug}
    +        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +        {$ENDIF}
           end;
         end;
     
    -    if n < 0 then exit;
    +  // invalidate cache
    +  //DebugLn('DoTextChange: invalidate cache %d-%d: %d %d ', [StartLine, EndLine, Length(FFirstCharacterColumn), Length(FEndLine)]);
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
    +    FFirstCharacterColumn[i] := 0;
    +    FEndLine[i] := 0;
    +  end;
     
    -    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);
    +  if lEndLine > EndLine then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    {$ENDIF}
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  end;
    +end;
    +
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
    +var
    +  old: TSynEditStrings;
    +begin
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // change:
    +    // remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    FreeAndNil(FNestList);
    +  end;
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // change:
    +    if Assigned(AValue) then begin
    +      // set cache size
    +      SetLength(FFirstCharacterColumn, AValue.Count);
    +      SetLength(FEndLine, AValue.Count);
    +      // add Changehandler
    +      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    +      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +      if Assigned(FHighlighter) then begin
    +        FNestList := TLazSynEditNestedFoldsList.Create(AValue, FHighlighter);
    +        FNestList.ResetFilter;
    +        FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +        FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +        FNestList.IncludeOpeningOnLine := True; //False; //
    +      end;
    +    end else begin
    +      // clear cache
    +      SetLength(FFirstCharacterColumn, 0);
    +      SetLength(FEndLine, 0);
         end;
    +  end;
    +end;
     
    -  end;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
     var
    -  EndFoldLine,y : integer;
    +  absCount,
    +  idx, i: 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);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  idx := ToIdx(aIndex);
    +  if aCount < 0 then begin
    +    // lines deleted
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
    +      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
    +      FEndLine[i] := FEndLine[i + absCount];
    +    end;
       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}
    +  SetLength(FFirstCharacterColumn, Sender.Count);
    +  SetLength(FEndLine, Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // lines added
    +      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
    +        FEndLine[i + aCount] := FEndLine[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end else begin
    +      // first lines will be inserted
    +      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
    +  aIndex, aCount: Integer);
    +var
    +  newHighlighter: TSynCustomFoldHighlighter;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  if (aIndex <> -1)
    +  or (aCount <> -1) then
    +    exit;
     
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    -end;
    +  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(newHighlighter)
    +  and not (newHighlighter is TSynCustomFoldHighlighter) then
    +    newHighlighter := nil;
     
    +  if (newHighlighter = FHighlighter) then
    +    exit;
     
    +  FHighlighter := newHighlighter;
     
    +  FreeAndNil(FNestList);
    +  if Assigned(FHighlighter) then begin
    +    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +    FNestList.ResetFilter;
    +    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +    FNestList.IncludeOpeningOnLine := True; //False; //
    +  end;
    +end;
    +
     end.
     
    
  • syneditmarkupfoldcoloring.pas_v12.patch (39,139 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 52981)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -50,12 +50,14 @@
     unit SynEditMarkupFoldColoring;
     
     {$mode objfpc}{$H+}
    +{ $define SynEditMarkupFoldColoringDebug}
     
     interface
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText;
     
     type
     
    @@ -66,37 +68,39 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    +    function GetFirstCharacterColumn(index: Integer): Byte;
    +  private
    +    FHighlighter: TSynCustomFoldHighlighter;
         FNestList: TLazSynEditNestedFoldsList;
    +
    +    // cache
    +    FFirstCharacterColumn: Array of Byte;
    +    FEndLine: Array of Integer;
    +
         FDefaultGroup: integer;
    -     // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FFoldColorInfos: TMarkupFoldColorInfos;
         Colors : array of TColor;
    -
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    -
    +    FPreparedRow: integer;
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
    -
    -    function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
    +    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -115,36 +119,21 @@
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +  , SynHighlighterPas
    +{$ENDIF}
    +  ;
     
    -  {%region Sorting FoldInfo -fold}
    -  function CompareFI(Item1, Item2: Pointer): Integer;
    -  begin
    -    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
    -    if result = 0 then
    -        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
    -    if result = 0 then
    -        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
    -          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
    +  while length(Result) < 17 do Result := Result + ' ';
    +end;
    +{$ENDIF}
     
    -    SetLength(result, Length(a));
    -    for i := 0 to Pred(l.Count) do
    -      result[i] := PMarkupFoldColorInfo(l[i])^;
    -     l.Free;
    -  end;
    -  {%endregion}
    -
     { TSynEditMarkupFoldColors }
     
     constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
    @@ -151,23 +140,36 @@
     begin
       inherited Create(ASynEdit);
     
    -  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
    +  if Assigned(Lines) then begin
    +    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    SetLength(FFirstCharacterColumn, Lines.Count);
    +    SetLength(FEndLine, Lines.Count);
    +  end;
    +
    +  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(FHighlighter)
    +  and not (FHighlighter  is TSynCustomFoldHighlighter) then
    +    FHighlighter := nil;
    +
    +  FDefaultGroup := 0;
    +
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
       FNestList.ResetFilter;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    -  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    -  FNestList.IncludeOpeningOnLine := True; //False; //
    +  FNestList.FoldGroup := FDefaultGroup;
    +  FNestList.FoldFlags :=  [sfbIncludeDisabled];
    +  FNestList.IncludeOpeningOnLine := True;
     
       MarkupInfo.Foreground := clGreen;
    -  MarkupInfo.Background := clNone; //clFuchsia;
    +  MarkupInfo.Background := clNone;
       MarkupInfo.Style := [];
       MarkupInfo.StyleMask := [];
    -  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
    +  MarkupInfo.FrameEdges:= sfeLeft;
     
       SetLength(Colors, 5);
       Colors[0] := clRed;
       Colors[1] := $000098F7; //orange
       Colors[2] := $0022CC40; //green
    -  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
       Colors[3] := $00FF682A; //blue
       Colors[4] := $00CF00C4; //purple
     end;
    @@ -174,6 +176,10 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then begin
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
       FreeAndNil(FNestList);
       inherited Destroy;
     end;
    @@ -182,23 +188,27 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i, x2both: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if not Assigned(FHighlighter) then exit;
    +  if (FPreparedRow = aRow) then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
    +    {$ENDIF}
     
    -    x2both := -3; //flag
    -    for i := 0 to length(FHighlights)-1 do
    -      with FHighlights[i] do
    +    x2both := -3;
    +    for i := 0 to length(FFoldColorInfos)-1 do
    +      with FFoldColorInfos[i] do
             if not Ignore
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    -          //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +        and (aStartCol.Logical < X2) then begin
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
    +          {$ENDIF}
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,25 +219,14 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    +          if Border then begin
                 MarkupInfo.FrameColor:= Colors[ColorIdx];
    -            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    +            MarkupInfo.FrameEdges:= sfeLeft;
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
                 MarkupInfo.Foreground := Colors[ColorIdx];
    -
    -          //MarkupInfo.FrameEdges:= sfeAround; //debug
    -
    -          {//2nd debug
    -          if x > x2 then
    -          begin
    -            MarkupInfo.Background:= clYellow;
    -            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
    -            MarkupInfo.FrameColor:= clBlue; //debug
    -          end;}
    -
    -          //break;
    +          end;
             end;
       end;
     end;
    @@ -237,36 +236,82 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter)
    +  or (FPreparedRow <> aRow) then
    +    exit;
    +
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    -  for i := 0 to length(FHighlights)-1  do
    -    with FHighlights[i] do
    -    begin
    -      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
    -        //continue;
    -      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
    -      begin
    -        ANextLog := FHighlights[i].X;
    +  for i := 0 to length(FFoldColorInfos)-1  do
    +    with FFoldColorInfos[i] do begin
    +      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
    +        ANextLog := FFoldColorInfos[i].X;
             break;
           end;
         end;
     end;
     
    +function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
    +var
    +  l: String;
    +  p: Integer;
    +begin
    +  l := SynEdit.Lines[index];
    +  p := 1;
    +  while not (l[p] in [#13, #10, #0])
    +  and (l[p] = #32) do inc(p);
    +  if p > 255 then p := 255;
    +  Result := p;
    +end;
    +
     procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
     var
    -  i,lvl,z : integer; //iterate parents fold
    +  i,lvl,z: integer;
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    +  var
    +    p, s, l: integer;
       begin
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    // get column of first character in row
    +    s := Length(FFirstCharacterColumn);
    +    if (s <= ANode.LineIndex)
    +    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
    +      p := FirstCharacterColumn[ANode.LineIndex];
    +      if s > ANode.LineIndex then begin
    +        FFirstCharacterColumn[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    //DebugLn('  FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), FFirstCharacterColumn[ANode.LineIndex]]);
    +    s := Length(FEndLine);
    +    if (s <= ANode.LineIndex)
    +    or (FEndLine[ANode.LineIndex] = 0) then begin
    +      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
    +      if s > ANode.LineIndex then begin
    +        FEndLine[ANode.LineIndex] := l;
    +      end else begin
    +        DebugLn('!!! FEndLine-Array too small !!!');
    +      end;
    +      //DebugLn('  Find Endline: %d: %d', [ToPos(ANode.LineIndex), l]);
    +    end;
    +    //DebugLn('  EndLine: %d: %d', [ToPos(ANode.LineIndex), FEndLine[ANode.LineIndex]]);
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z+1);
    +    with FFoldColorInfos[z] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    -      Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      if s <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumn[ANode.LineIndex];
    +      Y  := aRow;
    +      X2 := X + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -273,7 +318,9 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
    +      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
         end;
       end;
     
    @@ -281,60 +328,62 @@
       y, lvlB,lvlA: Integer;
       TmpNode: TSynFoldNodeInfo;
       NestCount : integer;
    -  procedure Later(var J:integer);
    -  begin
    -    inc(J);
    -  end;
    -  function Allowed(J: integer):boolean;
    -  begin
    -    result := J < NestCount;
    -  end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
    +  FHighlighter.CurrentLines := Lines;
     
       lvl := 0;
       i := 0;
    -  while Allowed(i) do
    -  begin
    -
    +  while i < NestCount do begin
         TmpNode := FNestList.HLNode[i];
    -    //find till valid
    -    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
    -    begin
    -      Later(i);
    -      TmpNode := FNestList.HLNode[i];
    -    end;
    +    if (sfaOutline in TmpNode.FoldAction)
    +    and not (sfaInvalid in TmpNode.FoldAction) then
    +      //avoid bug of IncludeOpeningOnLine := False;
    +      if (sfaOpen in TmpNode.FoldAction)
    +      and (TmpNode.LineIndex + 1 = aRow) then begin
    +        {do nothing here}
    +      end else begin
    +        lvlB := lvl;
     
    -    if (sfaOutline in TmpNode.FoldAction ) then
    -    //avoid bug of IncludeOpeningOnLine := False;
    -    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
    -    begin {do nothing here} end
    -    else
    -    begin
    -      lvlB := lvl;
    +        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    +          inc(lvl)
    +        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +          dec(lvl);
    +        //if (FLastNode.LineIndex >= 0)
    +        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +        // inc(lvl);
     
    -      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    -        dec(lvl);
    +        AddVerticalLine(TmpNode);
     
    -      AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -        inc(lvl);
    +        //if (z > 0)
    +        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
    +        //  // if child is on same x-pos keep level
    +        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
    +        //    lvl := FFoldColorInfos[z - 1].Level;
    +        //    FFoldColorInfos[z].Level := lvl;
    +        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        //  end;
    +        //end;
     
    -      lvlA := lvl;
    +        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    +          inc(lvl);
     
    -      with FHighlights[z] do begin
    -        LevelBefore := lvlB;
    -        LevelAfter  := lvlA;
    +        if sfaOpen in TmpNode.FoldAction then
    +          FLastNode := TmpNode;
    +
    +        lvlA := lvl;
    +
    +        with FFoldColorInfos[z] do begin
    +          LevelBefore := lvlB;
    +          LevelAfter  := lvlA;
    +        end;
           end;
    -    end;
    -
    -    Later(i);
    -    //break; //debug
    +    inc(i);
       end;
     end;
     
    @@ -345,33 +394,29 @@
       procedure AddHighlight( ANode: TSynFoldNodeInfo );
       var x,j : integer;
       begin
    -        //don't replace; don't add when already found
         x  := ANode.LogXStart + 1;
    -
         if ANode.LogXStart < ANode.LogXEnd then
    -    for j := 0 to Pred(length(FHighlights)) do
    -      if (FHighlights[j].X = x)
    -      and (FHighlights[j].Border)
    -      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
    -      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
    +    for j := 0 to Pred(length(FFoldColorInfos)) do
    +      if (FFoldColorInfos[j].X = x)
    +      and (FFoldColorInfos[j].Border)
    +      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    +      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
           then begin
    -       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
    -       FHighlights[j].Border := False
    -
    +       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    +       FFoldColorInfos[j].Border := False
           end;
     
    -    //exit; //debug
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z + 1);
    +    with FFoldColorInfos[z] do begin
           Border := False;
           SrcNode:= ANode; //needed by close node
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -378,91 +423,105 @@
       end;
     
     var
    -  y,i,j,lvlB,lvlA : integer;
    -  HL: TSynCustomFoldHighlighter;
    +  LineIdx,i,j,lvlB,lvlA : integer;
       NodeList: TLazSynFoldNodeInfoList;
       TmpNode: TSynFoldNodeInfo;
    -  Found : boolean;
    +  Found: boolean;
     begin
    -  y := aRow -1;
    +  LineIdx := ToIdx(aRow);
     
    -  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -  HL.CurrentLines := Lines;
    -  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
    +  FHighlighter.CurrentLines := Lines;
    +  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
     
    -  NodeList := HL.FoldNodeInfo[y];
    +  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
       NodeList.AddReference;
       try
         NodeList.ActionFilter := [sfaOutline];
    -    //NodeList.FoldFlags:= [sfbIncludeDisabled];
         lvl := 0;
    -    J := Length(FHighlights)-1;
    +    J := Length(FFoldColorInfos) - 1;
         if J >=0 then
    -      lvl := max(0,FHighlights[J].LevelAfter);
    +      lvl := max(0,FFoldColorInfos[J].LevelAfter);
         i := 0;
         repeat
           TmpNode := NodeList[i];
     
    -      //find till valid
    -      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
    -      begin
    -        inc(i);
    -        TmpNode := NodeList[i];
    -      end;
    -      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
    -        if sfaOpen in TmpNode.FoldAction then
    -        begin
    +      if not (sfaInvalid in TmpNode.FoldAction)
    +      and (sfaOutline in TmpNode.FoldAction) then begin
    +        if sfaOpen in TmpNode.FoldAction then begin
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          //if (FLastNode.LineIndex >= 0)
    +          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +          // inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          //if (z > 0)
    +          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
    +          //begin
    +          //  // if child is on same x-pos keep level
    +          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
    +          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
    +          //    lvl := FFoldColorInfos[z - 1].Level;
    +          //    FFoldColorInfos[z].Level := lvl;
    +          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +          //  end;
    +          //end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
                 inc(lvl);
    +
               lvlA := lvl;
     
    -          with FHighlights[z] do begin
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
    +          with FFoldColorInfos[z] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
               end;
    -
    -        end
    -        else
    -        if sfaClose in TmpNode.FoldAction then
    -        begin
    +        end else if sfaClose in TmpNode.FoldAction then begin
               Found := False;
    -          for j := Length(FHighlights)-1 downto 0 do begin
    -            with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +          for j := Length(FFoldColorInfos)-1 downto 0 do begin
    +            with FFoldColorInfos[j].SrcNode do begin
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FFoldColorInfos[j].Level;
    +                lvlA := FFoldColorInfos[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            with FFoldColorInfos[z] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
    +              for j := j to z - 1 do begin
    +                FFoldColorInfos[j] := FFoldColorInfos[j+1];
    +              end;
    +              SetLength(FFoldColorInfos, z);
    +            end;
               end;
    -
    -          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -            //inc(lvl);
             end;
           end;
    -
           inc(i);
         until i >= NodeList.Count;
    -
       finally
         NodeList.ReleaseReference;
       end;
    @@ -469,197 +528,306 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, LastX, j: Integer;
     begin
    -  CurrentY := aRow;
    -  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter) then exit;
    +  FPreparedRow := aRow;
    +  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    -  //DoMarkupFoldAtRow(aRow);
    +  // invalidate fLastNode
    +  FLastNode.LineIndex := -1;
    +
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
    -  //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := length(FFoldColorInfos) - 1 downto 0 do begin
    +    if FFoldColorInfos[i].X > LastX then begin
    +      for j := i to length(FFoldColorInfos) - 2 do begin
    +        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
    +      end;
    +      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
    +    end;
    +    LastX := FFoldColorInfos[i].X;
    +  end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('EndMarkup');
    +  {$ENDIF}
       inherited EndMarkup;
    +  if not Assigned(FHighlighter) then exit;
       FNestList.Clear; // for next markup start
     end;
     
    -function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
    -begin
    -  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -end;
    -
     procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
     begin
       if FDefaultGroup = AValue then Exit;
       FDefaultGroup := AValue;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldGroup := FDefaultGroup;
     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;
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +    lLineIdx := ToIdx(pLine);
    +    pNestList.Line := lLineIdx;
    +    lCount := pNestList.Count;
    +    SetLength(pList, lCount);
    +    lAnz := 0;
    +    for i := 0 to lCount - 1 do begin
    +      lNode := pNestList.HLNode[i];
    +      if (sfaInvalid in lNode.FoldAction)
    +      or (
    +        (sfaOpen in lNode.FoldAction)
    +        and (lNode.LineIndex = lLineIdx)
    +      ) then
    +        Continue;
     
    -    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;
    +      // hint: endline for node is stored in NodeIndex
    +      lNode.NodeIndex := pNestList.NodeEndLine[i];
    +      pList[i] := lNode;
    +      inc(lAnz);
    +    end;
    +    SetLength(pList, lAnz);
    +  end;
     
    -    begin
    -      Result := SearchLine(YIndex, NIndex);
    -      if not (sfaInvalid in Result.FoldAction) then
    -        exit;
    +var
    +  i, lMinAnz, lEndLine, j, l: Integer;
    +  //lEnd, lEndLine2: integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  {$ENDIF}
     
    -      inc(YIndex);
    -      while (YIndex < LCnt) and
    -            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
    -             > StartNode.NestLvlStart)
    -      do
    -        inc(YIndex);
    -      if YIndex = LCnt then
    -        exit;
    +  if not Assigned(FHighlighter) then exit;
    +  FHighlighter.CurrentLines := Lines;
    +  if EndLine < 0 then
    +    EndLine := StartLine
    +  else
    +    // endline seems to be the first line after the change
    +    EndLine := EndLine - 1;
    +  lEndLine := EndLine;
     
    -      NIndex := -1;
    -      Result := SearchLine(YIndex, NIndex);
    +  SetLength(lStartNestList, 0);
    +  SetLength(lEndNestList, 0);
     
    -      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
    -        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
    -    end;
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at Start:');
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    +  FillNestList(lEndNestList, EndLine + 1, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at End:');
    +  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -    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
    +  // delete all nodes in lEndNodeList which where active at StartLine
    +  // to get the nodes which reach behind EndLine
    +  //lEndLine2 := 0;
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
     
    -    NodeList := HL.FoldNodeInfo[y];
    -    NodeList.AddReference;
    -    try
    -      NodeList.ActionFilter := [sfaOpen];
    -      i := 0;
    -      repeat
    -        TmpNode := NodeList[i];
    +      //// ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
    +      //lEnd := FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0);
    +      //if lEnd >= 0 then
    +      //  lEndLine2 := ToPos(lEnd);
     
    -        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;
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
       end;
     
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   Remaining Nodes:');
    +    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +    {$ENDIF}
    +    // does position of first character change for remaining node?
    +    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
    +      // position of first character changed -> find endline
    +      //lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
    +      // new: Field node index is used to store endline for node see: FillNestList()
    +      lEndLine := ToPos(lEndNestList[0].NodeIndex);
     
    -  function IsFoldMoved( aRow: Integer ): integer;
    -  var S : string;
    -    i,n : integer;
    -  begin
    -    Result := -1;
    -    n := -1;
    +    //// ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
    +    //if lEndLine = 0 then begin
    +    //  {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //  DebugLn('  !!! workaround needed !!!');
    +    //  {$ENDIF}
    +    //  lEndLine := lEndLine2;
    +    //end;
    +  end;
     
    -    S := Caret.LineText;
    -    for i := 1 to Min(Length(S), Length(FPrevCaretText)) do
    -    begin
    -      if S[i] <> FPrevCaretText[i] then
    -      begin
    -        n := i;
    -        break;
    +  // check for changes of endline for node which are active at StartLine
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    if sfaOutline in FoldAction then begin
    +      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
    +      if l <> FEndLine[LineIndex] then begin
    +        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
    +        FEndLine[LineIndex] := l;
    +        {$IFDEF SynEditMarkupFoldColoringDebug}
    +        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +        {$ENDIF}
           end;
         end;
     
    -    if n < 0 then exit;
    +  // invalidate cache
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
    +    FFirstCharacterColumn[i] := 0;
    +    FEndLine[i] := 0;
    +  end;
     
    -    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);
    +  if lEndLine > EndLine then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    {$ENDIF}
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  end;
    +end;
    +
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
    +var
    +  old: TSynEditStrings;
    +begin
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // change:
    +    // remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    FreeAndNil(FNestList);
    +  end;
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // change:
    +    if Assigned(AValue) then begin
    +      // set cache size
    +      SetLength(FFirstCharacterColumn, AValue.Count);
    +      SetLength(FEndLine, AValue.Count);
    +      // add Changehandler
    +      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    +      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +      if Assigned(FHighlighter) then begin
    +        FNestList := TLazSynEditNestedFoldsList.Create(AValue, FHighlighter);
    +        FNestList.ResetFilter;
    +        FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +        FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +        FNestList.IncludeOpeningOnLine := True; //False; //
    +      end;
    +    end else begin
    +      // clear cache
    +      SetLength(FFirstCharacterColumn, 0);
    +      SetLength(FEndLine, 0);
         end;
    +  end;
    +end;
     
    -  end;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
     var
    -  EndFoldLine,y : integer;
    +  absCount,
    +  idx, i: 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);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  idx := ToIdx(aIndex);
    +  if aCount < 0 then begin
    +    // lines deleted
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
    +      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
    +      FEndLine[i] := FEndLine[i + absCount];
    +    end;
       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}
    +  SetLength(FFirstCharacterColumn, Sender.Count);
    +  SetLength(FEndLine, Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // lines added
    +      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
    +        FEndLine[i + aCount] := FEndLine[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end else begin
    +      // first lines will be inserted
    +      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
    +  aIndex, aCount: Integer);
    +var
    +  newHighlighter: TSynCustomFoldHighlighter;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  if (aIndex <> -1)
    +  or (aCount <> -1) then
    +    exit;
     
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    -end;
    +  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(newHighlighter)
    +  and not (newHighlighter is TSynCustomFoldHighlighter) then
    +    newHighlighter := nil;
     
    +  if (newHighlighter = FHighlighter) then
    +    exit;
     
    +  FHighlighter := newHighlighter;
     
    +  FreeAndNil(FNestList);
    +  if Assigned(FHighlighter) then begin
    +    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +    FNestList.ResetFilter;
    +    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +    FNestList.IncludeOpeningOnLine := True; //False; //
    +  end;
    +end;
    +
     end.
     
    
  • syneditmarkupfoldcoloring.pas_v13.patch (39,610 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 52990)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -50,12 +50,14 @@
     unit SynEditMarkupFoldColoring;
     
     {$mode objfpc}{$H+}
    +{ $define SynEditMarkupFoldColoringDebug}
     
     interface
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText;
     
     type
     
    @@ -66,37 +68,40 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    +    function GetFirstCharacterColumn(index: Integer): Byte;
    +  private
    +    FHighlighter: TSynCustomFoldHighlighter;
         FNestList: TLazSynEditNestedFoldsList;
    +
    +    // cache
    +    FFirstCharacterColumn: Array of Byte;
    +    FEndLine: Array of Integer;
    +
         FDefaultGroup: integer;
    -     // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FFoldColorInfos: TMarkupFoldColorInfos;
         Colors : array of TColor;
    -
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    -
    +    FPreparedRow: integer;
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
    -
    -    function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    procedure InitArrays;
    +    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
    +    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -115,36 +120,21 @@
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +  , SynHighlighterPas
    +{$ENDIF}
    +  ;
     
    -  {%region Sorting FoldInfo -fold}
    -  function CompareFI(Item1, Item2: Pointer): Integer;
    -  begin
    -    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
    -    if result = 0 then
    -        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
    -    if result = 0 then
    -        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
    -          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
    +  while length(Result) < 17 do Result := Result + ' ';
    +end;
    +{$ENDIF}
     
    -    SetLength(result, Length(a));
    -    for i := 0 to Pred(l.Count) do
    -      result[i] := PMarkupFoldColorInfo(l[i])^;
    -     l.Free;
    -  end;
    -  {%endregion}
    -
     { TSynEditMarkupFoldColors }
     
     constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
    @@ -151,23 +141,36 @@
     begin
       inherited Create(ASynEdit);
     
    -  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
    +  if Assigned(Lines) then begin
    +    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    SetLength(FFirstCharacterColumn, Lines.Count);
    +    SetLength(FEndLine, Lines.Count);
    +  end;
    +
    +  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(FHighlighter)
    +  and not (FHighlighter  is TSynCustomFoldHighlighter) then
    +    FHighlighter := nil;
    +
    +  FDefaultGroup := 0;
    +
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
       FNestList.ResetFilter;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    -  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    -  FNestList.IncludeOpeningOnLine := True; //False; //
    +  FNestList.FoldGroup := FDefaultGroup;
    +  FNestList.FoldFlags :=  [sfbIncludeDisabled];
    +  FNestList.IncludeOpeningOnLine := True;
     
       MarkupInfo.Foreground := clGreen;
    -  MarkupInfo.Background := clNone; //clFuchsia;
    +  MarkupInfo.Background := clNone;
       MarkupInfo.Style := [];
       MarkupInfo.StyleMask := [];
    -  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
    +  MarkupInfo.FrameEdges:= sfeLeft;
     
       SetLength(Colors, 5);
       Colors[0] := clRed;
       Colors[1] := $000098F7; //orange
       Colors[2] := $0022CC40; //green
    -  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
       Colors[3] := $00FF682A; //blue
       Colors[4] := $00CF00C4; //purple
     end;
    @@ -174,6 +177,10 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then begin
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
       FreeAndNil(FNestList);
       inherited Destroy;
     end;
    @@ -182,23 +189,27 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i, x2both: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if not Assigned(FHighlighter) then exit;
    +  if (FPreparedRow = aRow) then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
    +    {$ENDIF}
     
    -    x2both := -3; //flag
    -    for i := 0 to length(FHighlights)-1 do
    -      with FHighlights[i] do
    +    x2both := -3;
    +    for i := 0 to length(FFoldColorInfos)-1 do
    +      with FFoldColorInfos[i] do
             if not Ignore
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    -          //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +        and (aStartCol.Logical < X2) then begin
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
    +          {$ENDIF}
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,25 +220,14 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    +          if Border then begin
                 MarkupInfo.FrameColor:= Colors[ColorIdx];
    -            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    +            MarkupInfo.FrameEdges:= sfeLeft;
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
                 MarkupInfo.Foreground := Colors[ColorIdx];
    -
    -          //MarkupInfo.FrameEdges:= sfeAround; //debug
    -
    -          {//2nd debug
    -          if x > x2 then
    -          begin
    -            MarkupInfo.Background:= clYellow;
    -            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
    -            MarkupInfo.FrameColor:= clBlue; //debug
    -          end;}
    -
    -          //break;
    +          end;
             end;
       end;
     end;
    @@ -237,36 +237,82 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter)
    +  or (FPreparedRow <> aRow) then
    +    exit;
    +
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    -  for i := 0 to length(FHighlights)-1  do
    -    with FHighlights[i] do
    -    begin
    -      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
    -        //continue;
    -      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
    -      begin
    -        ANextLog := FHighlights[i].X;
    +  for i := 0 to length(FFoldColorInfos)-1  do
    +    with FFoldColorInfos[i] do begin
    +      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
    +        ANextLog := FFoldColorInfos[i].X;
             break;
           end;
         end;
     end;
     
    +function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
    +var
    +  l: String;
    +  p: Integer;
    +begin
    +  l := SynEdit.Lines[index];
    +  p := 1;
    +  while not (l[p] in [#13, #10, #0])
    +  and (l[p] = #32) do inc(p);
    +  if p > 255 then p := 255;
    +  Result := p;
    +end;
    +
     procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
     var
    -  i,lvl,z : integer; //iterate parents fold
    +  i,lvl,z: integer;
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    +  var
    +    p, s, l: integer;
       begin
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    // get column of first character in row
    +    s := Length(FFirstCharacterColumn);
    +    if (s <= ANode.LineIndex)
    +    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
    +      p := FirstCharacterColumn[ANode.LineIndex];
    +      if s > ANode.LineIndex then begin
    +        FFirstCharacterColumn[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
    +      end;
    +      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
    +    end;
    +    //DebugLn('  FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), FFirstCharacterColumn[ANode.LineIndex]]);
    +    s := Length(FEndLine);
    +    if (s <= ANode.LineIndex)
    +    or (FEndLine[ANode.LineIndex] = 0) then begin
    +      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
    +      if s > ANode.LineIndex then begin
    +        FEndLine[ANode.LineIndex] := l;
    +      end else begin
    +        DebugLn('!!! FEndLine-Array too small !!!');
    +      end;
    +      //DebugLn('  Find Endline: %d: %d', [ToPos(ANode.LineIndex), l]);
    +    end;
    +    //DebugLn('  EndLine: %d: %d', [ToPos(ANode.LineIndex), FEndLine[ANode.LineIndex]]);
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z+1);
    +    with FFoldColorInfos[z] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    -      Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      if s <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumn[ANode.LineIndex];
    +      Y  := aRow;
    +      X2 := X + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -273,7 +319,9 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
    +      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
         end;
       end;
     
    @@ -281,60 +329,62 @@
       y, lvlB,lvlA: Integer;
       TmpNode: TSynFoldNodeInfo;
       NestCount : integer;
    -  procedure Later(var J:integer);
    -  begin
    -    inc(J);
    -  end;
    -  function Allowed(J: integer):boolean;
    -  begin
    -    result := J < NestCount;
    -  end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
    +  FHighlighter.CurrentLines := Lines;
     
       lvl := 0;
       i := 0;
    -  while Allowed(i) do
    -  begin
    -
    +  while i < NestCount do begin
         TmpNode := FNestList.HLNode[i];
    -    //find till valid
    -    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
    -    begin
    -      Later(i);
    -      TmpNode := FNestList.HLNode[i];
    -    end;
    +    if (sfaOutline in TmpNode.FoldAction)
    +    and not (sfaInvalid in TmpNode.FoldAction) then
    +      //avoid bug of IncludeOpeningOnLine := False;
    +      if (sfaOpen in TmpNode.FoldAction)
    +      and (TmpNode.LineIndex + 1 = aRow) then begin
    +        {do nothing here}
    +      end else begin
    +        lvlB := lvl;
     
    -    if (sfaOutline in TmpNode.FoldAction ) then
    -    //avoid bug of IncludeOpeningOnLine := False;
    -    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
    -    begin {do nothing here} end
    -    else
    -    begin
    -      lvlB := lvl;
    +        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    +          inc(lvl)
    +        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +          dec(lvl);
    +        //if (FLastNode.LineIndex >= 0)
    +        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +        // inc(lvl);
     
    -      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    -        dec(lvl);
    +        AddVerticalLine(TmpNode);
     
    -      AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -        inc(lvl);
    +        //if (z > 0)
    +        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
    +        //  // if child is on same x-pos keep level
    +        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
    +        //    lvl := FFoldColorInfos[z - 1].Level;
    +        //    FFoldColorInfos[z].Level := lvl;
    +        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        //  end;
    +        //end;
     
    -      lvlA := lvl;
    +        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    +          inc(lvl);
     
    -      with FHighlights[z] do begin
    -        LevelBefore := lvlB;
    -        LevelAfter  := lvlA;
    +        if sfaOpen in TmpNode.FoldAction then
    +          FLastNode := TmpNode;
    +
    +        lvlA := lvl;
    +
    +        with FFoldColorInfos[z] do begin
    +          LevelBefore := lvlB;
    +          LevelAfter  := lvlA;
    +        end;
           end;
    -    end;
    -
    -    Later(i);
    -    //break; //debug
    +    inc(i);
       end;
     end;
     
    @@ -345,33 +395,29 @@
       procedure AddHighlight( ANode: TSynFoldNodeInfo );
       var x,j : integer;
       begin
    -        //don't replace; don't add when already found
         x  := ANode.LogXStart + 1;
    -
         if ANode.LogXStart < ANode.LogXEnd then
    -    for j := 0 to Pred(length(FHighlights)) do
    -      if (FHighlights[j].X = x)
    -      and (FHighlights[j].Border)
    -      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
    -      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
    +    for j := 0 to Pred(length(FFoldColorInfos)) do
    +      if (FFoldColorInfos[j].X = x)
    +      and (FFoldColorInfos[j].Border)
    +      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    +      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
           then begin
    -       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
    -       FHighlights[j].Border := False
    -
    +       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    +       FFoldColorInfos[j].Border := False
           end;
     
    -    //exit; //debug
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    z := Length(FFoldColorInfos);
    +    SetLength(FFoldColorInfos, z + 1);
    +    with FFoldColorInfos[z] do begin
           Border := False;
           SrcNode:= ANode; //needed by close node
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -378,91 +424,105 @@
       end;
     
     var
    -  y,i,j,lvlB,lvlA : integer;
    -  HL: TSynCustomFoldHighlighter;
    +  LineIdx,i,j,lvlB,lvlA : integer;
       NodeList: TLazSynFoldNodeInfoList;
       TmpNode: TSynFoldNodeInfo;
    -  Found : boolean;
    +  Found: boolean;
     begin
    -  y := aRow -1;
    +  LineIdx := ToIdx(aRow);
     
    -  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -  HL.CurrentLines := Lines;
    -  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
    +  FHighlighter.CurrentLines := Lines;
    +  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
     
    -  NodeList := HL.FoldNodeInfo[y];
    +  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
       NodeList.AddReference;
       try
         NodeList.ActionFilter := [sfaOutline];
    -    //NodeList.FoldFlags:= [sfbIncludeDisabled];
         lvl := 0;
    -    J := Length(FHighlights)-1;
    +    J := Length(FFoldColorInfos) - 1;
         if J >=0 then
    -      lvl := max(0,FHighlights[J].LevelAfter);
    +      lvl := max(0,FFoldColorInfos[J].LevelAfter);
         i := 0;
         repeat
           TmpNode := NodeList[i];
     
    -      //find till valid
    -      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
    -      begin
    -        inc(i);
    -        TmpNode := NodeList[i];
    -      end;
    -      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
    -        if sfaOpen in TmpNode.FoldAction then
    -        begin
    +      if not (sfaInvalid in TmpNode.FoldAction)
    +      and (sfaOutline in TmpNode.FoldAction) then begin
    +        if sfaOpen in TmpNode.FoldAction then begin
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          //if (FLastNode.LineIndex >= 0)
    +          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +          // inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          //if (z > 0)
    +          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
    +          //begin
    +          //  // if child is on same x-pos keep level
    +          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
    +          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
    +          //    lvl := FFoldColorInfos[z - 1].Level;
    +          //    FFoldColorInfos[z].Level := lvl;
    +          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
    +          //  end;
    +          //end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
                 inc(lvl);
    +
               lvlA := lvl;
     
    -          with FHighlights[z] do begin
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
    +          with FFoldColorInfos[z] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
               end;
    -
    -        end
    -        else
    -        if sfaClose in TmpNode.FoldAction then
    -        begin
    +        end else if sfaClose in TmpNode.FoldAction then begin
               Found := False;
    -          for j := Length(FHighlights)-1 downto 0 do begin
    -            with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +          for j := Length(FFoldColorInfos)-1 downto 0 do begin
    +            with FFoldColorInfos[j].SrcNode do begin
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FFoldColorInfos[j].Level;
    +                lvlA := FFoldColorInfos[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            with FFoldColorInfos[z] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
    +              for j := j to z - 1 do begin
    +                FFoldColorInfos[j] := FFoldColorInfos[j+1];
    +              end;
    +              SetLength(FFoldColorInfos, z);
    +            end;
               end;
    -
    -          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -            //inc(lvl);
             end;
           end;
    -
           inc(i);
         until i >= NodeList.Count;
    -
       finally
         NodeList.ReleaseReference;
       end;
    @@ -469,197 +529,322 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, LastX, j: Integer;
     begin
    -  CurrentY := aRow;
    -  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter) then exit;
    +  if length(FFirstCharacterColumn) = 0 then begin
    +    // array should have been initialized due to a call to SetLines before
    +    // but with a cloned TextBuffer this is not the case
    +    // therefore init it here
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('!!! SetLines not called before PrepareMarkupForRow (cloned TextBuffer)!!!');
    +    {$ENDIF}
    +    InitArrays;
    +  end;
    +  FPreparedRow := aRow;
    +  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    -  //DoMarkupFoldAtRow(aRow);
    +  // invalidate fLastNode
    +  FLastNode.LineIndex := -1;
    +
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
    -  //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := length(FFoldColorInfos) - 1 downto 0 do begin
    +    if FFoldColorInfos[i].X > LastX then begin
    +      for j := i to length(FFoldColorInfos) - 2 do begin
    +        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
    +      end;
    +      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
    +    end;
    +    LastX := FFoldColorInfos[i].X;
    +  end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('EndMarkup');
    +  {$ENDIF}
       inherited EndMarkup;
    +  if not Assigned(FHighlighter) then exit;
       FNestList.Clear; // for next markup start
     end;
     
    -function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
    -begin
    -  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -end;
    -
     procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
     begin
       if FDefaultGroup = AValue then Exit;
       FDefaultGroup := AValue;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldGroup := FDefaultGroup;
     end;
     
    -{.$define debug_FC_line_changed}
    +procedure TSynEditMarkupFoldColors.InitArrays;
    +begin
    +  FreeAndNil(FNestList);
    +  // set cache size
    +  SetLength(FFirstCharacterColumn, Lines.Count);
    +  SetLength(FEndLine, Lines.Count);
    +  // add Changehandler
    +  Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +  Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  if Assigned(FHighlighter) then begin
    +    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +    FNestList.ResetFilter;
    +    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +    FNestList.IncludeOpeningOnLine := True; //False; //
    +  end;
    +end;
    +
     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;
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +    lLineIdx := ToIdx(pLine);
    +    pNestList.Line := lLineIdx;
    +    lCount := pNestList.Count;
    +    SetLength(pList, lCount);
    +    lAnz := 0;
    +    for i := 0 to lCount - 1 do begin
    +      lNode := pNestList.HLNode[i];
    +      if (sfaInvalid in lNode.FoldAction)
    +      or (
    +        (sfaOpen in lNode.FoldAction)
    +        and (lNode.LineIndex = lLineIdx)
    +      ) then
    +        Continue;
     
    -    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;
    +      // hint: endline for node is stored in NodeIndex
    +      lNode.NodeIndex := pNestList.NodeEndLine[i];
    +      pList[i] := lNode;
    +      inc(lAnz);
    +    end;
    +    SetLength(pList, lAnz);
    +  end;
     
    -    begin
    -      Result := SearchLine(YIndex, NIndex);
    -      if not (sfaInvalid in Result.FoldAction) then
    -        exit;
    +var
    +  i, lMinAnz, lEndLine, j, l: Integer;
    +  //lEnd, lEndLine2: integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  {$ENDIF}
     
    -      inc(YIndex);
    -      while (YIndex < LCnt) and
    -            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
    -             > StartNode.NestLvlStart)
    -      do
    -        inc(YIndex);
    -      if YIndex = LCnt then
    -        exit;
    +  if not Assigned(FHighlighter) then exit;
    +  FHighlighter.CurrentLines := Lines;
    +  if EndLine < 0 then
    +    EndLine := StartLine
    +  else
    +    // endline seems to be the first line after the change
    +    EndLine := EndLine - 1;
    +  lEndLine := EndLine;
     
    -      NIndex := -1;
    -      Result := SearchLine(YIndex, NIndex);
    +  SetLength(lStartNestList, 0);
    +  SetLength(lEndNestList, 0);
     
    -      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
    -        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
    -    end;
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at Start:');
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    +  FillNestList(lEndNestList, EndLine + 1, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at End:');
    +  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -    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
    +  // delete all nodes in lEndNodeList which where active at StartLine
    +  // to get the nodes which reach behind EndLine
    +  //lEndLine2 := 0;
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
     
    -    NodeList := HL.FoldNodeInfo[y];
    -    NodeList.AddReference;
    -    try
    -      NodeList.ActionFilter := [sfaOpen];
    -      i := 0;
    -      repeat
    -        TmpNode := NodeList[i];
    +      //// ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
    +      //lEnd := FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0);
    +      //if lEnd >= 0 then
    +      //  lEndLine2 := ToPos(lEnd);
     
    -        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;
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
       end;
     
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   Remaining Nodes:');
    +    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +    {$ENDIF}
    +    // does position of first character change for remaining node?
    +    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
    +      // position of first character changed -> find endline
    +      //lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
    +      // new: Field node index is used to store endline for node see: FillNestList()
    +      lEndLine := ToPos(lEndNestList[0].NodeIndex);
     
    -  function IsFoldMoved( aRow: Integer ): integer;
    -  var S : string;
    -    i,n : integer;
    -  begin
    -    Result := -1;
    -    n := -1;
    +    //// ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
    +    //if lEndLine = 0 then begin
    +    //  {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //  DebugLn('  !!! workaround needed !!!');
    +    //  {$ENDIF}
    +    //  lEndLine := lEndLine2;
    +    //end;
    +  end;
     
    -    S := Caret.LineText;
    -    for i := 1 to Min(Length(S), Length(FPrevCaretText)) do
    -    begin
    -      if S[i] <> FPrevCaretText[i] then
    -      begin
    -        n := i;
    -        break;
    +  // check for changes of endline for node which are active at StartLine
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    if sfaOutline in FoldAction then begin
    +      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
    +      if l <> FEndLine[LineIndex] then begin
    +        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
    +        FEndLine[LineIndex] := l;
    +        {$IFDEF SynEditMarkupFoldColoringDebug}
    +        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +        {$ENDIF}
           end;
         end;
     
    -    if n < 0 then exit;
    +  // invalidate cache
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
    +    FFirstCharacterColumn[i] := 0;
    +    FEndLine[i] := 0;
    +  end;
     
    -    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);
    +  if lEndLine > EndLine then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    {$ENDIF}
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  end;
    +end;
    +
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
    +var
    +  old: TSynEditStrings;
    +begin
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // change:
    +    // remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    FreeAndNil(FNestList);
    +  end;
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // change:
    +    if Assigned(AValue) then begin
    +      InitArrays;
    +    end else begin
    +      // clear cache
    +      SetLength(FFirstCharacterColumn, 0);
    +      SetLength(FEndLine, 0);
         end;
    +  end;
    +end;
     
    -  end;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
     var
    -  EndFoldLine,y : integer;
    +  absCount,
    +  idx, i: 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);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  idx := ToIdx(aIndex);
    +  if aCount < 0 then begin
    +    // lines deleted
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
    +      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
    +      FEndLine[i] := FEndLine[i + absCount];
    +    end;
       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}
    +  SetLength(FFirstCharacterColumn, Sender.Count);
    +  SetLength(FEndLine, Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // lines added
    +      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
    +        FEndLine[i + aCount] := FEndLine[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end else begin
    +      // first lines will be inserted
    +      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
    +        FFirstCharacterColumn[i] := 0;
    +        FEndLine[i] := 0;
    +      end;
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
    +  aIndex, aCount: Integer);
    +var
    +  newHighlighter: TSynCustomFoldHighlighter;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  if (aIndex <> -1)
    +  or (aCount <> -1) then
    +    exit;
     
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    -end;
    +  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(newHighlighter)
    +  and not (newHighlighter is TSynCustomFoldHighlighter) then
    +    newHighlighter := nil;
     
    +  if (newHighlighter = FHighlighter) then
    +    exit;
     
    +  FHighlighter := newHighlighter;
     
    +  FreeAndNil(FNestList);
    +  if Assigned(FHighlighter) then begin
    +    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +    FNestList.ResetFilter;
    +    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    +    FNestList.IncludeOpeningOnLine := True; //False; //
    +  end;
    +end;
    +
     end.
     
    +
    
  • syneditmarkupfoldcoloring.pas_v14.patch (39,894 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 53047)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -50,12 +50,14 @@
     unit SynEditMarkupFoldColoring;
     
     {$mode objfpc}{$H+}
    +{ $define SynEditMarkupFoldColoringDebug}
     
     interface
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText;
     
     type
     
    @@ -66,37 +68,47 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
       TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
    -
       { TSynEditMarkupFoldColors }
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    -    FNestList: TLazSynEditNestedFoldsList;
    +    function GetFirstCharacterColumn(index: Integer): Byte;
    +  private
    +    FHighlighter: TSynCustomFoldHighlighter;
    +    FNestList,
    +    FNestList2: TLazSynEditNestedFoldsList;
    +
    +    // cache
    +    FFirstCharacterColumnCache: Array of Byte;
    +    FEndLineCache: Array of Integer;
    +    FCacheCount,
    +    FCacheCapacity: Integer;
    +
         FDefaultGroup: integer;
    -     // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FFoldColorInfos: TMarkupFoldColorInfos;
    +    FFoldColorInfosCount: Integer;
    +
         Colors : array of TColor;
    -
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    -
    +    FPreparedRow: integer;
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
    -
    -    function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    procedure SetCacheCount(pNewCount: Integer);
    +    procedure InitCache;
    +    procedure CreateNestListsMaybe;
    +    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
    +    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -115,36 +127,21 @@
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +  , SynHighlighterPas
    +{$ENDIF}
    +  ;
     
    -  {%region Sorting FoldInfo -fold}
    -  function CompareFI(Item1, Item2: Pointer): Integer;
    -  begin
    -    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
    -    if result = 0 then
    -        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
    -    if result = 0 then
    -        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
    -          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
    +  while length(Result) < 17 do Result := Result + ' ';
    +end;
    +{$ENDIF}
     
    -    SetLength(result, Length(a));
    -    for i := 0 to Pred(l.Count) do
    -      result[i] := PMarkupFoldColorInfo(l[i])^;
    -     l.Free;
    -  end;
    -  {%endregion}
    -
     { TSynEditMarkupFoldColors }
     
     constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
    @@ -151,23 +148,30 @@
     begin
       inherited Create(ASynEdit);
     
    -  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
    -  FNestList.ResetFilter;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    -  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    -  FNestList.IncludeOpeningOnLine := True; //False; //
    +  FCacheCapacity := 0;
    +  SetCacheCount(100);
     
    +  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(FHighlighter)
    +  and not (FHighlighter  is TSynCustomFoldHighlighter) then
    +    FHighlighter := nil;
    +
    +  FDefaultGroup := 0;
    +  FFoldColorInfosCount := 0;
    +  SetLength(FFoldColorInfos, 50);
    +
    +  CreateNestListsMaybe;
    +
       MarkupInfo.Foreground := clGreen;
    -  MarkupInfo.Background := clNone; //clFuchsia;
    +  MarkupInfo.Background := clNone;
       MarkupInfo.Style := [];
       MarkupInfo.StyleMask := [];
    -  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
    +  MarkupInfo.FrameEdges:= sfeLeft;
     
       SetLength(Colors, 5);
       Colors[0] := clRed;
       Colors[1] := $000098F7; //orange
       Colors[2] := $0022CC40; //green
    -  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
       Colors[3] := $00FF682A; //blue
       Colors[4] := $00CF00C4; //purple
     end;
    @@ -174,7 +178,12 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then begin
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
       FreeAndNil(FNestList);
    +  FreeAndNil(FNestList2);
       inherited Destroy;
     end;
     
    @@ -182,23 +191,27 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i, x2both: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if not Assigned(FHighlighter) then exit;
    +  if (FPreparedRow = aRow) then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
    +    {$ENDIF}
     
    -    x2both := -3; //flag
    -    for i := 0 to length(FHighlights)-1 do
    -      with FHighlights[i] do
    +    x2both := -3;
    +    for i := 0 to FFoldColorInfosCount - 1 do
    +      with FFoldColorInfos[i] do
             if not Ignore
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    -          //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +        and (aStartCol.Logical < X2) then begin
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
    +          {$ENDIF}
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,25 +222,14 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    +          if Border then begin
                 MarkupInfo.FrameColor:= Colors[ColorIdx];
    -            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    +            MarkupInfo.FrameEdges:= sfeLeft;
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
                 MarkupInfo.Foreground := Colors[ColorIdx];
    -
    -          //MarkupInfo.FrameEdges:= sfeAround; //debug
    -
    -          {//2nd debug
    -          if x > x2 then
    -          begin
    -            MarkupInfo.Background:= clYellow;
    -            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
    -            MarkupInfo.FrameColor:= clBlue; //debug
    -          end;}
    -
    -          //break;
    +          end;
             end;
       end;
     end;
    @@ -237,36 +239,77 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter)
    +  or (FPreparedRow <> aRow) then
    +    exit;
    +
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    -  for i := 0 to length(FHighlights)-1  do
    -    with FHighlights[i] do
    -    begin
    -      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
    -        //continue;
    -      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
    -      begin
    -        ANextLog := FHighlights[i].X;
    +  for i := 0 to FFoldColorInfosCount - 1  do
    +    with FFoldColorInfos[i] do begin
    +      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
    +        ANextLog := FFoldColorInfos[i].X;
             break;
           end;
         end;
     end;
     
    +function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
    +var
    +  l: String;
    +  p: Integer;
    +begin
    +  l := SynEdit.Lines[index];
    +  p := 1;
    +  while not (l[p] in [#13, #10, #0])
    +  and (l[p] in [#9, #32]) do inc(p);
    +  if p > 255 then p := 255;
    +  Result := TCustomSynEdit(SynEdit).LogicalToPhysicalPos(Point(p, toPos(index))).x;
    +end;
    +
     procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
     var
    -  i,lvl,z : integer; //iterate parents fold
    +  i,lvl: integer;
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    +  var
    +    p, l: integer;
       begin
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    // get column of first character in row
    +    if (FCacheCapacity <= ANode.LineIndex)
    +    or (FFirstCharacterColumnCache[ANode.LineIndex] = 0) then begin
    +      p := FirstCharacterColumn[ANode.LineIndex];
    +      if FCacheCapacity > ANode.LineIndex then begin
    +        FFirstCharacterColumnCache[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow: FFirstCharacterColumn-Array too small !!!');
    +      end;
    +    end;
    +    if (FCacheCapacity <= ANode.LineIndex)
    +    or (FEndLineCache[ANode.LineIndex] = 0) then begin
    +      FNestList2.Line := ANode.LineIndex;
    +      l := ToPos(FNestList2.NodeEndLine[0]);
    +      if FCacheCapacity > ANode.LineIndex then begin
    +        FEndLineCache[ANode.LineIndex] := l;
    +      end else begin
    +        DebugLn('!!! TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow: FEndLine-Array too small !!!');
    +      end;
    +    end;
    +    inc(FFoldColorInfosCount);
    +    with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    -      Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      if FCacheCapacity <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumnCache[ANode.LineIndex];
    +      X := TCustomSynEdit(SynEdit).PhysicalToLogicalPos(Point(X, aRow)).x;
    +      Y  := aRow;
    +      X2 := X + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -273,7 +316,8 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
         end;
       end;
     
    @@ -281,60 +325,62 @@
       y, lvlB,lvlA: Integer;
       TmpNode: TSynFoldNodeInfo;
       NestCount : integer;
    -  procedure Later(var J:integer);
    -  begin
    -    inc(J);
    -  end;
    -  function Allowed(J: integer):boolean;
    -  begin
    -    result := J < NestCount;
    -  end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
    +  FHighlighter.CurrentLines := Lines;
     
       lvl := 0;
       i := 0;
    -  while Allowed(i) do
    -  begin
    -
    +  while i < NestCount do begin
         TmpNode := FNestList.HLNode[i];
    -    //find till valid
    -    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
    -    begin
    -      Later(i);
    -      TmpNode := FNestList.HLNode[i];
    -    end;
    +    if (sfaOutline in TmpNode.FoldAction)
    +    and not (sfaInvalid in TmpNode.FoldAction) then
    +      //avoid bug of IncludeOpeningOnLine := False;
    +      if (sfaOpen in TmpNode.FoldAction)
    +      and (TmpNode.LineIndex + 1 = aRow) then begin
    +        {do nothing here}
    +      end else begin
    +        lvlB := lvl;
     
    -    if (sfaOutline in TmpNode.FoldAction ) then
    -    //avoid bug of IncludeOpeningOnLine := False;
    -    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
    -    begin {do nothing here} end
    -    else
    -    begin
    -      lvlB := lvl;
    +        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    +          inc(lvl)
    +        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +          dec(lvl);
    +        //if (FLastNode.LineIndex >= 0)
    +        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +        // inc(lvl);
     
    -      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    -        dec(lvl);
    +        AddVerticalLine(TmpNode);
     
    -      AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -        inc(lvl);
    +        //if (FFoldColorInfosCount - 1 > 0)
    +        //and (FFoldColorInfos[FFoldColorInfosCount - 1].X = FFoldColorInfos[FFoldColorInfosCount - 2].X) then begin
    +        //  // if child is on same x-pos keep level
    +        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[FFoldColorInfosCount - 1].SrcNode.FoldAction then begin
    +        //    lvl := FFoldColorInfos[FFoldColorInfosCount - 2].Level;
    +        //    FFoldColorInfos[FFoldColorInfosCount - 1].Level := lvl;
    +        //    FFoldColorInfos[FFoldColorInfosCount - 1].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        //  end;
    +        //end;
     
    -      lvlA := lvl;
    +        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    +          inc(lvl);
     
    -      with FHighlights[z] do begin
    -        LevelBefore := lvlB;
    -        LevelAfter  := lvlA;
    +        if sfaOpen in TmpNode.FoldAction then
    +          FLastNode := TmpNode;
    +
    +        lvlA := lvl;
    +
    +        with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
    +          LevelBefore := lvlB;
    +          LevelAfter  := lvlA;
    +        end;
           end;
    -    end;
    -
    -    Later(i);
    -    //break; //debug
    +    inc(i);
       end;
     end;
     
    @@ -345,33 +391,28 @@
       procedure AddHighlight( ANode: TSynFoldNodeInfo );
       var x,j : integer;
       begin
    -        //don't replace; don't add when already found
         x  := ANode.LogXStart + 1;
    -
         if ANode.LogXStart < ANode.LogXEnd then
    -    for j := 0 to Pred(length(FHighlights)) do
    -      if (FHighlights[j].X = x)
    -      and (FHighlights[j].Border)
    -      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
    -      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
    +    for j := 0 to FFoldColorInfosCount - 1 do
    +      if (FFoldColorInfos[j].X = x)
    +      and (FFoldColorInfos[j].Border)
    +      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    +      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
           then begin
    -       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
    -       FHighlights[j].Border := False
    -
    +       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    +       FFoldColorInfos[j].Border := False
           end;
     
    -    //exit; //debug
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    inc(FFoldColorInfosCount);
    +    with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
           Border := False;
           SrcNode:= ANode; //needed by close node
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -378,91 +419,105 @@
       end;
     
     var
    -  y,i,j,lvlB,lvlA : integer;
    -  HL: TSynCustomFoldHighlighter;
    +  LineIdx,i,j,lvlB,lvlA : integer;
       NodeList: TLazSynFoldNodeInfoList;
       TmpNode: TSynFoldNodeInfo;
    -  Found : boolean;
    +  Found: boolean;
     begin
    -  y := aRow -1;
    +  LineIdx := ToIdx(aRow);
     
    -  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -  HL.CurrentLines := Lines;
    -  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
    +  FHighlighter.CurrentLines := Lines;
    +  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
     
    -  NodeList := HL.FoldNodeInfo[y];
    +  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
       NodeList.AddReference;
       try
         NodeList.ActionFilter := [sfaOutline];
    -    //NodeList.FoldFlags:= [sfbIncludeDisabled];
         lvl := 0;
    -    J := Length(FHighlights)-1;
    +    J := FFoldColorInfosCount - 1;
         if J >=0 then
    -      lvl := max(0,FHighlights[J].LevelAfter);
    +      lvl := max(0,FFoldColorInfos[J].LevelAfter);
         i := 0;
         repeat
           TmpNode := NodeList[i];
     
    -      //find till valid
    -      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
    -      begin
    -        inc(i);
    -        TmpNode := NodeList[i];
    -      end;
    -      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
    -        if sfaOpen in TmpNode.FoldAction then
    -        begin
    +      if not (sfaInvalid in TmpNode.FoldAction)
    +      and (sfaOutline in TmpNode.FoldAction) then begin
    +        if sfaOpen in TmpNode.FoldAction then begin
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          //if (FLastNode.LineIndex >= 0)
    +          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +          // inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          //if (FFoldColorInfosCount - 1 > 0)
    +          //and (FFoldColorInfos[FFoldColorInfosCount - 1].X = FFoldColorInfos[FFoldColorInfosCount - 2].X) then
    +          //begin
    +          //  // if child is on same x-pos keep level
    +          //  if (sfaClose in FFoldColorInfos[FFoldColorInfosCount - 1].SrcNode.FoldAction)
    +          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[FFoldColorInfosCount - 1].SrcNode.FoldAction) then begin
    +          //    lvl := FFoldColorInfos[FFoldColorInfosCount - 2].Level;
    +          //    FFoldColorInfos[FFoldColorInfosCount - 1].Level := lvl;
    +          //    FFoldColorInfos[FFoldColorInfosCount - 1].ColorIdx := Max(0, lvl) mod (length(Colors));
    +          //  end;
    +          //end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
                 inc(lvl);
    +
               lvlA := lvl;
     
    -          with FHighlights[z] do begin
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
    +          with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
               end;
    -
    -        end
    -        else
    -        if sfaClose in TmpNode.FoldAction then
    -        begin
    +        end else if sfaClose in TmpNode.FoldAction then begin
               Found := False;
    -          for j := Length(FHighlights)-1 downto 0 do begin
    -            with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +          for j := FFoldColorInfosCount - 1 downto 0 do begin
    +            with FFoldColorInfos[j].SrcNode do begin
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FFoldColorInfos[j].Level;
    +                lvlA := FFoldColorInfos[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FFoldColorInfos[j].X > FFoldColorInfos[FFoldColorInfosCount - 1].X then begin
    +              for j := j to FFoldColorInfosCount - 1 - 1 do begin
    +                FFoldColorInfos[j] := FFoldColorInfos[j+1];
    +              end;
    +              dec(FFoldColorInfosCount);
    +            end;
               end;
    -
    -          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -            //inc(lvl);
             end;
           end;
    -
           inc(i);
         until i >= NodeList.Count;
    -
       finally
         NodeList.ReleaseReference;
       end;
    @@ -469,197 +524,339 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, LastX, j: Integer;
    +
     begin
    -  CurrentY := aRow;
    -  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter) then exit;
    +  if length(FFirstCharacterColumnCache) = 0 then begin
    +    // array should have been initialized due to a call to SetLines before
    +    // but with a cloned TextBuffer this is not the case
    +    // therefore init it here
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('!!! SetLines not called before PrepareMarkupForRow (cloned TextBuffer)!!!');
    +    {$ENDIF}
    +    InitCache;
    +  end;
    +  FPreparedRow := aRow;
    +  FFoldColorInfosCount := 0; //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    -  //DoMarkupFoldAtRow(aRow);
    +  // invalidate fLastNode
    +  FLastNode.LineIndex := -1;
    +
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
    -  //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := FFoldColorInfosCount - 1 downto 0 do begin
    +    if FFoldColorInfos[i].X > LastX then begin
    +      for j := i to length(FFoldColorInfos) - 2 do begin
    +        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
    +      end;
    +      dec(FFoldColorInfosCount);
    +    end;
    +    LastX := FFoldColorInfos[i].X;
    +  end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('EndMarkup');
    +  {$ENDIF}
       inherited EndMarkup;
    +  if not Assigned(FHighlighter) then exit;
       FNestList.Clear; // for next markup start
     end;
     
    -function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
    -begin
    -  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -end;
    -
     procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
     begin
       if FDefaultGroup = AValue then Exit;
       FDefaultGroup := AValue;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldGroup := FDefaultGroup;
    +  FNestList2.FoldGroup := FDefaultGroup;
     end;
     
    -{.$define debug_FC_line_changed}
    -procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
    -  ACountDiff: Integer);
    -{$ifdef debug_FC_line_changed}
    -var F : TCustomForm;
    +procedure TSynEditMarkupFoldColors.SetCacheCount(pNewCount: Integer);
    +var
    +  i: Integer;
     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}
    +  if pNewCount > FCacheCapacity then begin
    +    // expand array
    +    FCacheCapacity := pNewCount + 900;
    +    SetLength(FFirstCharacterColumnCache, FCacheCapacity);
    +    SetLength(FEndLineCache, FCacheCapacity);
    +  end;
    +  if pNewCount > FCacheCount then begin
    +    // clear new section
    +    for i := FCacheCount to pNewCount - 1 do begin
    +      FFirstCharacterColumnCache[i] := 0;
    +      FEndLineCache[i] := 0;
    +    end;
    +  end;
    +  FCacheCount := pNewCount;
    +end;
     
    +procedure TSynEditMarkupFoldColors.InitCache;
    +begin
    +  if Assigned(FNestList) then begin
    +    FNestList.Lines := Lines;
    +    FNestList2.Lines := Lines;
    +  end;
    +  // set cache size
    +  SetCacheCount(Lines.Count);
    +  // add Changehandler
    +  Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +  Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +end;
     
    +procedure TSynEditMarkupFoldColors.CreateNestListsMaybe;
    +begin
    +  if Assigned(FHighlighter) then begin
    +    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +    FNestList.ResetFilter;
    +    FNestList.FoldGroup := FDefaultGroup;
    +    FNestList.FoldFlags := [sfbIncludeDisabled];
    +    FNestList.IncludeOpeningOnLine := True;
    +    FNestList2 := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +    FNestList2.ResetFilter;
    +    FNestList2.FoldGroup := FDefaultGroup;
    +    FNestList2.FoldFlags := [sfbIncludeDisabled];
    +    FNestList2.IncludeOpeningOnLine := True;
    +  end;
    +end;
     
    -  function GetPairCloseFold(aRow, X : integer  ): Integer;
    +procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
    +  ACountDiff: Integer);
    +
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +    lLineIdx := ToIdx(pLine);
    +    pNestList.Line := lLineIdx;
    +    lCount := pNestList.Count;
    +    SetLength(pList, lCount);
    +    lAnz := 0;
    +    for i := 0 to lCount - 1 do begin
    +      lNode := pNestList.HLNode[i];
    +      if (sfaInvalid in lNode.FoldAction)
    +      or (
    +        (sfaOpen in lNode.FoldAction)
    +        and (lNode.LineIndex = lLineIdx)
    +      ) then
    +        Continue;
     
    -    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?)
    +      // hint: NodeEndLine for node is stored in NodeIndex
    +      lNode.NodeIndex := pNestList.NodeEndLine[i];
    +      pList[i] := lNode;
    +      inc(lAnz);
         end;
    +    SetLength(pList, lAnz);
    +  end;
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    +var
    +  i, lMinAnz, lEndLine, j, l: Integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  {$ENDIF}
     
    -    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
    +  // lines available?
    +  if Lines.Count = 0 then
    +    exit;
     
    -    NodeList := HL.FoldNodeInfo[y];
    -    NodeList.AddReference;
    -    try
    -      NodeList.ActionFilter := [sfaOpen];
    -      i := 0;
    -      repeat
    -        TmpNode := NodeList[i];
    +  if not Assigned(FHighlighter) then exit;
    +  FHighlighter.CurrentLines := Lines;
    +  if EndLine < 0 then
    +    EndLine := StartLine
    +  else
    +    // endline seems to be the first line after the change
    +    EndLine := EndLine - 1;
    +  lEndLine := EndLine;
     
    -        if TmpNode.LogXStart < X-1 then
    -        begin
    -          inc(i);
    -          continue;
    -        end;
    +  SetLength(lStartNestList, 0);
    +  SetLength(lEndNestList, 0);
     
    -        //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;
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at Start:');
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -        inc(i);
    -      until i >= NodeList.Count;
    +  FillNestList(lEndNestList, EndLine + 1, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at End:');
    +  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -    finally
    -      NodeList.ReleaseReference;
    +  // delete all nodes in lEndNodeList which where active at StartLine
    +  // to get the nodes which reach behind EndLine
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
       end;
     
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   Remaining Nodes:');
    +    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +    {$ENDIF}
    +    // does position of first character change for remaining node?
    +    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumnCache[lEndNestList[0].LineIndex] then
    +      // new: Field NodeIndex is used to store NodeEndLine for node see: FillNestList()
    +      lEndLine := ToPos(lEndNestList[0].NodeIndex);
    +  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;
    +  // check for changes of endline for node which are active at StartLine
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    if sfaOutline in FoldAction then begin
    +      FNestList2.Line := LineIndex;
    +      l := ToPos(FNestList2.NodeEndLine[0]);
    +      if l <> FEndLineCache[LineIndex] then begin
    +        lEndLine := Max(lEndLine, Max(l, FEndLineCache[LineIndex]));
    +        FEndLineCache[LineIndex] := l;
    +        {$IFDEF SynEditMarkupFoldColoringDebug}
    +        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +        {$ENDIF}
           end;
         end;
     
    -    if n < 0 then exit;
    +  // invalidate cache
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
    +    FFirstCharacterColumnCache[i] := 0;
    +    FEndLineCache[i] := 0;
    +  end;
     
    -    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);
    +  if lEndLine > EndLine then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    {$ENDIF}
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  end;
    +end;
    +
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
    +var
    +  old: TSynEditStrings;
    +begin
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // change:
    +    // remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // change:
    +    if Assigned(AValue) then begin
    +      InitCache;
    +    end else begin
    +      // clear cache
    +      SetCacheCount(0);
    +      if Assigned(FNestList) then begin
    +        FNestList.Lines := nil;
    +        FNestList2.Lines := nil;
    +      end;
         end;
    +  end;
    +end;
     
    -  end;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
     var
    -  EndFoldLine,y : integer;
    +  absCount,
    +  idx, i: 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);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  idx := ToIdx(aIndex);
    +  if (aCount < 0)
    +  and (idx >= 0) then begin
    +    // lines deleted
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumnCache) - 1 - absCount do begin
    +      FFirstCharacterColumnCache[i] := FFirstCharacterColumnCache[i + absCount];
    +      FEndLineCache[i] := FEndLineCache[i + absCount];
    +    end;
       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}
    +  SetCacheCount(Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // lines added
    +      for i := Length(FFirstCharacterColumnCache) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumnCache[i + aCount] := FFirstCharacterColumnCache[i];
    +        FEndLineCache[i + aCount] := FEndLineCache[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumnCache) - 1) do begin
    +        FFirstCharacterColumnCache[i] := 0;
    +        FEndLineCache[i] := 0;
    +      end;
    +    end else begin
    +      // first lines will be inserted
    +      for i := 0 to Length(FFirstCharacterColumnCache) - 1 do begin
    +        FFirstCharacterColumnCache[i] := 0;
    +        FEndLineCache[i] := 0;
    +      end;
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
    +  aIndex, aCount: Integer);
    +var
    +  newHighlighter: TSynCustomFoldHighlighter;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  if (aIndex <> -1)
    +  or (aCount <> -1) then
    +    exit;
     
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    -end;
    +  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
    +  if Assigned(newHighlighter)
    +  and not (newHighlighter is TSynCustomFoldHighlighter) then
    +    newHighlighter := nil;
     
    +  if (newHighlighter = FHighlighter) then
    +    exit;
     
    +  FHighlighter := newHighlighter;
     
    +  FreeAndNil(FNestList);
    +  FreeAndNil(FNestList2);
    +  CreateNestListsMaybe;
    +end;
    +
     end.
     
    +
    
  • syneditmarkupfoldcoloring.pas_v15.patch (40,296 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 53069)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -39,23 +39,18 @@
       - many features are well tested for PasSynPas.pas
       - only active when SynEdit.Highlighter is TSynCustomFoldHighlighter
     
    -
    -Known Issues:
    -  - wrong drawing vertical lines position when a line is mixed with tab char
    -  - poor configuration
    -  - no design time
    -
    -
     -------------------------------------------------------------------------------}
     unit SynEditMarkupFoldColoring;
     
     {$mode objfpc}{$H+}
    +{ $define SynEditMarkupFoldColoringDebug}
     
     interface
     
     uses
       Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
    -  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
    +  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
    +  LazSynEditText;
     
     type
     
    @@ -66,7 +61,7 @@
         Border  : Boolean;
         Ignore  : Boolean; //no color no line
         SrcNode : TSynFoldNodeInfo;
    -    LevelBefore, LevelAfter : integer;//needed by non nest nodes
    +    LevelBefore, Level, LevelAfter : integer; //needed by non nest nodes
       end;
     
       TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
    @@ -76,27 +71,38 @@
     
       TSynEditMarkupFoldColors = class(TSynEditMarkup)
       private
    -    FNestList: TLazSynEditNestedFoldsList;
    +    function GetFirstCharacterColumn(index: Integer): Byte;
    +  private
    +    FHighlighter: TSynCustomFoldHighlighter;
    +    FNestList,
    +    FNestList2: TLazSynEditNestedFoldsList;
    +
    +    // cache
    +    FFirstCharacterColumnCache: Array of Byte;
    +    FEndLineCache: Array of Integer;
    +    FCacheCount,
    +    FCacheCapacity: Integer;
    +
         FDefaultGroup: integer;
    -     // Physical Position
    -    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
    +    FFoldColorInfos: TMarkupFoldColorInfos;
    +    FFoldColorInfosCount: Integer;
    +
         Colors : array of TColor;
    -
    -    {%region invalidating}
    -    CurrentY : integer;  //??
    -    FCaretY : integer;    // flag identify for refresh begin______
    -    FPrevCaretText : string;  // flag identify for refresh begin______
    -    {%endregion}
    -
    +    FPreparedRow: integer;
    +    FLastNode: TSynFoldNodeInfo;
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
    -
    -    function GetFoldHighLighter: TSynCustomFoldHighlighter;
         procedure SetDefaultGroup(AValue: integer);
    +    procedure SetCacheCount(pNewCount: Integer);
    +    procedure InitCache;
    +    procedure ClearCache;
    +    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
       protected
         // Notifications about Changes to the text
         procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
    -    procedure DoCaretChanged(Sender: TObject); override;
    +    procedure SetLines(const AValue: TSynEditStrings); override;
    +    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
    +    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -115,36 +121,21 @@
     
     implementation
     uses
    -  SynEdit,SynEditTypes, SynEditMiscProcs;
    +  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +  , SynHighlighterPas
    +{$ENDIF}
    +  ;
     
    -  {%region Sorting FoldInfo -fold}
    -  function CompareFI(Item1, Item2: Pointer): Integer;
    -  begin
    -    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
    -    if result = 0 then
    -        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
    -    if result = 0 then
    -        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
    -          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
    +{$IFDEF SynEditMarkupFoldColoringDebug}
    +function FoldTypeToStr(p_FoldType: Pointer): String;
    +begin
    +  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
    +  while length(Result) < 17 do Result := Result + ' ';
    +end;
    +{$ENDIF}
     
    -    SetLength(result, Length(a));
    -    for i := 0 to Pred(l.Count) do
    -      result[i] := PMarkupFoldColorInfo(l[i])^;
    -     l.Free;
    -  end;
    -  {%endregion}
    -
     { TSynEditMarkupFoldColors }
     
     constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
    @@ -151,23 +142,39 @@
     begin
       inherited Create(ASynEdit);
     
    -  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
    +  FCacheCapacity := 0;
    +  SetCacheCount(100);
    +
    +  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(SynEdit).Highlighter);
    +  if Assigned(FHighlighter)
    +  and not (FHighlighter  is TSynCustomFoldHighlighter) then
    +    FHighlighter := nil;
    +
    +  FDefaultGroup := 0;
    +  FFoldColorInfosCount := 0;
    +  SetLength(FFoldColorInfos, 50);
    +
    +  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
       FNestList.ResetFilter;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    -  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
    -  FNestList.IncludeOpeningOnLine := True; //False; //
    +  FNestList.FoldGroup := FDefaultGroup;
    +  FNestList.FoldFlags := [sfbIncludeDisabled];
    +  FNestList.IncludeOpeningOnLine := True;
    +  FNestList2 := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
    +  FNestList2.ResetFilter;
    +  FNestList2.FoldGroup := FDefaultGroup;
    +  FNestList2.FoldFlags := [sfbIncludeDisabled];
    +  FNestList2.IncludeOpeningOnLine := True;
     
       MarkupInfo.Foreground := clGreen;
    -  MarkupInfo.Background := clNone; //clFuchsia;
    +  MarkupInfo.Background := clNone;
       MarkupInfo.Style := [];
       MarkupInfo.StyleMask := [];
    -  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
    +  MarkupInfo.FrameEdges:= sfeLeft;
     
       SetLength(Colors, 5);
       Colors[0] := clRed;
       Colors[1] := $000098F7; //orange
       Colors[2] := $0022CC40; //green
    -  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
       Colors[3] := $00FF682A; //blue
       Colors[4] := $00CF00C4; //purple
     end;
    @@ -174,7 +181,12 @@
     
     destructor TSynEditMarkupFoldColors.Destroy;
     begin
    +  if Assigned(Lines) then begin
    +    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +  end;
       FreeAndNil(FNestList);
    +  FreeAndNil(FNestList2);
       inherited Destroy;
     end;
     
    @@ -182,23 +194,27 @@
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
     var
    -  i,x2both : integer;
    +  i, x2both: integer;
     begin
       Result := nil;
    -  if (CurrentY = aRow) then begin
    +  if not Assigned(FHighlighter) then exit;
    +  if (FPreparedRow = aRow) then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
    +    {$ENDIF}
     
    -    x2both := -3; //flag
    -    for i := 0 to length(FHighlights)-1 do
    -      with FHighlights[i] do
    +    x2both := -3;
    +    for i := 0 to FFoldColorInfosCount - 1 do
    +      with FFoldColorInfos[i] do
             if not Ignore
             and (X < X2)
             and (ColorIdx >= 0)
             and (aStartCol.Logical >= x)
    -        and (aStartCol.Logical < X2) then
    -        begin
    -          //MarkupInfo.FrameColor:= clGreen; //debug
    -          if x2both = -3 then //first call flag
    -          begin
    +        and (aStartCol.Logical < X2) then begin
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
    +          {$ENDIF}
    +          if x2both = -3 then begin //first call flag
                 MarkupInfo.FrameColor:= clNone;
                 MarkupInfo.Foreground:= clNone;
                 MarkupInfo.Background:= clNone;
    @@ -209,25 +225,14 @@
               Result := MarkupInfo;
               x2both := max(x2both, x2);
               MarkupInfo.SetFrameBoundsLog(x, x2both);
    -          if Border then
    -          begin
    +          if Border then begin
                 MarkupInfo.FrameColor:= Colors[ColorIdx];
    -            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
    -          end
    -          else
    +            MarkupInfo.FrameEdges:= sfeLeft;
    +          end else begin
    +            MarkupInfo.FrameColor:= clNone;
    +            MarkupInfo.FrameEdges:= sfeNone;
                 MarkupInfo.Foreground := Colors[ColorIdx];
    -
    -          //MarkupInfo.FrameEdges:= sfeAround; //debug
    -
    -          {//2nd debug
    -          if x > x2 then
    -          begin
    -            MarkupInfo.Background:= clYellow;
    -            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
    -            MarkupInfo.FrameColor:= clBlue; //debug
    -          end;}
    -
    -          //break;
    +          end;
             end;
       end;
     end;
    @@ -237,36 +242,77 @@
       const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
     var i : integer;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter)
    +  or (FPreparedRow <> aRow) then
    +    exit;
    +
       ANextLog := -1;
       ANextPhys := -1;
    -  if (CurrentY = aRow)  then
    -  for i := 0 to length(FHighlights)-1  do
    -    with FHighlights[i] do
    -    begin
    -      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
    -        //continue;
    -      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
    -      begin
    -        ANextLog := FHighlights[i].X;
    +  for i := 0 to FFoldColorInfosCount - 1  do
    +    with FFoldColorInfos[i] do begin
    +      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
    +        ANextLog := FFoldColorInfos[i].X;
             break;
           end;
         end;
     end;
     
    +function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
    +var
    +  l: String;
    +  p: Integer;
    +begin
    +  l := SynEdit.Lines[index];
    +  p := 1;
    +  while not (l[p] in [#13, #10, #0])
    +  and (l[p] in [#9, #32]) do inc(p);
    +  if p > 255 then p := 255;
    +  Result := TCustomSynEdit(SynEdit).LogicalToPhysicalPos(Point(p, toPos(index))).x;
    +end;
    +
     procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
     var
    -  i,lvl,z : integer; //iterate parents fold
    +  i,lvl: integer;
     
       procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
    +  var
    +    p, l: integer;
       begin
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    // get column of first character in row
    +    if (FCacheCapacity <= ANode.LineIndex)
    +    or (FFirstCharacterColumnCache[ANode.LineIndex] = 0) then begin
    +      p := FirstCharacterColumn[ANode.LineIndex];
    +      if FCacheCapacity > ANode.LineIndex then begin
    +        FFirstCharacterColumnCache[ANode.LineIndex] := p;
    +      end else begin
    +        DebugLn('!!! TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow: FFirstCharacterColumn-Array too small !!!');
    +      end;
    +    end;
    +    if (FCacheCapacity <= ANode.LineIndex)
    +    or (FEndLineCache[ANode.LineIndex] = 0) then begin
    +      FNestList2.Line := ANode.LineIndex;
    +      l := ToPos(FNestList2.NodeEndLine[0]);
    +      if FCacheCapacity > ANode.LineIndex then begin
    +        FEndLineCache[ANode.LineIndex] := l;
    +      end else begin
    +        DebugLn('!!! TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow: FEndLine-Array too small !!!');
    +      end;
    +    end;
    +    inc(FFoldColorInfosCount);
    +    with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
    +
           SrcNode:= ANode; //needed by close node
    -      Border := ANode.LineIndex + 1 <> aRow;
    -      X  := ANode.LogXStart + 1;
    -      Y  := aRow;//ANode.LineIndex + 1;
    -      X2 := X+1; //ANode.LogXEnd + 1;
    +      Border := ToPos(ANode.LineIndex) <> aRow;
    +      if FCacheCapacity <= ANode.LineIndex then
    +        X  := p
    +      else
    +        X  := FFirstCharacterColumnCache[ANode.LineIndex];
    +      X := TCustomSynEdit(SynEdit).PhysicalToLogicalPos(Point(X, aRow)).x;
    +      Y  := aRow;
    +      X2 := X + 1;
           Ignore := False;
     
           if Border and (sfaOutlineNoLine in ANode.FoldAction) then
    @@ -273,7 +319,8 @@
             Ignore := True;
           if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
             Ignore := True;
    -        ColorIdx := lvl mod (length(Colors))
    +      Level := lvl;
    +      ColorIdx := Max(0, lvl) mod (length(Colors));
         end;
       end;
     
    @@ -281,60 +328,62 @@
       y, lvlB,lvlA: Integer;
       TmpNode: TSynFoldNodeInfo;
       NestCount : integer;
    -  procedure Later(var J:integer);
    -  begin
    -    inc(J);
    -  end;
    -  function Allowed(J: integer):boolean;
    -  begin
    -    result := J < NestCount;
    -  end;
     
     begin
    -  y := aRow-1;
    +  y := ToIdx(aRow);
       FNestList.Line := y;
       NestCount := FNestList.Count;
    +  FHighlighter.CurrentLines := Lines;
     
       lvl := 0;
       i := 0;
    -  while Allowed(i) do
    -  begin
    -
    +  while i < NestCount do begin
         TmpNode := FNestList.HLNode[i];
    -    //find till valid
    -    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
    -    begin
    -      Later(i);
    -      TmpNode := FNestList.HLNode[i];
    -    end;
    +    if (sfaOutline in TmpNode.FoldAction)
    +    and not (sfaInvalid in TmpNode.FoldAction) then
    +      //avoid bug of IncludeOpeningOnLine := False;
    +      if (sfaOpen in TmpNode.FoldAction)
    +      and (TmpNode.LineIndex + 1 = aRow) then begin
    +        {do nothing here}
    +      end else begin
    +        lvlB := lvl;
     
    -    if (sfaOutline in TmpNode.FoldAction ) then
    -    //avoid bug of IncludeOpeningOnLine := False;
    -    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
    -    begin {do nothing here} end
    -    else
    -    begin
    -      lvlB := lvl;
    +        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    +          inc(lvl)
    +        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +          dec(lvl);
    +        //if (FLastNode.LineIndex >= 0)
    +        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +        // inc(lvl);
     
    -      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -        inc(lvl);
    -      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    -        dec(lvl);
    +        AddVerticalLine(TmpNode);
     
    -      AddVerticalLine(TmpNode);
    -      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -        inc(lvl);
    +        //if (FFoldColorInfosCount - 1 > 0)
    +        //and (FFoldColorInfos[FFoldColorInfosCount - 1].X = FFoldColorInfos[FFoldColorInfosCount - 2].X) then begin
    +        //  // if child is on same x-pos keep level
    +        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[FFoldColorInfosCount - 1].SrcNode.FoldAction then begin
    +        //    lvl := FFoldColorInfos[FFoldColorInfosCount - 2].Level;
    +        //    FFoldColorInfos[FFoldColorInfosCount - 1].Level := lvl;
    +        //    FFoldColorInfos[FFoldColorInfosCount - 1].ColorIdx := Max(0, lvl) mod (length(Colors));
    +        //  end;
    +        //end;
     
    -      lvlA := lvl;
    +        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    +          inc(lvl);
     
    -      with FHighlights[z] do begin
    -        LevelBefore := lvlB;
    -        LevelAfter  := lvlA;
    +        if sfaOpen in TmpNode.FoldAction then
    +          FLastNode := TmpNode;
    +
    +        lvlA := lvl;
    +
    +        with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
    +          LevelBefore := lvlB;
    +          LevelAfter  := lvlA;
    +        end;
           end;
    -    end;
    -
    -    Later(i);
    -    //break; //debug
    +    inc(i);
       end;
     end;
     
    @@ -345,33 +394,28 @@
       procedure AddHighlight( ANode: TSynFoldNodeInfo );
       var x,j : integer;
       begin
    -        //don't replace; don't add when already found
         x  := ANode.LogXStart + 1;
    -
         if ANode.LogXStart < ANode.LogXEnd then
    -    for j := 0 to Pred(length(FHighlights)) do
    -      if (FHighlights[j].X = x)
    -      and (FHighlights[j].Border)
    -      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
    -      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
    +    for j := 0 to FFoldColorInfosCount - 1 do
    +      if (FFoldColorInfos[j].X = x)
    +      and (FFoldColorInfos[j].Border)
    +      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    +      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
           then begin
    -       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
    -       FHighlights[j].Border := False
    -
    +       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    +       FFoldColorInfos[j].Border := False
           end;
     
    -    //exit; //debug
    -    z := Length(FHighlights);
    -    SetLength(FHighlights, z+1);
    -    with FHighlights[z] do begin
    +    inc(FFoldColorInfosCount);
    +    with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
           Border := False;
           SrcNode:= ANode; //needed by close node
           Y  := ANode.LineIndex + 1;
           X  := ANode.LogXStart + 1;
           X2 := ANode.LogXEnd + 1;
    -      //ColorIdx := lvl;
    +      Level := lvl;
           if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := lvl mod (length(Colors))
    +         ColorIdx := Max(0, lvl) mod (length(Colors))
           else
              ColorIdx := -1;
         end;
    @@ -378,91 +422,105 @@
       end;
     
     var
    -  y,i,j,lvlB,lvlA : integer;
    -  HL: TSynCustomFoldHighlighter;
    +  LineIdx,i,j,lvlB,lvlA : integer;
       NodeList: TLazSynFoldNodeInfoList;
       TmpNode: TSynFoldNodeInfo;
    -  Found : boolean;
    +  Found: boolean;
     begin
    -  y := aRow -1;
    +  LineIdx := ToIdx(aRow);
     
    -  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -  HL.CurrentLines := Lines;
    -  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
    +  FHighlighter.CurrentLines := Lines;
    +  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
     
    -  NodeList := HL.FoldNodeInfo[y];
    +  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
       NodeList.AddReference;
       try
         NodeList.ActionFilter := [sfaOutline];
    -    //NodeList.FoldFlags:= [sfbIncludeDisabled];
         lvl := 0;
    -    J := Length(FHighlights)-1;
    +    J := FFoldColorInfosCount - 1;
         if J >=0 then
    -      lvl := max(0,FHighlights[J].LevelAfter);
    +      lvl := max(0,FFoldColorInfos[J].LevelAfter);
         i := 0;
         repeat
           TmpNode := NodeList[i];
     
    -      //find till valid
    -      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
    -      begin
    -        inc(i);
    -        TmpNode := NodeList[i];
    -      end;
    -      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
    -        if sfaOpen in TmpNode.FoldAction then
    -        begin
    +      if not (sfaInvalid in TmpNode.FoldAction)
    +      and (sfaOutline in TmpNode.FoldAction) then begin
    +        if sfaOpen in TmpNode.FoldAction then begin
               lvlB := lvl;
     
               if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
    -            inc(lvl);
    -          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
    +            inc(lvl)
    +          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    +          //if (FLastNode.LineIndex >= 0)
    +          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    +          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +          // inc(lvl);
     
               AddHighlight(TmpNode);
    -          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    +
    +          //if (FFoldColorInfosCount - 1 > 0)
    +          //and (FFoldColorInfos[FFoldColorInfosCount - 1].X = FFoldColorInfos[FFoldColorInfosCount - 2].X) then
    +          //begin
    +          //  // if child is on same x-pos keep level
    +          //  if (sfaClose in FFoldColorInfos[FFoldColorInfosCount - 1].SrcNode.FoldAction)
    +          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[FFoldColorInfosCount - 1].SrcNode.FoldAction) then begin
    +          //    lvl := FFoldColorInfos[FFoldColorInfosCount - 2].Level;
    +          //    FFoldColorInfos[FFoldColorInfosCount - 1].Level := lvl;
    +          //    FFoldColorInfos[FFoldColorInfosCount - 1].ColorIdx := Max(0, lvl) mod (length(Colors));
    +          //  end;
    +          //end;
    +
    +          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
    +          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
                 inc(lvl);
    +
               lvlA := lvl;
     
    -          with FHighlights[z] do begin
    +          if sfaOpen in TmpNode.FoldAction then
    +            FLastNode := TmpNode;
    +
    +          with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
                 LevelBefore := lvlB;
                 LevelAfter  := lvlA;
               end;
    -
    -        end
    -        else
    -        if sfaClose in TmpNode.FoldAction then
    -        begin
    +        end else if sfaClose in TmpNode.FoldAction then begin
               Found := False;
    -          for j := Length(FHighlights)-1 downto 0 do begin
    -            with FHighlights[j].SrcNode do begin
    -              if  (FoldType = TmpNode.FoldType) and
    -                (FoldGroup = TmpNode.FoldGroup) and
    -                (sfaOpen in FoldAction) and
    -                // (FoldLvlEnd = TmpNode.FoldLvlStart)
    -                (NestLvlEnd = TmpNode.NestLvlStart)
    -
    -                then begin
    -                  lvl := FHighlights[j].ColorIdx;
    -                  lvlB := FHighlights[j].LevelBefore;
    -                  Found := True;
    -                  break;
    -                end;
    +          for j := FFoldColorInfosCount - 1 downto 0 do begin
    +            with FFoldColorInfos[j].SrcNode do begin
    +              if (FoldType = TmpNode.FoldType)
    +              and (FoldGroup = TmpNode.FoldGroup)
    +              and (sfaOpen in FoldAction)
    +              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
    +                lvlB := lvl;
    +                lvl := FFoldColorInfos[j].Level;
    +                lvlA := FFoldColorInfos[j].LevelAfter;
    +                FLastNode := TmpNode;
    +                Found := True;
    +                break;
    +              end;
                 end;
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            lvl := lvlB;
    +            with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
    +              LevelBefore := lvlB;
    +              LevelAfter  := lvlA;
    +            end;
    +            // if found opening position is behind closing position:
    +            // delete this as it does not have to be drawn
    +            if FFoldColorInfos[j].X > FFoldColorInfos[FFoldColorInfosCount - 1].X then begin
    +              for j := j to FFoldColorInfosCount - 1 - 1 do begin
    +                FFoldColorInfos[j] := FFoldColorInfos[j+1];
    +              end;
    +              dec(FFoldColorInfosCount);
    +            end;
               end;
    -
    -          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
    -            //inc(lvl);
             end;
           end;
    -
           inc(i);
         until i >= NodeList.Count;
    -
       finally
         NodeList.ReleaseReference;
       end;
    @@ -469,197 +527,342 @@
     end;
     
     procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
    +var
    +  i, LastX, j: Integer;
    +
     begin
    -  CurrentY := aRow;
    -  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('PrepareMarkupForRow %d', [aRow]);
    +  {$ENDIF}
    +  if not Assigned(FHighlighter) then exit;
    +  if length(FFirstCharacterColumnCache) = 0 then begin
    +    // array should have been initialized due to a call to SetLines before
    +    // but with a cloned TextBuffer this is not the case
    +    // therefore init it here
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('!!! SetLines not called before PrepareMarkupForRow (cloned TextBuffer)!!!');
    +    {$ENDIF}
    +    InitCache;
    +  end;
    +  FPreparedRow := aRow;
    +  FFoldColorInfosCount := 0; //reset needed to prevent using of invalid area
     
       if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
         exit;
     
    -  //DoMarkupFoldAtRow(aRow);
    +  // invalidate fLastNode
    +  FLastNode.LineIndex := -1;
    +
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
    -  //DoMarkupRangeFoldAtRow(aRow);
     
    -  FHighlights := SortLeftMostFI(FHighlights);
    +  // delete parents with bigger x
    +  // to keep out mis indented blocks
    +  LastX := MaxInt;
    +  for i := FFoldColorInfosCount - 1 downto 0 do begin
    +    if FFoldColorInfos[i].X > LastX then begin
    +      for j := i to length(FFoldColorInfos) - 2 do begin
    +        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
    +      end;
    +      dec(FFoldColorInfosCount);
    +    end;
    +    LastX := FFoldColorInfos[i].X;
    +  end;
     end;
     
     procedure TSynEditMarkupFoldColors.EndMarkup;
     begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('EndMarkup');
    +  {$ENDIF}
       inherited EndMarkup;
    +  if not Assigned(FHighlighter) then exit;
       FNestList.Clear; // for next markup start
     end;
     
    -function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
    -begin
    -  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
    -end;
    -
     procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
     begin
       if FDefaultGroup = AValue then Exit;
       FDefaultGroup := AValue;
    -  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
    +  FNestList.FoldGroup := FDefaultGroup;
    +  FNestList2.FoldGroup := FDefaultGroup;
     end;
     
    -{.$define debug_FC_line_changed}
    -procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
    -  ACountDiff: Integer);
    -{$ifdef debug_FC_line_changed}
    -var F : TCustomForm;
    +procedure TSynEditMarkupFoldColors.SetCacheCount(pNewCount: Integer);
    +var
    +  i: Integer;
     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}
    +  if pNewCount > FCacheCapacity then begin
    +    // expand array
    +    FCacheCapacity := pNewCount + 900;
    +    SetLength(FFirstCharacterColumnCache, FCacheCapacity);
    +    SetLength(FEndLineCache, FCacheCapacity);
    +  end;
    +  if pNewCount > FCacheCount then begin
    +    // clear new section
    +    for i := FCacheCount to pNewCount - 1 do begin
    +      FFirstCharacterColumnCache[i] := 0;
    +      FEndLineCache[i] := 0;
    +    end;
    +  end;
    +  FCacheCount := pNewCount;
    +end;
     
    +procedure TSynEditMarkupFoldColors.InitCache;
    +begin
    +  if Assigned(FNestList) then begin
    +    FNestList.Lines := Lines;
    +    FNestList2.Lines := Lines;
    +  end;
    +  // set cache size
    +  SetCacheCount(Lines.Count);
    +  // add Changehandler
    +  Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +  Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +end;
     
    +procedure TSynEditMarkupFoldColors.ClearCache;
    +var
    +  i: Integer;
    +begin
    +  for i := 0 to FCacheCount - 1 do begin
    +    FFirstCharacterColumnCache[i] := 0;
    +    FEndLineCache[i] := 0;
    +  end;
    +end;
     
    -  function GetPairCloseFold(aRow, X : integer  ): Integer;
    +procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
    +  ACountDiff: Integer);
    +
    +  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
       var
    -    y,i,LCnt : integer;
    -    HL: TSynCustomFoldHighlighter;
    -    NodeList: TLazSynFoldNodeInfoList;
    -    TmpNode, CloseNode: TSynFoldNodeInfo;
    +    lCount, lLineIdx, i, lAnz: Integer;
    +    lNode: TSynFoldNodeInfo;
    +  begin
    +    lLineIdx := ToIdx(pLine);
    +    pNestList.Line := lLineIdx;
    +    lCount := pNestList.Count;
    +    SetLength(pList, lCount);
    +    lAnz := 0;
    +    for i := 0 to lCount - 1 do begin
    +      lNode := pNestList.HLNode[i];
    +      if (sfaInvalid in lNode.FoldAction)
    +      or (
    +        (sfaOpen in lNode.FoldAction)
    +        and (lNode.LineIndex = lLineIdx)
    +      ) then
    +        Continue;
     
    -    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;
    +      // hint: NodeEndLine for node is stored in NodeIndex
    +      lNode.NodeIndex := pNestList.NodeEndLine[i];
    +      pList[i] := lNode;
    +      inc(lAnz);
    +    end;
    +    SetLength(pList, lAnz);
    +  end;
     
    -    begin
    -      Result := SearchLine(YIndex, NIndex);
    -      if not (sfaInvalid in Result.FoldAction) then
    -        exit;
    +var
    +  i, lMinAnz, lEndLine, j, l: Integer;
    +  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
    +begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  {$ENDIF}
     
    -      inc(YIndex);
    -      while (YIndex < LCnt) and
    -            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
    -             > StartNode.NestLvlStart)
    -      do
    -        inc(YIndex);
    -      if YIndex = LCnt then
    -        exit;
    +  // lines available?
    +  if Lines.Count = 0 then
    +    exit;
     
    -      NIndex := -1;
    -      Result := SearchLine(YIndex, NIndex);
    +  // called by accident
    +  if StartLine = 0 then
    +    exit;
     
    -      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
    -        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
    -    end;
    +  // no TSynCustomFoldHighlighter
    +  if not Assigned(FHighlighter) then
    +    exit;
     
    -  begin
    -    Result := -1;
    -    y := aRow -1;
    +  FHighlighter.CurrentLines := Lines;
    +  if EndLine < 0 then
    +    EndLine := StartLine
    +  else
    +    // endline seems to be the first line after the change
    +    EndLine := EndLine - 1;
    +  lEndLine := EndLine;
     
    -    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
    +  SetLength(lStartNestList, 0);
    +  SetLength(lEndNestList, 0);
     
    -    NodeList := HL.FoldNodeInfo[y];
    -    NodeList.AddReference;
    -    try
    -      NodeList.ActionFilter := [sfaOpen];
    -      i := 0;
    -      repeat
    -        TmpNode := NodeList[i];
    +  FillNestList(lStartNestList, StartLine, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at Start:');
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -        if TmpNode.LogXStart < X-1 then
    -        begin
    -          inc(i);
    -          continue;
    -        end;
    +  FillNestList(lEndNestList, EndLine + 1, FNestList);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   Nodes at End:');
    +  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  {$ENDIF}
     
    -        //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;
    +  // delete all nodes in lEndNodeList which where active at StartLine
    +  // to get the nodes which reach behind EndLine
    +  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
    +  for i := 0 to lMinAnz - 1 do begin
    +    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
    +    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
    +    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
    +    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
    +    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
    +      for j := 0 to length(lEndNestList) - 2 do
    +        lEndNestList[j] := lEndNestList[j + 1];
    +      SetLength(lEndNestList, Length(lEndNestList) - 1);
    +    end else begin
    +      break
         end;
       end;
     
    +  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
    +    // deeper fold group than StartLine: fold group ends after EndLine
    +    // find real EndLine (end line of first remaining fold node)
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   Remaining Nodes:');
    +    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +    {$ENDIF}
    +    // does position of first character change for remaining node?
    +    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumnCache[lEndNestList[0].LineIndex] then
    +      // new: Field NodeIndex is used to store NodeEndLine for node see: FillNestList() above
    +      lEndLine := ToPos(lEndNestList[0].NodeIndex);
    +  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;
    +  // check for changes of endline for node which are active at StartLine
    +  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +    if sfaOutline in FoldAction then begin
    +      FNestList2.Line := LineIndex;
    +      l := ToPos(FNestList2.NodeEndLine[0]);
    +      if l <> FEndLineCache[LineIndex] then begin
    +        lEndLine := Max(lEndLine, Max(l, FEndLineCache[LineIndex]));
    +        FEndLineCache[LineIndex] := l;
    +        {$IFDEF SynEditMarkupFoldColoringDebug}
    +        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +        {$ENDIF}
           end;
         end;
     
    -    if n < 0 then exit;
    +  // invalidate cache
    +  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
    +    FFirstCharacterColumnCache[i] := 0;
    +    FEndLineCache[i] := 0;
    +  end;
     
    -    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);
    +  if lEndLine > EndLine then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
    +    {$ENDIF}
    +    InvalidateSynLines(EndLine + 1 , lEndLine);
    +  end;
    +end;
    +
    +procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
    +var
    +  old: TSynEditStrings;
    +begin
    +  old := Lines;
    +  if Assigned(old)
    +  and (AValue <> old) then begin
    +    // change:
    +    // remove Changehandler
    +    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +    ClearCache;
    +  end;
    +  inherited SetLines(AValue);
    +  if (AValue <> old) then begin
    +    // change:
    +    if Assigned(AValue) then begin
    +      InitCache;
    +    end else begin
    +      // clear cache
    +      SetCacheCount(0);
    +      if Assigned(FNestList) then begin
    +        FNestList.Lines := nil;
    +        FNestList2.Lines := nil;
    +      end;
         end;
    +  end;
    +end;
     
    -  end;
    +procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
    +                                        aIndex, aCount: Integer);
     var
    -  EndFoldLine,y : integer;
    +  absCount,
    +  idx, i: 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);
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  idx := ToIdx(aIndex);
    +  if (aCount < 0)
    +  and (idx >= 0) then begin
    +    // lines deleted
    +    absCount := Abs(aCount);
    +    for i := idx to Length(FFirstCharacterColumnCache) - 1 - absCount do begin
    +      FFirstCharacterColumnCache[i] := FFirstCharacterColumnCache[i + absCount];
    +      FEndLineCache[i] := FEndLineCache[i + absCount];
    +    end;
       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}
    +  SetCacheCount(Sender.Count);
    +  if (aCount > 0) then begin
    +    if idx >= 0 then begin
    +      // lines added
    +      for i := Length(FFirstCharacterColumnCache) - 1 - aCount downto idx do begin
    +        FFirstCharacterColumnCache[i + aCount] := FFirstCharacterColumnCache[i];
    +        FEndLineCache[i + aCount] := FEndLineCache[i];
    +      end;
    +      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumnCache) - 1) do begin
    +        FFirstCharacterColumnCache[i] := 0;
    +        FEndLineCache[i] := 0;
    +      end;
    +    end else begin
    +      // first lines will be inserted
    +      for i := 0 to Length(FFirstCharacterColumnCache) - 1 do begin
    +        FFirstCharacterColumnCache[i] := 0;
    +        FEndLineCache[i] := 0;
    +      end;
    +    end;
    +  end;
     end;
     
    -procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
    -var Y : integer;
    +procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
    +  aIndex, aCount: Integer);
    +var
    +  newHighlighter: TSynCustomHighlighter;
     begin
    -  Y := Caret.LineBytePos.y;
    -  if Y = FCaretY then exit;
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
    +  {$ENDIF}
    +  if (aIndex <> -1)
    +  or (aCount <> -1) then
    +    exit;
     
    -  FCaretY := Y;
    -  FPrevCaretText := Caret.LineText;
    -end;
    +  newHighlighter := TCustomSynEdit(self.SynEdit).Highlighter;
    +  if Assigned(newHighlighter)
    +  and not (newHighlighter is TSynCustomFoldHighlighter) then
    +    newHighlighter := nil;
     
    +  if (newHighlighter = FHighlighter) then
    +    exit;
     
    +  FHighlighter := TSynCustomFoldHighlighter(newHighlighter);
     
    +  FNestList.HighLighter := FHighlighter;
    +  FNestList2.HighLighter := FHighlighter;
    +
    +  ClearCache;
    +end;
    +
     end.
     
    +
    
  • syneditmarkupfoldcoloring.pas_v16.patch (12,115 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 54012)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -92,6 +92,8 @@
         Colors : array of TColor;
         FPreparedRow: integer;
         FLastNode: TSynFoldNodeInfo;
    +    FLastEnabled: Boolean;
    +
         procedure DoMarkupParentFoldAtRow(aRow: Integer);
         procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
         procedure SetDefaultGroup(AValue: integer);
    @@ -106,6 +108,7 @@
         procedure SetLines(const AValue: TSynEditStrings); override;
         procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
         procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
    +    procedure DoEnabledChanged(Sender: TObject); override;
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    @@ -124,7 +127,13 @@
     
     implementation
     uses
    -  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs;
    +  SynEdit,
    +  SynEditTypes,
    +  SynEditMiscProcs,
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  SynHighlighterPas,
    +  {$endif}
    +  Dialogs;
     
     
     {$IFDEF SynEditMarkupFoldColoringDebug}
    @@ -271,6 +280,9 @@
     procedure TSynEditMarkupFoldColors.TextBufferChanged(Sender: TSynEditStrings;
       aIndex, aCount: Integer);
     begin
    +  if not Enabled then
    +    exit;
    +
       InitCache;
     end;
     
    @@ -535,15 +547,6 @@
       //DebugLn('PrepareMarkupForRow %d', [aRow]);
       {$ENDIF}
       if not Assigned(FHighlighter) then exit;
    -//  if length(FFirstCharacterColumnCache) = 0 then begin
    -//    // array should have been initialized due to a call to SetLines before
    -//    // but with a cloned TextBuffer this is not the case
    -//    // therefore init it here
    -//    {$IFDEF SynEditMarkupFoldColoringDebug}
    -//    DebugLn('!!! SetLines not called before PrepareMarkupForRow (cloned TextBuffer)!!!');
    -//    {$ENDIF}
    -//    InitCache;
    -//  end;
       FPreparedRow := aRow;
       FFoldColorInfosCount := 0; //reset needed to prevent using of invalid area
     
    @@ -608,8 +611,6 @@
     end;
     
     procedure TSynEditMarkupFoldColors.SetFoldColorInfosCount(pNewCount: Integer);
    -var
    -  i: Integer;
     begin
       if pNewCount > FFoldColorInfosCapacity then begin
         // expand array
    @@ -671,8 +672,11 @@
       i, lMinAnz, lEndLine, j, l: Integer;
       lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
     begin
    +  if not Enabled then
    +    exit;
    +
       {$IFDEF SynEditMarkupFoldColoringDebug}
    -  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
    +  DebugLn('   DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
       {$ENDIF}
     
       // lines available?
    @@ -688,6 +692,10 @@
         exit;
     
       FHighlighter.CurrentLines := Lines;
    +  // highlighter still scanning
    +  if FHighlighter.NeedScan then
    +    exit;
    +
       if EndLine < 0 then
         EndLine := StartLine
       else
    @@ -700,16 +708,16 @@
     
       FillNestList(lStartNestList, StartLine, FNestList);
       {$IFDEF SynEditMarkupFoldColoringDebug}
    -  DebugLn('   Nodes at Start:');
    -  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    -    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  //DebugLn('   Nodes at Start:');
    +  //for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    +  //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
       {$ENDIF}
     
       FillNestList(lEndNestList, EndLine + 1, FNestList);
       {$IFDEF SynEditMarkupFoldColoringDebug}
    -  DebugLn('   Nodes at End:');
    -  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    -    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  //DebugLn('   Nodes at End:');
    +  //for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +  //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
       {$ENDIF}
     
       // delete all nodes in lEndNodeList which where active at StartLine
    @@ -733,9 +741,9 @@
         // deeper fold group than StartLine: fold group ends after EndLine
         // find real EndLine (end line of first remaining fold node)
         {$IFDEF SynEditMarkupFoldColoringDebug}
    -    DebugLn('   Remaining Nodes:');
    -    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    -      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +    //DebugLn('   Remaining Nodes:');
    +    //for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    +    //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
         {$ENDIF}
         // does position of first character change for remaining node?
         if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumnCache[lEndNestList[0].LineIndex] then
    @@ -752,7 +760,7 @@
             lEndLine := Max(lEndLine, Max(l, FEndLineCache[LineIndex]));
             FEndLineCache[LineIndex] := l;
             {$IFDEF SynEditMarkupFoldColoringDebug}
    -        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +        //DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
             {$ENDIF}
           end;
         end;
    @@ -769,6 +777,8 @@
         {$ENDIF}
         InvalidateSynLines(EndLine + 1 , lEndLine);
       end;
    +
    +  FNestList.Clear;
     end;
     
     procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
    @@ -775,30 +785,34 @@
     var
       old: TSynEditStrings;
     begin
    -  old := Lines;
    -  if Assigned(old)
    -  and (AValue <> old) then begin
    -    // change:
    -    // remove Changehandler
    -    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    -    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    -    old.RemoveChangeHandler(senrTextBufferChanged, @TextBufferChanged);
    -    ClearCache;
    +  if Enabled then begin
    +    old := Lines;
    +    if Assigned(old)
    +    and (AValue <> old) then begin
    +      // change:
    +      // remove Changehandler
    +      old.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +      old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +      old.RemoveChangeHandler(senrTextBufferChanged, @TextBufferChanged);
    +      ClearCache;
    +    end;
       end;
       inherited SetLines(AValue);
    -  if (AValue <> old) then begin
    -    // change:
    -    if Assigned(AValue) then begin
    -      // add Changehandler
    -      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    -      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    -      AValue.AddChangeHandler(senrTextBufferChanged, @TextBufferChanged);
    -      InitCache;
    -    end else begin
    -      // clear cache
    -      SetCacheCount(0);
    -      if Assigned(FNestList) then
    -        FNestList.Lines := nil;
    +  if Enabled then begin
    +    if (AValue <> old) then begin
    +      // change:
    +      if Assigned(AValue) then begin
    +        // add Changehandler
    +        AValue.AddChangeHandler(senrLineCount, @LinesChanged);
    +        AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +        AValue.AddChangeHandler(senrTextBufferChanged, @TextBufferChanged);
    +        InitCache;
    +      end else begin
    +        // clear cache
    +        SetCacheCount(0);
    +        if Assigned(FNestList) then
    +          FNestList.Lines := nil;
    +      end;
         end;
       end;
     end;
    @@ -809,9 +823,13 @@
       absCount,
       idx, i: Integer;
     begin
    +  if not Enabled then
    +    exit;
    +
       {$IFDEF SynEditMarkupFoldColoringDebug}
       DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
       {$ENDIF}
    +
       idx := ToIdx(aIndex);
       if (aCount < 0)
       and (idx >= 0) then begin
    @@ -849,9 +867,13 @@
     var
       newHighlighter: TSynCustomHighlighter;
     begin
    +  if not Enabled then
    +    exit;
    +
       {$IFDEF SynEditMarkupFoldColoringDebug}
       DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
       {$ENDIF}
    +
       if (aIndex <> -1)
       or (aCount <> -1) then
         exit;
    @@ -871,6 +893,36 @@
       ClearCache;
     end;
     
    +procedure TSynEditMarkupFoldColors.DoEnabledChanged(Sender: TObject);
    +begin
    +  if Enabled = FLastEnabled then
    +    exit;
    +  FLastEnabled := Enabled;
    +  if FLastEnabled then begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   *** TSynEditMarkupFoldColors Enabled');
    +    {$ENDIF}
    +    if Assigned(Lines) then begin
    +      // add Changehandler
    +      Lines.AddChangeHandler(senrLineCount, @LinesChanged);
    +      Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
    +      Lines.AddChangeHandler(senrTextBufferChanged, @TextBufferChanged);
    +      InitCache;
    +    end;
    +  end else begin
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    DebugLn('   *** TSynEditMarkupFoldColors Disabled');
    +    {$ENDIF}
    +    if Assigned(Lines) then begin
    +      // remove Changehandler
    +      Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
    +      Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
    +      Lines.RemoveChangeHandler(senrTextBufferChanged, @TextBufferChanged);
    +      ClearCache;
    +    end;
    +  end;
    +end;
    +
     end.
     
     
    
  • syneditmarkupfoldcoloring.pas_v17.patch (2,359 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 54022)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -112,6 +112,7 @@
       public
         constructor Create(ASynEdit : TSynEditBase);
         destructor Destroy; override;
    +    procedure BeginMarkup; override;
         function GetMarkupAttributeAtRowCol(const aRow: Integer;
                                             const aStartCol: TLazSynDisplayTokenBound;
                                             const {%H-}AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor; override;
    @@ -121,7 +122,6 @@
                                              out   ANextPhys, ANextLog: Integer); override;
     
         procedure PrepareMarkupForRow(aRow : Integer); override;
    -    procedure EndMarkup; override;
         property DefaultGroup : integer read FDefaultGroup write SetDefaultGroup;
       end;
     
    @@ -194,6 +194,17 @@
       inherited Destroy;
     end;
     
    +procedure TSynEditMarkupFoldColors.BeginMarkup;
    +begin
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  //DebugLn('BeginMarkup');
    +  {$ENDIF}
    +  inherited BeginMarkup;
    +  if not Assigned(FHighlighter) then
    +    exit;
    +  FNestList.Clear; // for next markup start
    +end;
    +
     function TSynEditMarkupFoldColors.GetMarkupAttributeAtRowCol(
       const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
       const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
    @@ -573,16 +584,6 @@
       end;
     end;
     
    -procedure TSynEditMarkupFoldColors.EndMarkup;
    -begin
    -  {$IFDEF SynEditMarkupFoldColoringDebug}
    -  //DebugLn('EndMarkup');
    -  {$ENDIF}
    -  inherited EndMarkup;
    -  if not Assigned(FHighlighter) then exit;
    -  FNestList.Clear; // for next markup start
    -end;
    -
     procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
     begin
       if FDefaultGroup = AValue then Exit;
    @@ -867,9 +868,6 @@
     var
       newHighlighter: TSynCustomHighlighter;
     begin
    -  if not Enabled then
    -    exit;
    -
       {$IFDEF SynEditMarkupFoldColoringDebug}
       DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
       {$ENDIF}
    @@ -890,6 +888,9 @@
     
       FNestList.HighLighter := FHighlighter;
     
    +  if not Enabled then
    +    exit;
    +
       ClearCache;
     end;
     
    @@ -921,6 +922,8 @@
           ClearCache;
         end;
       end;
    +  if Assigned(Lines) then
    +    InvalidateSynLines(0, Lines.Count - 1);
     end;
     
     end.
    
  • syneditmarkupfoldcoloring.pas_v18.patch (9,155 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 54035)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -132,6 +132,7 @@
       SynEditMiscProcs,
       {$IFDEF SynEditMarkupFoldColoringDebug}
       SynHighlighterPas,
    +  strutils,
       {$endif}
       Dialogs;
     
    @@ -362,6 +363,9 @@
       i := 0;
       while i < NestCount do begin
         TmpNode := FNestList.HLNode[i];
    +    {$IFDEF SynEditMarkupFoldColoringDebug}
    +    //DebugLn('  O: %s %s %s', [IfThen(sfaOutline in TmpNode.FoldAction, 'X', '-'), IfThen(sfaClose in TmpNode.FoldAction, 'C ', IfThen(sfaOpen in TmpNode.FoldAction, 'O ', '??')),FoldTypeToStr(TmpNode.FoldType)]);
    +    {$ENDIF}
         if (sfaOutline in TmpNode.FoldAction)
         and not (sfaInvalid in TmpNode.FoldAction) then
           //avoid bug of IncludeOpeningOnLine := False;
    @@ -418,16 +422,22 @@
       var x,j : integer;
       begin
         x  := ANode.LogXStart + 1;
    -    if ANode.LogXStart < ANode.LogXEnd then
    -    for j := 0 to FFoldColorInfosCount - 1 do
    -      if (FFoldColorInfos[j].X = x)
    -      and (FFoldColorInfos[j].Border)
    -      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    -      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
    -      then begin
    -       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    -       FFoldColorInfos[j].Border := False
    -      end;
    +    if ANode.LogXStart < ANode.LogXEnd then begin
    +      {$IFDEF SynEditMarkupFoldColoringDebug}
    +      //DebugLn('    %d < %d', [ANode.LogXStart, ANode.LogXEnd]);
    +      {$ENDIF}
    +      for j := 0 to FFoldColorInfosCount - 1 do
    +        if (FFoldColorInfos[j].X = x)
    +        and (FFoldColorInfos[j].Border)
    +        and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
    +        and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart ) then begin
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //DebugLn('      X2: %d->%d', [FFoldColorInfos[j].X2, ANode.LogXEnd + 1]);
    +          {$ENDIF}
    +          FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
    +          FFoldColorInfos[j].Border := False
    +        end;
    +    end;
     
         SetFoldColorInfosCount(FFoldColorInfosCount + 1);
         with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
    @@ -467,6 +477,11 @@
         repeat
           TmpNode := NodeList[i];
     
    +      {$IFDEF SynEditMarkupFoldColoringDebug}
    +      //if not (sfaInvalid in TmpNode.FoldAction) then
    +      //  DebugLn('  C: %s %s', [IfThen(sfaClose in TmpNode.FoldAction, 'C ', IfThen(sfaOpen in TmpNode.FoldAction, 'O ', '??')),FoldTypeToStr(TmpNode.FoldType)]);
    +      {$ENDIF}
    +
           if not (sfaInvalid in TmpNode.FoldAction)
           and (sfaOutline in TmpNode.FoldAction) then begin
             if sfaOpen in TmpNode.FoldAction then begin
    @@ -482,6 +497,10 @@
               // inc(lvl);
     
               AddHighlight(TmpNode);
    +          {$IFDEF SynEditMarkupFoldColoringDebug}
    +          //with FFoldColorInfos[FFoldColorInfosCount - 1] do
    +          //  DebugLn('    %d-%d', [x, x2]);
    +          {$ENDIF}
     
               //if (FFoldColorInfosCount - 1 > 0)
               //and (FFoldColorInfos[FFoldColorInfosCount - 1].X = FFoldColorInfos[FFoldColorInfosCount - 2].X) then
    @@ -527,6 +546,10 @@
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    +            {$IFDEF SynEditMarkupFoldColoringDebug}
    +            //with FFoldColorInfos[FFoldColorInfosCount - 1] do
    +            //  DebugLn('    %d-%d', [x, x2]);
    +            {$ENDIF}
                 with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
                   LevelBefore := lvlB;
                   LevelAfter  := lvlA;
    @@ -570,6 +593,12 @@
       DoMarkupParentFoldAtRow(aRow);
       DoMarkupParentCloseFoldAtRow(aRow);
     
    +  {$IFDEF SynEditMarkupFoldColoringDebug}
    +  for i := 0 to FFoldColorInfosCount - 1 do with FFoldColorInfos[i] do begin
    +    DebugLn('  %.5d %.2d-%.2d: %d - %s %s', [y, x, X2, ColorIdx, IfThen(sfaClose in SrcNode.FoldAction, 'C ', IfThen(sfaOpen in SrcNode.FoldAction, 'O ', '??')),FoldTypeToStr(SrcNode.FoldType)]);
    +  end;
    +  {$ENDIF}
    +
       // delete parents with bigger x
       // to keep out mis indented blocks
       LastX := MaxInt;
    @@ -713,7 +742,7 @@
       {$IFDEF SynEditMarkupFoldColoringDebug}
       //DebugLn('   Nodes at Start:');
       //for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
    -  //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, ToPos(NodeIndex), ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
       {$ENDIF}
     
       FillNestList(lEndNestList, EndLine + 1, FNestList);
    @@ -720,7 +749,7 @@
       {$IFDEF SynEditMarkupFoldColoringDebug}
       //DebugLn('   Nodes at End:');
       //for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    -  //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +  //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, ToPos(NodeIndex), ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
       {$ENDIF}
     
       // delete all nodes in lEndNodeList which where active at StartLine
    @@ -746,7 +775,7 @@
         {$IFDEF SynEditMarkupFoldColoringDebug}
         //DebugLn('   Remaining Nodes:');
         //for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
    -    //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +    //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, ToPos(NodeIndex), ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
         {$ENDIF}
         // does position of first character change for remaining node?
         if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumnCache[lEndNestList[0].LineIndex] then
    @@ -763,7 +792,7 @@
             lEndLine := Max(lEndLine, Max(l, FEndLineCache[LineIndex]));
             FEndLineCache[LineIndex] := l;
             {$IFDEF SynEditMarkupFoldColoringDebug}
    -        //DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
    +        //DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, ToPos(NodeIndex), ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
             {$ENDIF}
           end;
         end;
    
  • syneditmarkupfoldcoloring.pas_v19.patch (2,658 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 54045)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -439,18 +439,30 @@
             end;
         end;
     
    -    SetFoldColorInfosCount(FFoldColorInfosCount + 1);
    -    with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
    -      Border := False;
    -      SrcNode:= ANode; //needed by close node
    -      Y  := ANode.LineIndex + 1;
    -      X  := ANode.LogXStart + 1;
    -      X2 := ANode.LogXEnd + 1;
    -      Level := lvl;
    -      if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := Max(0, lvl) mod (length(Colors))
    -      else
    -         ColorIdx := -1;
    +    if (ANode.LogXStart = ANode.LogXEnd)
    +    and (ANode.LogXStart = length(Lines.Strings[ANode.LineIndex]))  then begin
    +      {$IFDEF SynEditMarkupFoldColoringDebug}
    +      DebugLn('    implicit close token at line end: FoldType=%s', [FoldTypeToStr(ANode.FoldType)]);
    +      {$ENDIF}
    +      // ignore implicit close nodes at end of line, especially if line is empty
    +      // or at least has less characters as vertical line is on
    +    end else begin
    +      SetFoldColorInfosCount(FFoldColorInfosCount + 1);
    +      with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
    +        Border := False;
    +        SrcNode:= ANode; //needed by close node
    +        Y  := ANode.LineIndex + 1;
    +        X  := ANode.LogXStart + 1;
    +        X2 := ANode.LogXEnd + 1;
    +        Level := lvl;
    +        if not (sfaOutlineNocolor in ANode.FoldAction) then
    +           ColorIdx := Max(0, lvl) mod (length(Colors))
    +        else
    +           ColorIdx := -1;
    +        {$IFDEF SynEditMarkupFoldColoringDebug}
    +        //  DebugLn('    %d-%d', [x, x2]);
    +        {$ENDIF}
    +      end;
         end;
       end;
     
    @@ -497,10 +509,6 @@
               // inc(lvl);
     
               AddHighlight(TmpNode);
    -          {$IFDEF SynEditMarkupFoldColoringDebug}
    -          //with FFoldColorInfos[FFoldColorInfosCount - 1] do
    -          //  DebugLn('    %d-%d', [x, x2]);
    -          {$ENDIF}
     
               //if (FFoldColorInfosCount - 1 > 0)
               //and (FFoldColorInfos[FFoldColorInfosCount - 1].X = FFoldColorInfos[FFoldColorInfosCount - 2].X) then
    @@ -546,10 +554,6 @@
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            {$IFDEF SynEditMarkupFoldColoringDebug}
    -            //with FFoldColorInfos[FFoldColorInfosCount - 1] do
    -            //  DebugLn('    %d-%d', [x, x2]);
    -            {$ENDIF}
                 with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
                   LevelBefore := lvlB;
                   LevelAfter  := lvlA;
    
  • syneditmarkupfoldcoloring.pas_v20.patch (7,764 bytes)
    Index: syneditmarkupfoldcoloring.pas
    ===================================================================
    --- syneditmarkupfoldcoloring.pas	(revision 54049)
    +++ syneditmarkupfoldcoloring.pas	(working copy)
    @@ -176,12 +176,13 @@
       MarkupInfo.StyleMask := [];
       MarkupInfo.FrameEdges:= sfeLeft;
     
    -  SetLength(Colors, 5);
    +  SetLength(Colors, 6);
       Colors[0] := clRed;
       Colors[1] := $000098F7; //orange
       Colors[2] := $0022CC40; //green
    -  Colors[3] := $00FF682A; //blue
    -  Colors[4] := $00CF00C4; //purple
    +  Colors[3] := $00CCCC00;   //cyan
    +  Colors[4] := $00FF682A; //blue
    +  Colors[5] := $00CF00C4; //purple
     end;
     
     destructor TSynEditMarkupFoldColors.Destroy;
    @@ -379,22 +380,22 @@
               inc(lvl)
             else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
               dec(lvl);
    -        //if (FLastNode.LineIndex >= 0)
    -        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    -        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    -        // inc(lvl);
    +        if (FLastNode.LineIndex >= 0)
    +        and (sfaOutlineKeepLevel in FLastNode.FoldAction)
    +        and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +         inc(lvl);
     
             AddVerticalLine(TmpNode, i);
     
    -        //if (FFoldColorInfosCount - 1 > 0)
    -        //and (FFoldColorInfos[FFoldColorInfosCount - 1].X = FFoldColorInfos[FFoldColorInfosCount - 2].X) then begin
    -        //  // if child is on same x-pos keep level
    -        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[FFoldColorInfosCount - 1].SrcNode.FoldAction then begin
    -        //    lvl := FFoldColorInfos[FFoldColorInfosCount - 2].Level;
    -        //    FFoldColorInfos[FFoldColorInfosCount - 1].Level := lvl;
    -        //    FFoldColorInfos[FFoldColorInfosCount - 1].ColorIdx := Max(0, lvl) mod (length(Colors));
    -        //  end;
    -        //end;
    +        if (FFoldColorInfosCount - 1 > 0)
    +        and (FFoldColorInfos[FFoldColorInfosCount - 1].X = FFoldColorInfos[FFoldColorInfosCount - 2].X) then begin
    +          // if child is on same x-pos keep level
    +          if sfaOutlineKeepLevel in FFoldColorInfos[FFoldColorInfosCount - 2].SrcNode.FoldAction then begin
    +            lvl := FFoldColorInfos[FFoldColorInfosCount - 2].Level;
    +            FFoldColorInfos[FFoldColorInfosCount - 1].Level := lvl;
    +            FFoldColorInfos[FFoldColorInfosCount - 1].ColorIdx := Max(0, lvl) mod (length(Colors));
    +          end;
    +        end;
     
             if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
             {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    @@ -439,18 +440,30 @@
             end;
         end;
     
    -    SetFoldColorInfosCount(FFoldColorInfosCount + 1);
    -    with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
    -      Border := False;
    -      SrcNode:= ANode; //needed by close node
    -      Y  := ANode.LineIndex + 1;
    -      X  := ANode.LogXStart + 1;
    -      X2 := ANode.LogXEnd + 1;
    -      Level := lvl;
    -      if not (sfaOutlineNocolor in ANode.FoldAction) then
    -         ColorIdx := Max(0, lvl) mod (length(Colors))
    -      else
    -         ColorIdx := -1;
    +    if (ANode.LogXStart = ANode.LogXEnd)
    +    and (ANode.LogXStart = length(Lines.Strings[ANode.LineIndex]))  then begin
    +      {$IFDEF SynEditMarkupFoldColoringDebug}
    +      DebugLn('    implicit close token at line end: FoldType=%s', [FoldTypeToStr(ANode.FoldType)]);
    +      {$ENDIF}
    +      // ignore implicit close nodes at end of line, especially if line is empty
    +      // or at least has less characters as vertical line is on
    +    end else begin
    +      SetFoldColorInfosCount(FFoldColorInfosCount + 1);
    +      with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
    +        Border := False;
    +        SrcNode:= ANode; //needed by close node
    +        Y  := ANode.LineIndex + 1;
    +        X  := ANode.LogXStart + 1;
    +        X2 := ANode.LogXEnd + 1;
    +        Level := lvl;
    +        if not (sfaOutlineNocolor in ANode.FoldAction) then
    +           ColorIdx := Max(0, lvl) mod (length(Colors))
    +        else
    +           ColorIdx := -1;
    +        {$IFDEF SynEditMarkupFoldColoringDebug}
    +        //  DebugLn('    %d-%d', [x, x2]);
    +        {$ENDIF}
    +      end;
         end;
       end;
     
    @@ -478,8 +491,8 @@
           TmpNode := NodeList[i];
     
           {$IFDEF SynEditMarkupFoldColoringDebug}
    -      //if not (sfaInvalid in TmpNode.FoldAction) then
    -      //  DebugLn('  C: %s %s', [IfThen(sfaClose in TmpNode.FoldAction, 'C ', IfThen(sfaOpen in TmpNode.FoldAction, 'O ', '??')),FoldTypeToStr(TmpNode.FoldType)]);
    +      if not (sfaInvalid in TmpNode.FoldAction) then
    +        DebugLn('  C: %s %s %s', [IfThen(sfaOutline in TmpNode.FoldAction, 'X', '-'), IfThen(sfaClose in TmpNode.FoldAction, 'C ', IfThen(sfaOpen in TmpNode.FoldAction, 'O ', '??')),FoldTypeToStr(TmpNode.FoldType)]);
           {$ENDIF}
     
           if not (sfaInvalid in TmpNode.FoldAction)
    @@ -491,28 +504,24 @@
                 inc(lvl)
               else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
                 dec(lvl);
    -          //if (FLastNode.LineIndex >= 0)
    -          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
    -          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
    -          // inc(lvl);
    +          if (FLastNode.LineIndex >= 0)
    +          and (sfaOutlineKeepLevel in FLastNode.FoldAction)
    +          and (FLastNode.LineIndex < TmpNode.LineIndex) then
    +           inc(lvl);
     
               AddHighlight(TmpNode);
    -          {$IFDEF SynEditMarkupFoldColoringDebug}
    -          //with FFoldColorInfos[FFoldColorInfosCount - 1] do
    -          //  DebugLn('    %d-%d', [x, x2]);
    -          {$ENDIF}
     
    -          //if (FFoldColorInfosCount - 1 > 0)
    -          //and (FFoldColorInfos[FFoldColorInfosCount - 1].X = FFoldColorInfos[FFoldColorInfosCount - 2].X) then
    -          //begin
    -          //  // if child is on same x-pos keep level
    -          //  if (sfaClose in FFoldColorInfos[FFoldColorInfosCount - 1].SrcNode.FoldAction)
    -          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[FFoldColorInfosCount - 1].SrcNode.FoldAction) then begin
    -          //    lvl := FFoldColorInfos[FFoldColorInfosCount - 2].Level;
    -          //    FFoldColorInfos[FFoldColorInfosCount - 1].Level := lvl;
    -          //    FFoldColorInfos[FFoldColorInfosCount - 1].ColorIdx := Max(0, lvl) mod (length(Colors));
    -          //  end;
    -          //end;
    +          if (FFoldColorInfosCount - 1 > 0)
    +          and (FFoldColorInfos[FFoldColorInfosCount - 1].X = FFoldColorInfos[FFoldColorInfosCount - 2].X) then
    +          begin
    +            // if child is on same x-pos keep level
    +            if (sfaClose in FFoldColorInfos[FFoldColorInfosCount - 1].SrcNode.FoldAction)
    +            or (sfaOutlineKeepLevel in FFoldColorInfos[FFoldColorInfosCount - 2].SrcNode.FoldAction) then begin
    +              lvl := FFoldColorInfos[FFoldColorInfosCount - 2].Level;
    +              FFoldColorInfos[FFoldColorInfosCount - 1].Level := lvl;
    +              FFoldColorInfos[FFoldColorInfosCount - 1].ColorIdx := Max(0, lvl) mod (length(Colors));
    +            end;
    +          end;
     
               if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
               {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
    @@ -546,10 +555,6 @@
               end;
               if Found then begin
                 AddHighlight(TmpNode);
    -            {$IFDEF SynEditMarkupFoldColoringDebug}
    -            //with FFoldColorInfos[FFoldColorInfosCount - 1] do
    -            //  DebugLn('    %d-%d', [x, x2]);
    -            {$ENDIF}
                 with FFoldColorInfos[FFoldColorInfosCount - 1] do begin
                   LevelBefore := lvlB;
                   LevelAfter  := lvlA;
    @@ -953,7 +958,7 @@
         end;
       end;
       if Assigned(Lines) then
    -    InvalidateSynLines(0, Lines.Count - 1);
    +    InvalidateSynLines(1, Lines.Count);
     end;
     
     end.
    

Relationships

parent of 0030420 closedMartin Friebe upgrade to Pascal highlighter: fold node for "else" part 

Activities

Pascal Riekenberg

2016-07-29 15:16

reporter  

syneditmarkupfoldcoloring.pas.patch (28,670 bytes)
Index: syneditmarkupfoldcoloring.pas
===================================================================
--- syneditmarkupfoldcoloring.pas	(revision 52753)
+++ syneditmarkupfoldcoloring.pas	(working copy)
@@ -55,7 +55,8 @@
 
 uses
   Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
-  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
+  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
+  LazSynEditText, fgl;
 
 type
 
@@ -66,37 +67,38 @@
     Border  : Boolean;
     Ignore  : Boolean; //no color no line
     SrcNode : TSynFoldNodeInfo;
-    LevelBefore, LevelAfter : integer;//needed by non nest nodes
+    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
   end;
 
   TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
   TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
-
   { TSynEditMarkupFoldColors }
 
   TSynEditMarkupFoldColors = class(TSynEditMarkup)
   private
+    FHighlighter: TSynCustomHighlighter;
     FNestList: TLazSynEditNestedFoldsList;
+    FFirstCharacterColumn: Array of Byte;
     FDefaultGroup: integer;
      // Physical Position
-    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
+    FHighlights: TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
     Colors : array of TColor;
 
-    {%region invalidating}
-    CurrentY : integer;  //??
-    FCaretY : integer;    // flag identify for refresh begin______
-    FPrevCaretText : string;  // flag identify for refresh begin______
-    {%endregion}
+    FCurrentRow: integer;  // prepared Row
 
+    FLastNode: TSynFoldNodeInfo;
     procedure DoMarkupParentFoldAtRow(aRow: Integer);
     procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
 
     function GetFoldHighLighter: TSynCustomFoldHighlighter;
     procedure SetDefaultGroup(AValue: integer);
+    procedure SetHighlighter(AValue: TSynCustomHighlighter);
   protected
     // Notifications about Changes to the text
     procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
-    procedure DoCaretChanged(Sender: TObject); override;
+
+    procedure SetLines(const AValue: TSynEditStrings); override;
+    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
   public
     constructor Create(ASynEdit : TSynEditBase);
     destructor Destroy; override;
@@ -111,12 +113,54 @@
     procedure PrepareMarkupForRow(aRow : Integer); override;
     procedure EndMarkup; override;
     property DefaultGroup : integer read FDefaultGroup write SetDefaultGroup;
+    property  Highlighter: TSynCustomHighlighter
+      read FHighlighter write SetHighlighter;
   end;
 
 implementation
 uses
-  SynEdit,SynEditTypes, SynEditMiscProcs;
+  SynEdit,SynEditTypes, SynEditMiscProcs, Dialogs, strutils;
 
+function FoldTypeToStr(p_FoldType: Pointer): String;
+begin
+  case PtrInt(p_FoldType) of
+    00: Result := 'cfbtBeginEnd      ';
+    01: Result := 'cfbtTopBeginEnd   ';
+    02: Result := 'cfbtNestedComment ';
+    03: Result := 'cfbtProcedure     ';
+    04: Result := 'cfbtUses          ';
+    05: Result := 'cfbtVarType       ';
+    06: Result := 'cfbtLocalVarType  ';
+    07: Result := 'cfbtClass         ';
+    08: Result := 'cfbtClassSection  ';
+    09: Result := 'cfbtUnitSection   ';
+    10: Result := 'cfbtProgram       ';
+    11: Result := 'cfbtUnit          ';
+    12: Result := 'cfbtRecord        ';
+    13: Result := 'cfbtTry           ';
+    14: Result := 'cfbtExcept        ';
+    15: Result := 'cfbtRepeat        ';
+    16: Result := 'cfbtAsm           ';
+    17: Result := 'cfbtCase          ';
+    18: Result := 'cfbtIfDef         ';
+    19: Result := 'cfbtRegion        ';
+    20: Result := 'cfbtAnsiComment   ';
+    21: Result := 'cfbtBorCommand    ';
+    22: Result := 'cfbtSlashComment  ';
+    23: Result := 'cfbtIfThen        ';
+    24: Result := 'cfbtForDo         ';
+    25: Result := 'cfbtWhileDo       ';
+    26: Result := 'cfbtWithDo        ';
+    27: Result := 'cfbtIfElse        ';
+    28: Result := '// Internal type  ';
+    29: Result := 'cfbtCaseElse (int)';
+    30: Result := 'cfbtPackage (int) ';
+    31: Result := 'cfbtNone (int)    ';
+    else Result := Format('unknown %.2d        ', [PtrInt(p_FoldType)]);
+  end;
+end;
+
+
   {%region Sorting FoldInfo -fold}
   function CompareFI(Item1, Item2: Pointer): Integer;
   begin
@@ -143,6 +187,12 @@
       result[i] := PMarkupFoldColorInfo(l[i])^;
      l.Free;
   end;
+
+  operator = (z1, z2: TSynFoldNodeInfo): boolean;
+  begin
+      Result := false;
+  end;
+
   {%endregion}
 
 { TSynEditMarkupFoldColors }
@@ -151,6 +201,9 @@
 begin
   inherited Create(ASynEdit);
 
+  if Assigned(Lines) then
+    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
+
   FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
   FNestList.ResetFilter;
   FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
@@ -157,6 +210,8 @@
   FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
   FNestList.IncludeOpeningOnLine := True; //False; //
 
+  SetLength(FFirstCharacterColumn, 0);
+
   MarkupInfo.Foreground := clGreen;
   MarkupInfo.Background := clNone; //clFuchsia;
   MarkupInfo.Style := [];
@@ -174,6 +229,8 @@
 
 destructor TSynEditMarkupFoldColors.Destroy;
 begin
+  if Assigned(Lines) then
+    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
   FreeAndNil(FNestList);
   inherited Destroy;
 end;
@@ -182,12 +239,15 @@
   const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
   const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
 var
-  i,x2both : integer;
+  i,x2both , found, color: integer;
 begin
   Result := nil;
-  if (CurrentY = aRow) then begin
+  if (FCurrentRow = aRow) then begin
+    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
 
     x2both := -3; //flag
+    //found := -1;
+    //color := -1;
     for i := 0 to length(FHighlights)-1 do
       with FHighlights[i] do
         if not Ignore
@@ -194,11 +254,21 @@
         and (X < X2)
         and (ColorIdx >= 0)
         and (aStartCol.Logical >= x)
-        and (aStartCol.Logical < X2) then
-        begin
+        and (aStartCol.Logical < X2) then begin
+          //if (found >= 0)
+          //and (found = x)
+          //and (
+          //  (ToPos(SrcNode.LineIndex) <> aRow)
+          //  or not (sfaOpen in SrcNode.FoldAction)
+          //) then
+          //  Continue;
+          //
+          //if x > found then
+          //  found := -1;
+
+          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
           //MarkupInfo.FrameColor:= clGreen; //debug
-          if x2both = -3 then //first call flag
-          begin
+          if x2both = -3 then begin //first call flag
             MarkupInfo.FrameColor:= clNone;
             MarkupInfo.Foreground:= clNone;
             MarkupInfo.Background:= clNone;
@@ -209,13 +279,16 @@
           Result := MarkupInfo;
           x2both := max(x2both, x2);
           MarkupInfo.SetFrameBoundsLog(x, x2both);
-          if Border then
-          begin
-            MarkupInfo.FrameColor:= Colors[ColorIdx];
+          //if found < 0 then
+            color := Colors[ColorIdx];
+          if Border then begin
+            MarkupInfo.FrameColor:= color;
             MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
-          end
-          else
-            MarkupInfo.Foreground := Colors[ColorIdx];
+          end else begin
+            MarkupInfo.FrameColor:= clNone;
+            MarkupInfo.FrameEdges:= sfeNone;
+            MarkupInfo.Foreground := color;
+          end;
 
           //MarkupInfo.FrameEdges:= sfeAround; //debug
 
@@ -227,7 +300,7 @@
             MarkupInfo.FrameColor:= clBlue; //debug
           end;}
 
-          //break;
+          //found := x;
         end;
   end;
 end;
@@ -237,9 +310,10 @@
   const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
 var i : integer;
 begin
+  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
   ANextLog := -1;
   ANextPhys := -1;
-  if (CurrentY = aRow)  then
+  if (FCurrentRow = aRow)  then
   for i := 0 to length(FHighlights)-1  do
     with FHighlights[i] do
     begin
@@ -258,16 +332,42 @@
   i,lvl,z : integer; //iterate parents fold
 
   procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
-  var x,j : integer;
+  var
+    j, p, s: integer;
+    l: String;
+
   begin
+    // get column of first character in row
+      // Fallback if Highlighter doesn't populate ColumnOfFirstCharInRow
+    s := Length(FFirstCharacterColumn);
+    if (s <= ANode.LineIndex)
+    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
+      l := SynEdit.Lines[ANode.LineIndex];
+      p := 1;
+      while not (l[p] in [#13, #10, #0])
+      and (l[p] = #32) do inc(p);
+      if p > 255 then p := 255;
+      if s > ANode.LineIndex then begin
+        FFirstCharacterColumn[ANode.LineIndex] := p;
+      end else begin
+        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
     z := Length(FHighlights);
     SetLength(FHighlights, z+1);
     with FHighlights[z] do begin
+
       SrcNode:= ANode; //needed by close node
-      Border := ANode.LineIndex + 1 <> aRow;
-      X  := ANode.LogXStart + 1;
+      Border := ToPos(ANode.LineIndex) <> aRow;
+      //X  := ANode.LogXStart + 1;
+      //X  := p;
+      if s <= ANode.LineIndex then
+        X  := p
+      else
+        X  := FFirstCharacterColumn[ANode.LineIndex];
       Y  := aRow;//ANode.LineIndex + 1;
-      X2 := X+1; //ANode.LogXEnd + 1;
+      X2 := X + 1; //ANode.LogXEnd + 1;
       Ignore := False;
 
       if Border and (sfaOutlineNoLine in ANode.FoldAction) then
@@ -274,7 +374,9 @@
         Ignore := True;
       if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
         Ignore := True;
-        ColorIdx := lvl mod (length(Colors))
+      Level := lvl;
+      ColorIdx := Max(0, lvl) mod (length(Colors));
+      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
     end;
   end;
 
@@ -292,7 +394,7 @@
   end;
 
 begin
-  y := aRow-1;
+  y := ToIdx(aRow);
   FNestList.Line := y;
   NestCount := FNestList.Count;
 
@@ -318,14 +420,33 @@
       lvlB := lvl;
 
       if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-        inc(lvl);
-      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+        inc(lvl)
+      else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
         dec(lvl);
+      if (FLastNode.LineIndex >= 0)
+      and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+      and (FLastNode.LineIndex < TmpNode.LineIndex) then
+       inc(lvl);
 
       AddVerticalLine(TmpNode);
-      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
+
+      if (z > 0)
+      and (FHighlights[z].X = FHighlights[z - 1].X) then begin
+        // if child is on same x-pos keep level
+        if sfaOutlineMergeLevelOnWrongCol in FHighlights[z].SrcNode.FoldAction then begin
+          lvl := FHighlights[z - 1].Level;
+          FHighlights[z].Level := lvl;
+          FHighlights[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+        end;
+      end;
+
+      if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+      and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction) then
         inc(lvl);
 
+      if sfaOpen in TmpNode.FoldAction then
+        FLastNode := TmpNode;
+
       lvlA := lvl;
 
       with FHighlights[z] do begin
@@ -370,9 +491,9 @@
       Y  := ANode.LineIndex + 1;
       X  := ANode.LogXStart + 1;
       X2 := ANode.LogXEnd + 1;
-      //ColorIdx := lvl;
+      Level := lvl;
       if not (sfaOutlineNocolor in ANode.FoldAction) then
-         ColorIdx := lvl mod (length(Colors))
+         ColorIdx := Max(0, lvl) mod (length(Colors))
       else
          ColorIdx := -1;
     end;
@@ -382,10 +503,10 @@
   y,i,j,lvlB,lvlA : integer;
   HL: TSynCustomFoldHighlighter;
   NodeList: TLazSynFoldNodeInfoList;
-  TmpNode: TSynFoldNodeInfo;
+  TmpNode, PrevNode: TSynFoldNodeInfo;
   Found : boolean;
 begin
-  y := aRow -1;
+  y := ToIdx(aRow);
 
   HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
   HL.CurrentLines := Lines;
@@ -401,6 +522,7 @@
     if J >=0 then
       lvl := max(0,FHighlights[J].LevelAfter);
     i := 0;
+    PrevNode.LineIndex := -2;
     repeat
       TmpNode := NodeList[i];
 
@@ -416,15 +538,37 @@
           lvlB := lvl;
 
           if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-            inc(lvl);
-          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+            inc(lvl)
+          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
             dec(lvl);
+          if (FLastNode.LineIndex >= 0)
+          and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+          and (FLastNode.LineIndex < TmpNode.LineIndex) then
+           inc(lvl);
 
           AddHighlight(TmpNode);
-          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
+
+          if (z > 0)
+          and (FHighlights[z].X = FHighlights[z - 1].X) then
+          begin
+            // if child is on same x-pos keep level
+            if (sfaClose in FHighlights[z].SrcNode.FoldAction)
+            or (sfaOutlineMergeLevelOnWrongCol in FHighlights[z].SrcNode.FoldAction) then begin
+              lvl := FHighlights[z - 1].Level;
+              FHighlights[z].Level := lvl;
+              FHighlights[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+            end;
+          end;
+
+          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+          and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction) then
             inc(lvl);
+
           lvlA := lvl;
 
+          if sfaOpen in TmpNode.FoldAction then
+            FLastNode := TmpNode;
+
           with FHighlights[z] do begin
             LevelBefore := lvlB;
             LevelAfter  := lvlA;
@@ -437,28 +581,50 @@
           Found := False;
           for j := Length(FHighlights)-1 downto 0 do begin
             with FHighlights[j].SrcNode do begin
-              if  (FoldType = TmpNode.FoldType) and
-                (FoldGroup = TmpNode.FoldGroup) and
-                (sfaOpen in FoldAction) and
-                // (FoldLvlEnd = TmpNode.FoldLvlStart)
-                (NestLvlEnd = TmpNode.NestLvlStart)
-
-                then begin
-                  lvl := FHighlights[j].ColorIdx;
-                  lvlB := FHighlights[j].LevelBefore;
-                  Found := True;
-                  break;
-                end;
+              if (FoldType = TmpNode.FoldType)
+              and (FoldGroup = TmpNode.FoldGroup)
+              and (sfaOpen in FoldAction)
+              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
+                lvlB := lvl;
+                lvl := FHighlights[j].Level;
+                lvlA := FHighlights[j].LevelAfter;
+                FLastNode := TmpNode;
+                Found := True;
+                break;
+              end;
             end;
           end;
           if Found then begin
+            //FLastNode.LineIndex := -1;
+            //for j := j - 1 downto 0 do begin
+            //  with FHighlights[j].SrcNode do begin
+            //    if (sfaOpen in FoldAction) then begin
+            //      FLastNode := FHighlights[j].SrcNode;
+            //      Break;
+            //    end;
+            //  end;
+            //end;
             AddHighlight(TmpNode);
-            lvl := lvlB;
+            //lvl := lvlA;
+            with FHighlights[z] do begin
+              LevelBefore := lvlB;
+              LevelAfter  := lvlA;
+            end;
+            // if found opening position is behind closing position:
+            // delete this as it does not have to be drawn
+            if FHighlights[j].X > FHighlights[z].X then begin
+              for j := j to z - 1 do begin
+                FHighlights[j] := FHighlights[j+1];
+              end;
+              SetLength(FHighlights, z);
+            end;
           end;
 
           //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
             //inc(lvl);
         end;
+
+        PrevNode := TmpNode;
       end;
 
       inc(i);
@@ -470,23 +636,49 @@
 end;
 
 procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
+var
+  i, li, LastX, j: Integer;
+  n: TMarkupFoldColorInfos;
+  l: TList;
 begin
-  CurrentY := aRow;
+  //DebugLn('PrepareMarkupForRow %d', [aRow]);
+  FCurrentRow := aRow;
   SetLength(FHighlights,0); //reset needed to prevent using of invalid area
 
   if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
     exit;
 
+  FLastNode.LineIndex := -1;
+
   //DoMarkupFoldAtRow(aRow);
   DoMarkupParentFoldAtRow(aRow);
   DoMarkupParentCloseFoldAtRow(aRow);
   //DoMarkupRangeFoldAtRow(aRow);
 
-  FHighlights := SortLeftMostFI(FHighlights);
+  //FHighlights := SortLeftMostFI(FHighlights);
+
+  // delete parents with bigger x
+  // to keep out mis indented blocks
+  LastX := MaxInt;
+  for i := length(FHighlights) - 1 downto 0 do begin
+    if FHighlights[i].X > LastX then begin
+      for j := i to length(FHighlights) - 2 do begin
+        FHighlights[j] := FHighlights[j + 1];
+      end;
+      SetLength(FHighlights, length(FHighlights) - 1);
+    end;
+    LastX := FHighlights[i].X;
+  end;
+
+  //DebugLn('Zeile %d', [aRow]);
+  //for i := 0 to length(FHighlights) - 1 do with FHighlights[i] do begin
+  //  DebugLn('   %.2d: x=%.03d l=%.5d %s %s %s %s lvl=%.2d - %.2d - %.2d', [i, X, ToPos(SrcNode.LineIndex), IfThen(sfaOpen in SrcNode.FoldAction, 'O', IfThen(sfaClose in SrcNode.FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in SrcNode.FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in SrcNode.FoldAction, '+', IfThen(sfaOutlineMergeParent in SrcNode.FoldAction, '-', ' ')) ,FoldTypeToStr(SrcNode.FoldType), LevelBefore, Level, LevelAfter]);
+  //end;
 end;
 
 procedure TSynEditMarkupFoldColors.EndMarkup;
 begin
+  //DebugLn('EndMarkup');
   inherited EndMarkup;
   FNestList.Clear; // for next markup start
 end;
@@ -503,164 +695,180 @@
   FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
 end;
 
-{.$define debug_FC_line_changed}
-procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
-  ACountDiff: Integer);
-{$ifdef debug_FC_line_changed}
-var F : TCustomForm;
+procedure TSynEditMarkupFoldColors.SetHighlighter(AValue: TSynCustomHighlighter
+  );
 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}
+  if FHighlighter = AValue then Exit;
+  FHighlighter := AValue;
 
+  if not (FHighlighter is TSynCustomFoldHighlighter) then
+    exit;
 
+  FreeAndNil(FNestList);
 
-  function GetPairCloseFold(aRow, X : integer  ): Integer;
-  var
-    y,i,LCnt : integer;
-    HL: TSynCustomFoldHighlighter;
-    NodeList: TLazSynFoldNodeInfoList;
-    TmpNode, CloseNode: TSynFoldNodeInfo;
+  FNestList := TLazSynEditNestedFoldsList.Create(Lines, TSynCustomFoldHighlighter(FHighlighter));
+  FNestList.ResetFilter;
+  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
+  FNestList.IncludeOpeningOnLine := True; //False; //
+end;
 
-    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;
+procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
+  ACountDiff: Integer);
 
-    begin
-      Result := SearchLine(YIndex, NIndex);
-      if not (sfaInvalid in Result.FoldAction) then
-        exit;
+  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
+  var
+    lCount, lLineIdx, i, lAnz: Integer;
+    lNode: TSynFoldNodeInfo;
+  begin
+    pNestList.Line := pLine;
+    lCount := pNestList.Count;
+    SetLength(pList, lCount);
+    lLineIdx := ToIdx(pLine);
+    lAnz := 0;
+    for i := 0 to lCount - 1 do begin
+      lNode := pNestList.HLNode[i];
+      if (sfaInvalid in lNode.FoldAction)
+      or (
+        (sfaOpen in lNode.FoldAction)
+        and (lNode.LineIndex = lLineIdx)
+      ) then
+        Continue;
 
-      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?)
+      pList[i] := lNode;
+      inc(lAnz);
     end;
+    SetLength(pList, lAnz);
+  end;
 
-  begin
-    Result := -1;
-    y := aRow -1;
+var
+  i, lMinAnz, lEndLine, j: integer;
+  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
+  HL: TSynCustomFoldHighlighter;
+  lNodeList: TLazSynFoldNodeInfoList;
+  lNode: TSynFoldNodeInfo;
+  found: Boolean;
 
-    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
+begin
+  //DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
+  if EndLine < 0 then
+    EndLine := StartLine;
 
-    NodeList := HL.FoldNodeInfo[y];
-    NodeList.AddReference;
-    try
-      NodeList.ActionFilter := [sfaOpen];
-      i := 0;
-      repeat
-        TmpNode := NodeList[i];
+  for i := ToIdx(StartLine) to ToIdx(EndLine) do
+    FFirstCharacterColumn[i] := 0;
 
-        if TmpNode.LogXStart < X-1 then
-        begin
-          inc(i);
-          continue;
-        end;
+  lEndLine := EndLine;
 
-        //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;
+  FillNestList(lStartNestList, StartLine, FNestList);
+  FillNestList(lEndNestList, EndLine, FNestList);
 
-        inc(i);
-      until i >= NodeList.Count;
-
-    finally
-      NodeList.ReleaseReference;
+  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
+  for i := 0 to lMinAnz - 1 do begin
+    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
+    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
+    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
+    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
+    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
+      for j := 0 to length(lEndNestList) - 2 do
+        lEndNestList[j] := lEndNestList[j + 1];
+      SetLength(lEndNestList, Length(lEndNestList) - 1);
+    end else begin
+      break
     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;
+  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
+    // deeper fold group than StartLine: fold group ends after EndLine
+    // find real EndLine (end line of first remaining fold node)
+    //DebugLn('   x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
+    HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
+    HL.CurrentLines := Lines;
+    found := false;
+    repeat
+      inc(lEndLine);
+      if lEndLine > Lines.Count then break;
+      HL.FoldNodeInfo[ToIdx(lEndLine)].ClearFilter;
+      lNodeList := HL.FoldNodeInfo[ToIdx(lEndLine)];
+      lNodeList.AddReference;
+      try
+        lNodeList.ActionFilter := [sfaOutline, sfaClose];
+        i := 0;
+        repeat
+          lNode := lNodeList[i];
+          if not (sfaInvalid in lNode.FoldAction)
+          and (lNode.FoldType = lEndNestList[0].FoldType)
+          and (lNode.FoldGroup = lEndNestList[0].FoldGroup)
+          and (lNode.NestLvlEnd = lEndNestList[0].NestLvlStart) then begin
+            found := true;
+            break;
+          end;
+          inc(i);
+        until (i >= lNodeList.Count) or found;
+      finally
+        lNodeList.ReleaseReference;
       end;
-    end;
+    until found;
 
-    if n < 0 then exit;
+  end;
+  if lEndLine > EndLine then begin
+    //DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
+    InvalidateSynLines(EndLine + 1, lEndLine);
+  end;
+end;
 
-    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;
+procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
 var
-  EndFoldLine,y : integer;
+  old: TSynEditStrings;
 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);
+  old := Lines;
+  if Assigned(old)
+  and (AValue <> old) then begin
+    // Änderung:
+    // Remove Changehandler
+    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
   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}
+  inherited SetLines(AValue);
+  if (AValue <> old) then begin
+    // Änderung:
+    if Assigned(AValue) then begin
+      // Add Changehandler
+      SetLength(FFirstCharacterColumn, AValue.Count);
+      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
+    end else begin
+      SetLength(FFirstCharacterColumn, 0);
+    end;
+  end;
 end;
 
-procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
-var Y : integer;
+procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
+                                        aIndex, aCount: Integer);
+var
+  absCount,
+  idx, i: Integer;
 begin
-  Y := Caret.LineBytePos.y;
-  if Y = FCaretY then exit;
-
-  FCaretY := Y;
-  FPrevCaretText := Caret.LineText;
+  //DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  idx := ToIdx(aIndex);
+  if aCount < 0 then begin
+    // Zeilen entfernt
+    absCount := Abs(aCount);
+    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
+      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
+    end;
+  end;
+  SetLength(FFirstCharacterColumn, Sender.Count);
+  if (aCount > 0) then begin
+    if idx >= 0 then begin
+      // Zeilen eingefügt
+      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
+        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
+      end;
+      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do FFirstCharacterColumn[i] := 0;
+    end else begin
+      // erste Zeilen werden eingefügt
+      for i := 0 to Length(FFirstCharacterColumn) - 1 do FFirstCharacterColumn[i] := 0;
+    end;
+  end;
 end;
 
-
-
 end.
 

Pascal Riekenberg

2016-07-30 07:00

reporter  

syneditmarkupfoldcoloring.pas_v2.patch (29,678 bytes)
Index: syneditmarkupfoldcoloring.pas
===================================================================
--- syneditmarkupfoldcoloring.pas	(revision 52753)
+++ syneditmarkupfoldcoloring.pas	(working copy)
@@ -55,7 +55,8 @@
 
 uses
   Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
-  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
+  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
+  LazSynEditText, fgl;
 
 type
 
@@ -66,37 +67,38 @@
     Border  : Boolean;
     Ignore  : Boolean; //no color no line
     SrcNode : TSynFoldNodeInfo;
-    LevelBefore, LevelAfter : integer;//needed by non nest nodes
+    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
   end;
 
   TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
   TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
-
   { TSynEditMarkupFoldColors }
 
   TSynEditMarkupFoldColors = class(TSynEditMarkup)
   private
+    FHighlighter: TSynCustomHighlighter;
     FNestList: TLazSynEditNestedFoldsList;
+    FFirstCharacterColumn: Array of Byte;
     FDefaultGroup: integer;
      // Physical Position
-    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
+    FHighlights: TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
     Colors : array of TColor;
 
-    {%region invalidating}
-    CurrentY : integer;  //??
-    FCaretY : integer;    // flag identify for refresh begin______
-    FPrevCaretText : string;  // flag identify for refresh begin______
-    {%endregion}
+    FCurrentRow: integer;  // prepared Row
 
+    FLastNode: TSynFoldNodeInfo;
     procedure DoMarkupParentFoldAtRow(aRow: Integer);
     procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
 
     function GetFoldHighLighter: TSynCustomFoldHighlighter;
     procedure SetDefaultGroup(AValue: integer);
+    procedure SetHighlighter(AValue: TSynCustomHighlighter);
   protected
     // Notifications about Changes to the text
     procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
-    procedure DoCaretChanged(Sender: TObject); override;
+
+    procedure SetLines(const AValue: TSynEditStrings); override;
+    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
   public
     constructor Create(ASynEdit : TSynEditBase);
     destructor Destroy; override;
@@ -111,12 +113,54 @@
     procedure PrepareMarkupForRow(aRow : Integer); override;
     procedure EndMarkup; override;
     property DefaultGroup : integer read FDefaultGroup write SetDefaultGroup;
+    property  Highlighter: TSynCustomHighlighter
+      read FHighlighter write SetHighlighter;
   end;
 
 implementation
 uses
-  SynEdit,SynEditTypes, SynEditMiscProcs;
+  SynEdit,SynEditTypes, SynEditMiscProcs, Dialogs, strutils;
 
+function FoldTypeToStr(p_FoldType: Pointer): String;
+begin
+  case PtrInt(p_FoldType) of
+    00: Result := 'cfbtBeginEnd      ';
+    01: Result := 'cfbtTopBeginEnd   ';
+    02: Result := 'cfbtNestedComment ';
+    03: Result := 'cfbtProcedure     ';
+    04: Result := 'cfbtUses          ';
+    05: Result := 'cfbtVarType       ';
+    06: Result := 'cfbtLocalVarType  ';
+    07: Result := 'cfbtClass         ';
+    08: Result := 'cfbtClassSection  ';
+    09: Result := 'cfbtUnitSection   ';
+    10: Result := 'cfbtProgram       ';
+    11: Result := 'cfbtUnit          ';
+    12: Result := 'cfbtRecord        ';
+    13: Result := 'cfbtTry           ';
+    14: Result := 'cfbtExcept        ';
+    15: Result := 'cfbtRepeat        ';
+    16: Result := 'cfbtAsm           ';
+    17: Result := 'cfbtCase          ';
+    18: Result := 'cfbtIfDef         ';
+    19: Result := 'cfbtRegion        ';
+    20: Result := 'cfbtAnsiComment   ';
+    21: Result := 'cfbtBorCommand    ';
+    22: Result := 'cfbtSlashComment  ';
+    23: Result := 'cfbtIfThen        ';
+    24: Result := 'cfbtForDo         ';
+    25: Result := 'cfbtWhileDo       ';
+    26: Result := 'cfbtWithDo        ';
+    27: Result := 'cfbtIfElse        ';
+    28: Result := '// Internal type  ';
+    29: Result := 'cfbtCaseElse (int)';
+    30: Result := 'cfbtPackage (int) ';
+    31: Result := 'cfbtNone (int)    ';
+    else Result := Format('unknown %.2d        ', [PtrInt(p_FoldType)]);
+  end;
+end;
+
+
   {%region Sorting FoldInfo -fold}
   function CompareFI(Item1, Item2: Pointer): Integer;
   begin
@@ -143,6 +187,12 @@
       result[i] := PMarkupFoldColorInfo(l[i])^;
      l.Free;
   end;
+
+  operator = (z1, z2: TSynFoldNodeInfo): boolean;
+  begin
+      Result := false;
+  end;
+
   {%endregion}
 
 { TSynEditMarkupFoldColors }
@@ -151,6 +201,9 @@
 begin
   inherited Create(ASynEdit);
 
+  if Assigned(Lines) then
+    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
+
   FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
   FNestList.ResetFilter;
   FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
@@ -157,6 +210,8 @@
   FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
   FNestList.IncludeOpeningOnLine := True; //False; //
 
+  SetLength(FFirstCharacterColumn, 0);
+
   MarkupInfo.Foreground := clGreen;
   MarkupInfo.Background := clNone; //clFuchsia;
   MarkupInfo.Style := [];
@@ -174,6 +229,8 @@
 
 destructor TSynEditMarkupFoldColors.Destroy;
 begin
+  if Assigned(Lines) then
+    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
   FreeAndNil(FNestList);
   inherited Destroy;
 end;
@@ -182,12 +239,15 @@
   const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
   const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
 var
-  i,x2both : integer;
+  i,x2both , found, color: integer;
 begin
   Result := nil;
-  if (CurrentY = aRow) then begin
+  if (FCurrentRow = aRow) then begin
+    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
 
     x2both := -3; //flag
+    //found := -1;
+    //color := -1;
     for i := 0 to length(FHighlights)-1 do
       with FHighlights[i] do
         if not Ignore
@@ -194,11 +254,21 @@
         and (X < X2)
         and (ColorIdx >= 0)
         and (aStartCol.Logical >= x)
-        and (aStartCol.Logical < X2) then
-        begin
+        and (aStartCol.Logical < X2) then begin
+          //if (found >= 0)
+          //and (found = x)
+          //and (
+          //  (ToPos(SrcNode.LineIndex) <> aRow)
+          //  or not (sfaOpen in SrcNode.FoldAction)
+          //) then
+          //  Continue;
+          //
+          //if x > found then
+          //  found := -1;
+
+          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
           //MarkupInfo.FrameColor:= clGreen; //debug
-          if x2both = -3 then //first call flag
-          begin
+          if x2both = -3 then begin //first call flag
             MarkupInfo.FrameColor:= clNone;
             MarkupInfo.Foreground:= clNone;
             MarkupInfo.Background:= clNone;
@@ -209,13 +279,16 @@
           Result := MarkupInfo;
           x2both := max(x2both, x2);
           MarkupInfo.SetFrameBoundsLog(x, x2both);
-          if Border then
-          begin
-            MarkupInfo.FrameColor:= Colors[ColorIdx];
+          //if found < 0 then
+            color := Colors[ColorIdx];
+          if Border then begin
+            MarkupInfo.FrameColor:= color;
             MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
-          end
-          else
-            MarkupInfo.Foreground := Colors[ColorIdx];
+          end else begin
+            MarkupInfo.FrameColor:= clNone;
+            MarkupInfo.FrameEdges:= sfeNone;
+            MarkupInfo.Foreground := color;
+          end;
 
           //MarkupInfo.FrameEdges:= sfeAround; //debug
 
@@ -227,7 +300,7 @@
             MarkupInfo.FrameColor:= clBlue; //debug
           end;}
 
-          //break;
+          //found := x;
         end;
   end;
 end;
@@ -237,9 +310,10 @@
   const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
 var i : integer;
 begin
+  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
   ANextLog := -1;
   ANextPhys := -1;
-  if (CurrentY = aRow)  then
+  if (FCurrentRow = aRow)  then
   for i := 0 to length(FHighlights)-1  do
     with FHighlights[i] do
     begin
@@ -258,16 +332,42 @@
   i,lvl,z : integer; //iterate parents fold
 
   procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
-  var x,j : integer;
+  var
+    j, p, s: integer;
+    l: String;
+
   begin
+    // get column of first character in row
+      // Fallback if Highlighter doesn't populate ColumnOfFirstCharInRow
+    s := Length(FFirstCharacterColumn);
+    if (s <= ANode.LineIndex)
+    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
+      l := SynEdit.Lines[ANode.LineIndex];
+      p := 1;
+      while not (l[p] in [#13, #10, #0])
+      and (l[p] = #32) do inc(p);
+      if p > 255 then p := 255;
+      if s > ANode.LineIndex then begin
+        FFirstCharacterColumn[ANode.LineIndex] := p;
+      end else begin
+        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
     z := Length(FHighlights);
     SetLength(FHighlights, z+1);
     with FHighlights[z] do begin
+
       SrcNode:= ANode; //needed by close node
-      Border := ANode.LineIndex + 1 <> aRow;
-      X  := ANode.LogXStart + 1;
+      Border := ToPos(ANode.LineIndex) <> aRow;
+      //X  := ANode.LogXStart + 1;
+      //X  := p;
+      if s <= ANode.LineIndex then
+        X  := p
+      else
+        X  := FFirstCharacterColumn[ANode.LineIndex];
       Y  := aRow;//ANode.LineIndex + 1;
-      X2 := X+1; //ANode.LogXEnd + 1;
+      X2 := X + 1; //ANode.LogXEnd + 1;
       Ignore := False;
 
       if Border and (sfaOutlineNoLine in ANode.FoldAction) then
@@ -274,7 +374,9 @@
         Ignore := True;
       if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
         Ignore := True;
-        ColorIdx := lvl mod (length(Colors))
+      Level := lvl;
+      ColorIdx := Max(0, lvl) mod (length(Colors));
+      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
     end;
   end;
 
@@ -292,7 +394,7 @@
   end;
 
 begin
-  y := aRow-1;
+  y := ToIdx(aRow);
   FNestList.Line := y;
   NestCount := FNestList.Count;
 
@@ -318,14 +420,33 @@
       lvlB := lvl;
 
       if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-        inc(lvl);
-      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+        inc(lvl)
+      else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
         dec(lvl);
+      if (FLastNode.LineIndex >= 0)
+      and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+      and (FLastNode.LineIndex < TmpNode.LineIndex) then
+       inc(lvl);
 
       AddVerticalLine(TmpNode);
-      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
+
+      if (z > 0)
+      and (FHighlights[z].X = FHighlights[z - 1].X) then begin
+        // if child is on same x-pos keep level
+        if sfaOutlineMergeLevelOnWrongCol in FHighlights[z].SrcNode.FoldAction then begin
+          lvl := FHighlights[z - 1].Level;
+          FHighlights[z].Level := lvl;
+          FHighlights[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+        end;
+      end;
+
+      if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+      and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction) then
         inc(lvl);
 
+      if sfaOpen in TmpNode.FoldAction then
+        FLastNode := TmpNode;
+
       lvlA := lvl;
 
       with FHighlights[z] do begin
@@ -370,9 +491,9 @@
       Y  := ANode.LineIndex + 1;
       X  := ANode.LogXStart + 1;
       X2 := ANode.LogXEnd + 1;
-      //ColorIdx := lvl;
+      Level := lvl;
       if not (sfaOutlineNocolor in ANode.FoldAction) then
-         ColorIdx := lvl mod (length(Colors))
+         ColorIdx := Max(0, lvl) mod (length(Colors))
       else
          ColorIdx := -1;
     end;
@@ -382,10 +503,10 @@
   y,i,j,lvlB,lvlA : integer;
   HL: TSynCustomFoldHighlighter;
   NodeList: TLazSynFoldNodeInfoList;
-  TmpNode: TSynFoldNodeInfo;
+  TmpNode, PrevNode: TSynFoldNodeInfo;
   Found : boolean;
 begin
-  y := aRow -1;
+  y := ToIdx(aRow);
 
   HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
   HL.CurrentLines := Lines;
@@ -401,6 +522,7 @@
     if J >=0 then
       lvl := max(0,FHighlights[J].LevelAfter);
     i := 0;
+    PrevNode.LineIndex := -2;
     repeat
       TmpNode := NodeList[i];
 
@@ -416,15 +538,37 @@
           lvlB := lvl;
 
           if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-            inc(lvl);
-          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+            inc(lvl)
+          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
             dec(lvl);
+          if (FLastNode.LineIndex >= 0)
+          and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+          and (FLastNode.LineIndex < TmpNode.LineIndex) then
+           inc(lvl);
 
           AddHighlight(TmpNode);
-          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
+
+          if (z > 0)
+          and (FHighlights[z].X = FHighlights[z - 1].X) then
+          begin
+            // if child is on same x-pos keep level
+            if (sfaClose in FHighlights[z].SrcNode.FoldAction)
+            or (sfaOutlineMergeLevelOnWrongCol in FHighlights[z].SrcNode.FoldAction) then begin
+              lvl := FHighlights[z - 1].Level;
+              FHighlights[z].Level := lvl;
+              FHighlights[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+            end;
+          end;
+
+          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+          and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction) then
             inc(lvl);
+
           lvlA := lvl;
 
+          if sfaOpen in TmpNode.FoldAction then
+            FLastNode := TmpNode;
+
           with FHighlights[z] do begin
             LevelBefore := lvlB;
             LevelAfter  := lvlA;
@@ -437,28 +581,50 @@
           Found := False;
           for j := Length(FHighlights)-1 downto 0 do begin
             with FHighlights[j].SrcNode do begin
-              if  (FoldType = TmpNode.FoldType) and
-                (FoldGroup = TmpNode.FoldGroup) and
-                (sfaOpen in FoldAction) and
-                // (FoldLvlEnd = TmpNode.FoldLvlStart)
-                (NestLvlEnd = TmpNode.NestLvlStart)
-
-                then begin
-                  lvl := FHighlights[j].ColorIdx;
-                  lvlB := FHighlights[j].LevelBefore;
-                  Found := True;
-                  break;
-                end;
+              if (FoldType = TmpNode.FoldType)
+              and (FoldGroup = TmpNode.FoldGroup)
+              and (sfaOpen in FoldAction)
+              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
+                lvlB := lvl;
+                lvl := FHighlights[j].Level;
+                lvlA := FHighlights[j].LevelAfter;
+                FLastNode := TmpNode;
+                Found := True;
+                break;
+              end;
             end;
           end;
           if Found then begin
+            //FLastNode.LineIndex := -1;
+            //for j := j - 1 downto 0 do begin
+            //  with FHighlights[j].SrcNode do begin
+            //    if (sfaOpen in FoldAction) then begin
+            //      FLastNode := FHighlights[j].SrcNode;
+            //      Break;
+            //    end;
+            //  end;
+            //end;
             AddHighlight(TmpNode);
-            lvl := lvlB;
+            //lvl := lvlA;
+            with FHighlights[z] do begin
+              LevelBefore := lvlB;
+              LevelAfter  := lvlA;
+            end;
+            // if found opening position is behind closing position:
+            // delete this as it does not have to be drawn
+            if FHighlights[j].X > FHighlights[z].X then begin
+              for j := j to z - 1 do begin
+                FHighlights[j] := FHighlights[j+1];
+              end;
+              SetLength(FHighlights, z);
+            end;
           end;
 
           //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
             //inc(lvl);
         end;
+
+        PrevNode := TmpNode;
       end;
 
       inc(i);
@@ -470,23 +636,49 @@
 end;
 
 procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
+var
+  i, li, LastX, j: Integer;
+  n: TMarkupFoldColorInfos;
+  l: TList;
 begin
-  CurrentY := aRow;
+  //DebugLn('PrepareMarkupForRow %d', [aRow]);
+  FCurrentRow := aRow;
   SetLength(FHighlights,0); //reset needed to prevent using of invalid area
 
   if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
     exit;
 
+  FLastNode.LineIndex := -1;
+
   //DoMarkupFoldAtRow(aRow);
   DoMarkupParentFoldAtRow(aRow);
   DoMarkupParentCloseFoldAtRow(aRow);
   //DoMarkupRangeFoldAtRow(aRow);
 
-  FHighlights := SortLeftMostFI(FHighlights);
+  //FHighlights := SortLeftMostFI(FHighlights);
+
+  // delete parents with bigger x
+  // to keep out mis indented blocks
+  LastX := MaxInt;
+  for i := length(FHighlights) - 1 downto 0 do begin
+    if FHighlights[i].X > LastX then begin
+      for j := i to length(FHighlights) - 2 do begin
+        FHighlights[j] := FHighlights[j + 1];
+      end;
+      SetLength(FHighlights, length(FHighlights) - 1);
+    end;
+    LastX := FHighlights[i].X;
+  end;
+
+  //DebugLn('Zeile %d', [aRow]);
+  //for i := 0 to length(FHighlights) - 1 do with FHighlights[i] do begin
+  //  DebugLn('   %.2d: x=%.03d l=%.5d %s %s %s %s lvl=%.2d - %.2d - %.2d', [i, X, ToPos(SrcNode.LineIndex), IfThen(sfaOpen in SrcNode.FoldAction, 'O', IfThen(sfaClose in SrcNode.FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in SrcNode.FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in SrcNode.FoldAction, '+', IfThen(sfaOutlineMergeParent in SrcNode.FoldAction, '-', ' ')) ,FoldTypeToStr(SrcNode.FoldType), LevelBefore, Level, LevelAfter]);
+  //end;
 end;
 
 procedure TSynEditMarkupFoldColors.EndMarkup;
 begin
+  //DebugLn('EndMarkup');
   inherited EndMarkup;
   FNestList.Clear; // for next markup start
 end;
@@ -503,164 +695,188 @@
   FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
 end;
 
-{.$define debug_FC_line_changed}
-procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
-  ACountDiff: Integer);
-{$ifdef debug_FC_line_changed}
-var F : TCustomForm;
+procedure TSynEditMarkupFoldColors.SetHighlighter(AValue: TSynCustomHighlighter
+  );
 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}
+  if FHighlighter = AValue then Exit;
+  FHighlighter := AValue;
 
+  if not (FHighlighter is TSynCustomFoldHighlighter) then
+    exit;
 
+  FreeAndNil(FNestList);
 
-  function GetPairCloseFold(aRow, X : integer  ): Integer;
+  FNestList := TLazSynEditNestedFoldsList.Create(Lines, TSynCustomFoldHighlighter(FHighlighter));
+  FNestList.ResetFilter;
+  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
+  FNestList.IncludeOpeningOnLine := True; //False; //
+end;
+
+procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
+  ACountDiff: Integer);
+
+  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
   var
-    y,i,LCnt : integer;
-    HL: TSynCustomFoldHighlighter;
-    NodeList: TLazSynFoldNodeInfoList;
-    TmpNode, CloseNode: TSynFoldNodeInfo;
+    lCount, lLineIdx, i, lAnz: Integer;
+    lNode: TSynFoldNodeInfo;
+  begin
+      lLineIdx := ToIdx(pLine);
+      pNestList.Line := lLineIdx;
+      lCount := pNestList.Count;
+      SetLength(pList, lCount);
+      lAnz := 0;
+      for i := 0 to lCount - 1 do begin
+        lNode := pNestList.HLNode[i];
+        if (sfaInvalid in lNode.FoldAction)
+        or (
+          (sfaOpen in lNode.FoldAction)
+          and (lNode.LineIndex = lLineIdx)
+        ) then
+          Continue;
 
-    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);
+        pList[i] := lNode;
+        inc(lAnz);
       end;
+      SetLength(pList, lAnz);
+  end;
 
-    begin
-      Result := SearchLine(YIndex, NIndex);
-      if not (sfaInvalid in Result.FoldAction) then
-        exit;
+var
+  i, lMinAnz, lEndLine, j: integer;
+  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
+  HL: TSynCustomFoldHighlighter;
+  lNodeList: TLazSynFoldNodeInfoList;
+  lNode: TSynFoldNodeInfo;
+  found: Boolean;
 
-      inc(YIndex);
-      while (YIndex < LCnt) and
-            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
-             > StartNode.NestLvlStart)
-      do
-        inc(YIndex);
-      if YIndex = LCnt then
-        exit;
+begin
+  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
+  if EndLine < 0 then
+    EndLine := StartLine;
 
-      NIndex := -1;
-      Result := SearchLine(YIndex, NIndex);
+  for i := ToIdx(StartLine) to ToIdx(EndLine) do
+    FFirstCharacterColumn[i] := 0;
 
-      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
-        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
+  lEndLine := EndLine;
+
+  FillNestList(lStartNestList, StartLine, FNestList);
+  DebugLn('   Nodes at Start:');
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
+  FillNestList(lEndNestList, EndLine, FNestList);
+  DebugLn('   Nodes at End:');
+  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
+
+  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
+  for i := 0 to lMinAnz - 1 do begin
+    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
+    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
+    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
+    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
+    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
+      for j := 0 to length(lEndNestList) - 2 do
+        lEndNestList[j] := lEndNestList[j + 1];
+      SetLength(lEndNestList, Length(lEndNestList) - 1);
+    end else begin
+      break
     end;
+  end;
 
-  begin
-    Result := -1;
-    y := aRow -1;
-
+  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
+    // deeper fold group than StartLine: fold group ends after EndLine
+    // find real EndLine (end line of first remaining fold node)
+    DebugLn('   Remaining Nodes:');
+    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
     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
+    found := false;
+    repeat
+      inc(lEndLine);
+      if lEndLine > Lines.Count then break;
+      HL.FoldNodeInfo[ToIdx(lEndLine)].ClearFilter;
+      lNodeList := HL.FoldNodeInfo[ToIdx(lEndLine)];
+      lNodeList.AddReference;
+      try
+        lNodeList.ActionFilter := [sfaOutline, sfaClose];
+        i := 0;
+        repeat
+          lNode := lNodeList[i];
+          if not (sfaInvalid in lNode.FoldAction)
+          and (lNode.FoldType = lEndNestList[0].FoldType)
+          and (lNode.FoldGroup = lEndNestList[0].FoldGroup)
+          and (lNode.NestLvlEnd = lEndNestList[0].NestLvlStart) then begin
+            found := true;
+            break;
+          end;
           inc(i);
-          continue;
-        end;
+        until (i >= lNodeList.Count) or found;
+      finally
+        lNodeList.ReleaseReference;
+      end;
+    until found;
 
-        //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;
+  if lEndLine > EndLine then begin
+    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
+    InvalidateSynLines(EndLine + 1 , lEndLine);
+  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;
+procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
 var
-  EndFoldLine,y : integer;
+  old: TSynEditStrings;
 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);
+  old := Lines;
+  if Assigned(old)
+  and (AValue <> old) then begin
+    // Änderung:
+    // Remove Changehandler
+    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
   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}
+  inherited SetLines(AValue);
+  if (AValue <> old) then begin
+    // Änderung:
+    if Assigned(AValue) then begin
+      // Add Changehandler
+      SetLength(FFirstCharacterColumn, AValue.Count);
+      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
+    end else begin
+      SetLength(FFirstCharacterColumn, 0);
+    end;
+  end;
 end;
 
-procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
-var Y : integer;
+procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
+                                        aIndex, aCount: Integer);
+var
+  absCount,
+  idx, i: Integer;
 begin
-  Y := Caret.LineBytePos.y;
-  if Y = FCaretY then exit;
-
-  FCaretY := Y;
-  FPrevCaretText := Caret.LineText;
+  //DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  idx := ToIdx(aIndex);
+  if aCount < 0 then begin
+    // Zeilen entfernt
+    absCount := Abs(aCount);
+    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
+      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
+    end;
+  end;
+  SetLength(FFirstCharacterColumn, Sender.Count);
+  if (aCount > 0) then begin
+    if idx >= 0 then begin
+      // Zeilen eingefügt
+      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
+        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
+      end;
+      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do FFirstCharacterColumn[i] := 0;
+    end else begin
+      // erste Zeilen werden eingefügt
+      for i := 0 to Length(FFirstCharacterColumn) - 1 do FFirstCharacterColumn[i] := 0;
+    end;
+  end;
 end;
 
-
-
 end.
 

Pascal Riekenberg

2016-07-30 07:02

reporter   ~0093900

Martin,

i've uploaded an updated path. It had a index/pos mismatch.

Pascal Riekenberg

2016-07-30 07:05

reporter  

syneditmarkupfoldcoloring.pas_v3.patch (29,749 bytes)
Index: syneditmarkupfoldcoloring.pas
===================================================================
--- syneditmarkupfoldcoloring.pas	(revision 52753)
+++ syneditmarkupfoldcoloring.pas	(working copy)
@@ -55,7 +55,8 @@
 
 uses
   Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
-  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
+  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
+  LazSynEditText, fgl;
 
 type
 
@@ -66,37 +67,38 @@
     Border  : Boolean;
     Ignore  : Boolean; //no color no line
     SrcNode : TSynFoldNodeInfo;
-    LevelBefore, LevelAfter : integer;//needed by non nest nodes
+    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
   end;
 
   TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
   TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
-
   { TSynEditMarkupFoldColors }
 
   TSynEditMarkupFoldColors = class(TSynEditMarkup)
   private
+    FHighlighter: TSynCustomHighlighter;
     FNestList: TLazSynEditNestedFoldsList;
+    FFirstCharacterColumn: Array of Byte;
     FDefaultGroup: integer;
      // Physical Position
-    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
+    FHighlights: TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
     Colors : array of TColor;
 
-    {%region invalidating}
-    CurrentY : integer;  //??
-    FCaretY : integer;    // flag identify for refresh begin______
-    FPrevCaretText : string;  // flag identify for refresh begin______
-    {%endregion}
+    FCurrentRow: integer;  // prepared Row
 
+    FLastNode: TSynFoldNodeInfo;
     procedure DoMarkupParentFoldAtRow(aRow: Integer);
     procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
 
     function GetFoldHighLighter: TSynCustomFoldHighlighter;
     procedure SetDefaultGroup(AValue: integer);
+    procedure SetHighlighter(AValue: TSynCustomHighlighter);
   protected
     // Notifications about Changes to the text
     procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
-    procedure DoCaretChanged(Sender: TObject); override;
+
+    procedure SetLines(const AValue: TSynEditStrings); override;
+    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
   public
     constructor Create(ASynEdit : TSynEditBase);
     destructor Destroy; override;
@@ -111,12 +113,54 @@
     procedure PrepareMarkupForRow(aRow : Integer); override;
     procedure EndMarkup; override;
     property DefaultGroup : integer read FDefaultGroup write SetDefaultGroup;
+    property  Highlighter: TSynCustomHighlighter
+      read FHighlighter write SetHighlighter;
   end;
 
 implementation
 uses
-  SynEdit,SynEditTypes, SynEditMiscProcs;
+  SynEdit,SynEditTypes, SynEditMiscProcs, Dialogs, strutils;
 
+function FoldTypeToStr(p_FoldType: Pointer): String;
+begin
+  case PtrInt(p_FoldType) of
+    00: Result := 'cfbtBeginEnd      ';
+    01: Result := 'cfbtTopBeginEnd   ';
+    02: Result := 'cfbtNestedComment ';
+    03: Result := 'cfbtProcedure     ';
+    04: Result := 'cfbtUses          ';
+    05: Result := 'cfbtVarType       ';
+    06: Result := 'cfbtLocalVarType  ';
+    07: Result := 'cfbtClass         ';
+    08: Result := 'cfbtClassSection  ';
+    09: Result := 'cfbtUnitSection   ';
+    10: Result := 'cfbtProgram       ';
+    11: Result := 'cfbtUnit          ';
+    12: Result := 'cfbtRecord        ';
+    13: Result := 'cfbtTry           ';
+    14: Result := 'cfbtExcept        ';
+    15: Result := 'cfbtRepeat        ';
+    16: Result := 'cfbtAsm           ';
+    17: Result := 'cfbtCase          ';
+    18: Result := 'cfbtIfDef         ';
+    19: Result := 'cfbtRegion        ';
+    20: Result := 'cfbtAnsiComment   ';
+    21: Result := 'cfbtBorCommand    ';
+    22: Result := 'cfbtSlashComment  ';
+    23: Result := 'cfbtIfThen        ';
+    24: Result := 'cfbtForDo         ';
+    25: Result := 'cfbtWhileDo       ';
+    26: Result := 'cfbtWithDo        ';
+    27: Result := 'cfbtIfElse        ';
+    28: Result := '// Internal type  ';
+    29: Result := 'cfbtCaseElse (int)';
+    30: Result := 'cfbtPackage (int) ';
+    31: Result := 'cfbtNone (int)    ';
+    else Result := Format('unknown %.2d        ', [PtrInt(p_FoldType)]);
+  end;
+end;
+
+
   {%region Sorting FoldInfo -fold}
   function CompareFI(Item1, Item2: Pointer): Integer;
   begin
@@ -143,6 +187,12 @@
       result[i] := PMarkupFoldColorInfo(l[i])^;
      l.Free;
   end;
+
+  operator = (z1, z2: TSynFoldNodeInfo): boolean;
+  begin
+      Result := false;
+  end;
+
   {%endregion}
 
 { TSynEditMarkupFoldColors }
@@ -151,6 +201,9 @@
 begin
   inherited Create(ASynEdit);
 
+  if Assigned(Lines) then
+    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
+
   FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
   FNestList.ResetFilter;
   FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
@@ -157,6 +210,8 @@
   FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
   FNestList.IncludeOpeningOnLine := True; //False; //
 
+  SetLength(FFirstCharacterColumn, 0);
+
   MarkupInfo.Foreground := clGreen;
   MarkupInfo.Background := clNone; //clFuchsia;
   MarkupInfo.Style := [];
@@ -174,6 +229,8 @@
 
 destructor TSynEditMarkupFoldColors.Destroy;
 begin
+  if Assigned(Lines) then
+    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
   FreeAndNil(FNestList);
   inherited Destroy;
 end;
@@ -182,12 +239,15 @@
   const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
   const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
 var
-  i,x2both : integer;
+  i,x2both , found, color: integer;
 begin
   Result := nil;
-  if (CurrentY = aRow) then begin
+  if (FCurrentRow = aRow) then begin
+    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
 
     x2both := -3; //flag
+    //found := -1;
+    //color := -1;
     for i := 0 to length(FHighlights)-1 do
       with FHighlights[i] do
         if not Ignore
@@ -194,11 +254,21 @@
         and (X < X2)
         and (ColorIdx >= 0)
         and (aStartCol.Logical >= x)
-        and (aStartCol.Logical < X2) then
-        begin
+        and (aStartCol.Logical < X2) then begin
+          //if (found >= 0)
+          //and (found = x)
+          //and (
+          //  (ToPos(SrcNode.LineIndex) <> aRow)
+          //  or not (sfaOpen in SrcNode.FoldAction)
+          //) then
+          //  Continue;
+          //
+          //if x > found then
+          //  found := -1;
+
+          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
           //MarkupInfo.FrameColor:= clGreen; //debug
-          if x2both = -3 then //first call flag
-          begin
+          if x2both = -3 then begin //first call flag
             MarkupInfo.FrameColor:= clNone;
             MarkupInfo.Foreground:= clNone;
             MarkupInfo.Background:= clNone;
@@ -209,13 +279,16 @@
           Result := MarkupInfo;
           x2both := max(x2both, x2);
           MarkupInfo.SetFrameBoundsLog(x, x2both);
-          if Border then
-          begin
-            MarkupInfo.FrameColor:= Colors[ColorIdx];
+          //if found < 0 then
+            color := Colors[ColorIdx];
+          if Border then begin
+            MarkupInfo.FrameColor:= color;
             MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
-          end
-          else
-            MarkupInfo.Foreground := Colors[ColorIdx];
+          end else begin
+            MarkupInfo.FrameColor:= clNone;
+            MarkupInfo.FrameEdges:= sfeNone;
+            MarkupInfo.Foreground := color;
+          end;
 
           //MarkupInfo.FrameEdges:= sfeAround; //debug
 
@@ -227,7 +300,7 @@
             MarkupInfo.FrameColor:= clBlue; //debug
           end;}
 
-          //break;
+          //found := x;
         end;
   end;
 end;
@@ -237,9 +310,10 @@
   const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
 var i : integer;
 begin
+  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
   ANextLog := -1;
   ANextPhys := -1;
-  if (CurrentY = aRow)  then
+  if (FCurrentRow = aRow)  then
   for i := 0 to length(FHighlights)-1  do
     with FHighlights[i] do
     begin
@@ -258,16 +332,42 @@
   i,lvl,z : integer; //iterate parents fold
 
   procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
-  var x,j : integer;
+  var
+    j, p, s: integer;
+    l: String;
+
   begin
+    // get column of first character in row
+      // Fallback if Highlighter doesn't populate ColumnOfFirstCharInRow
+    s := Length(FFirstCharacterColumn);
+    if (s <= ANode.LineIndex)
+    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
+      l := SynEdit.Lines[ANode.LineIndex];
+      p := 1;
+      while not (l[p] in [#13, #10, #0])
+      and (l[p] = #32) do inc(p);
+      if p > 255 then p := 255;
+      if s > ANode.LineIndex then begin
+        FFirstCharacterColumn[ANode.LineIndex] := p;
+      end else begin
+        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
     z := Length(FHighlights);
     SetLength(FHighlights, z+1);
     with FHighlights[z] do begin
+
       SrcNode:= ANode; //needed by close node
-      Border := ANode.LineIndex + 1 <> aRow;
-      X  := ANode.LogXStart + 1;
+      Border := ToPos(ANode.LineIndex) <> aRow;
+      //X  := ANode.LogXStart + 1;
+      //X  := p;
+      if s <= ANode.LineIndex then
+        X  := p
+      else
+        X  := FFirstCharacterColumn[ANode.LineIndex];
       Y  := aRow;//ANode.LineIndex + 1;
-      X2 := X+1; //ANode.LogXEnd + 1;
+      X2 := X + 1; //ANode.LogXEnd + 1;
       Ignore := False;
 
       if Border and (sfaOutlineNoLine in ANode.FoldAction) then
@@ -274,7 +374,9 @@
         Ignore := True;
       if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
         Ignore := True;
-        ColorIdx := lvl mod (length(Colors))
+      Level := lvl;
+      ColorIdx := Max(0, lvl) mod (length(Colors));
+      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
     end;
   end;
 
@@ -292,7 +394,7 @@
   end;
 
 begin
-  y := aRow-1;
+  y := ToIdx(aRow);
   FNestList.Line := y;
   NestCount := FNestList.Count;
 
@@ -318,14 +420,33 @@
       lvlB := lvl;
 
       if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-        inc(lvl);
-      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+        inc(lvl)
+      else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
         dec(lvl);
+      if (FLastNode.LineIndex >= 0)
+      and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+      and (FLastNode.LineIndex < TmpNode.LineIndex) then
+       inc(lvl);
 
       AddVerticalLine(TmpNode);
-      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
+
+      if (z > 0)
+      and (FHighlights[z].X = FHighlights[z - 1].X) then begin
+        // if child is on same x-pos keep level
+        if sfaOutlineMergeLevelOnWrongCol in FHighlights[z].SrcNode.FoldAction then begin
+          lvl := FHighlights[z - 1].Level;
+          FHighlights[z].Level := lvl;
+          FHighlights[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+        end;
+      end;
+
+      if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+      and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction) then
         inc(lvl);
 
+      if sfaOpen in TmpNode.FoldAction then
+        FLastNode := TmpNode;
+
       lvlA := lvl;
 
       with FHighlights[z] do begin
@@ -370,9 +491,9 @@
       Y  := ANode.LineIndex + 1;
       X  := ANode.LogXStart + 1;
       X2 := ANode.LogXEnd + 1;
-      //ColorIdx := lvl;
+      Level := lvl;
       if not (sfaOutlineNocolor in ANode.FoldAction) then
-         ColorIdx := lvl mod (length(Colors))
+         ColorIdx := Max(0, lvl) mod (length(Colors))
       else
          ColorIdx := -1;
     end;
@@ -382,10 +503,10 @@
   y,i,j,lvlB,lvlA : integer;
   HL: TSynCustomFoldHighlighter;
   NodeList: TLazSynFoldNodeInfoList;
-  TmpNode: TSynFoldNodeInfo;
+  TmpNode, PrevNode: TSynFoldNodeInfo;
   Found : boolean;
 begin
-  y := aRow -1;
+  y := ToIdx(aRow);
 
   HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
   HL.CurrentLines := Lines;
@@ -401,6 +522,7 @@
     if J >=0 then
       lvl := max(0,FHighlights[J].LevelAfter);
     i := 0;
+    PrevNode.LineIndex := -2;
     repeat
       TmpNode := NodeList[i];
 
@@ -416,15 +538,37 @@
           lvlB := lvl;
 
           if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-            inc(lvl);
-          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+            inc(lvl)
+          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
             dec(lvl);
+          if (FLastNode.LineIndex >= 0)
+          and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+          and (FLastNode.LineIndex < TmpNode.LineIndex) then
+           inc(lvl);
 
           AddHighlight(TmpNode);
-          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
+
+          if (z > 0)
+          and (FHighlights[z].X = FHighlights[z - 1].X) then
+          begin
+            // if child is on same x-pos keep level
+            if (sfaClose in FHighlights[z].SrcNode.FoldAction)
+            or (sfaOutlineMergeLevelOnWrongCol in FHighlights[z].SrcNode.FoldAction) then begin
+              lvl := FHighlights[z - 1].Level;
+              FHighlights[z].Level := lvl;
+              FHighlights[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+            end;
+          end;
+
+          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+          and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction) then
             inc(lvl);
+
           lvlA := lvl;
 
+          if sfaOpen in TmpNode.FoldAction then
+            FLastNode := TmpNode;
+
           with FHighlights[z] do begin
             LevelBefore := lvlB;
             LevelAfter  := lvlA;
@@ -437,28 +581,50 @@
           Found := False;
           for j := Length(FHighlights)-1 downto 0 do begin
             with FHighlights[j].SrcNode do begin
-              if  (FoldType = TmpNode.FoldType) and
-                (FoldGroup = TmpNode.FoldGroup) and
-                (sfaOpen in FoldAction) and
-                // (FoldLvlEnd = TmpNode.FoldLvlStart)
-                (NestLvlEnd = TmpNode.NestLvlStart)
-
-                then begin
-                  lvl := FHighlights[j].ColorIdx;
-                  lvlB := FHighlights[j].LevelBefore;
-                  Found := True;
-                  break;
-                end;
+              if (FoldType = TmpNode.FoldType)
+              and (FoldGroup = TmpNode.FoldGroup)
+              and (sfaOpen in FoldAction)
+              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
+                lvlB := lvl;
+                lvl := FHighlights[j].Level;
+                lvlA := FHighlights[j].LevelAfter;
+                FLastNode := TmpNode;
+                Found := True;
+                break;
+              end;
             end;
           end;
           if Found then begin
+            //FLastNode.LineIndex := -1;
+            //for j := j - 1 downto 0 do begin
+            //  with FHighlights[j].SrcNode do begin
+            //    if (sfaOpen in FoldAction) then begin
+            //      FLastNode := FHighlights[j].SrcNode;
+            //      Break;
+            //    end;
+            //  end;
+            //end;
             AddHighlight(TmpNode);
-            lvl := lvlB;
+            //lvl := lvlA;
+            with FHighlights[z] do begin
+              LevelBefore := lvlB;
+              LevelAfter  := lvlA;
+            end;
+            // if found opening position is behind closing position:
+            // delete this as it does not have to be drawn
+            if FHighlights[j].X > FHighlights[z].X then begin
+              for j := j to z - 1 do begin
+                FHighlights[j] := FHighlights[j+1];
+              end;
+              SetLength(FHighlights, z);
+            end;
           end;
 
           //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
             //inc(lvl);
         end;
+
+        PrevNode := TmpNode;
       end;
 
       inc(i);
@@ -470,23 +636,49 @@
 end;
 
 procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
+var
+  i, li, LastX, j: Integer;
+  n: TMarkupFoldColorInfos;
+  l: TList;
 begin
-  CurrentY := aRow;
+  //DebugLn('PrepareMarkupForRow %d', [aRow]);
+  FCurrentRow := aRow;
   SetLength(FHighlights,0); //reset needed to prevent using of invalid area
 
   if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
     exit;
 
+  FLastNode.LineIndex := -1;
+
   //DoMarkupFoldAtRow(aRow);
   DoMarkupParentFoldAtRow(aRow);
   DoMarkupParentCloseFoldAtRow(aRow);
   //DoMarkupRangeFoldAtRow(aRow);
 
-  FHighlights := SortLeftMostFI(FHighlights);
+  //FHighlights := SortLeftMostFI(FHighlights);
+
+  // delete parents with bigger x
+  // to keep out mis indented blocks
+  LastX := MaxInt;
+  for i := length(FHighlights) - 1 downto 0 do begin
+    if FHighlights[i].X > LastX then begin
+      for j := i to length(FHighlights) - 2 do begin
+        FHighlights[j] := FHighlights[j + 1];
+      end;
+      SetLength(FHighlights, length(FHighlights) - 1);
+    end;
+    LastX := FHighlights[i].X;
+  end;
+
+  //DebugLn('Zeile %d', [aRow]);
+  //for i := 0 to length(FHighlights) - 1 do with FHighlights[i] do begin
+  //  DebugLn('   %.2d: x=%.03d l=%.5d %s %s %s %s lvl=%.2d - %.2d - %.2d', [i, X, ToPos(SrcNode.LineIndex), IfThen(sfaOpen in SrcNode.FoldAction, 'O', IfThen(sfaClose in SrcNode.FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in SrcNode.FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in SrcNode.FoldAction, '+', IfThen(sfaOutlineMergeParent in SrcNode.FoldAction, '-', ' ')) ,FoldTypeToStr(SrcNode.FoldType), LevelBefore, Level, LevelAfter]);
+  //end;
 end;
 
 procedure TSynEditMarkupFoldColors.EndMarkup;
 begin
+  //DebugLn('EndMarkup');
   inherited EndMarkup;
   FNestList.Clear; // for next markup start
 end;
@@ -503,164 +695,188 @@
   FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
 end;
 
-{.$define debug_FC_line_changed}
-procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
-  ACountDiff: Integer);
-{$ifdef debug_FC_line_changed}
-var F : TCustomForm;
+procedure TSynEditMarkupFoldColors.SetHighlighter(AValue: TSynCustomHighlighter
+  );
 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}
+  if FHighlighter = AValue then Exit;
+  FHighlighter := AValue;
 
+  if not (FHighlighter is TSynCustomFoldHighlighter) then
+    exit;
 
+  FreeAndNil(FNestList);
 
-  function GetPairCloseFold(aRow, X : integer  ): Integer;
+  FNestList := TLazSynEditNestedFoldsList.Create(Lines, TSynCustomFoldHighlighter(FHighlighter));
+  FNestList.ResetFilter;
+  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
+  FNestList.IncludeOpeningOnLine := True; //False; //
+end;
+
+procedure TSynEditMarkupFoldColors.DoTextChanged(StartLine, EndLine,
+  ACountDiff: Integer);
+
+  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
   var
-    y,i,LCnt : integer;
-    HL: TSynCustomFoldHighlighter;
-    NodeList: TLazSynFoldNodeInfoList;
-    TmpNode, CloseNode: TSynFoldNodeInfo;
+    lCount, lLineIdx, i, lAnz: Integer;
+    lNode: TSynFoldNodeInfo;
+  begin
+      lLineIdx := ToIdx(pLine);
+      pNestList.Line := lLineIdx;
+      lCount := pNestList.Count;
+      SetLength(pList, lCount);
+      lAnz := 0;
+      for i := 0 to lCount - 1 do begin
+        lNode := pNestList.HLNode[i];
+        if (sfaInvalid in lNode.FoldAction)
+        or (
+          (sfaOpen in lNode.FoldAction)
+          and (lNode.LineIndex = lLineIdx)
+        ) then
+          Continue;
 
-    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);
+        pList[i] := lNode;
+        inc(lAnz);
       end;
+      SetLength(pList, lAnz);
+  end;
 
-    begin
-      Result := SearchLine(YIndex, NIndex);
-      if not (sfaInvalid in Result.FoldAction) then
-        exit;
+var
+  i, lMinAnz, lEndLine, j: integer;
+  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
+  HL: TSynCustomFoldHighlighter;
+  lNodeList: TLazSynFoldNodeInfoList;
+  lNode: TSynFoldNodeInfo;
+  found: Boolean;
 
-      inc(YIndex);
-      while (YIndex < LCnt) and
-            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
-             > StartNode.NestLvlStart)
-      do
-        inc(YIndex);
-      if YIndex = LCnt then
-        exit;
+begin
+  //DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
+  if EndLine < 0 then
+    EndLine := StartLine;
 
-      NIndex := -1;
-      Result := SearchLine(YIndex, NIndex);
+  for i := ToIdx(StartLine) to ToIdx(EndLine) do
+    FFirstCharacterColumn[i] := 0;
 
-      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
-        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
+  lEndLine := EndLine;
+
+  FillNestList(lStartNestList, StartLine, FNestList);
+  //DebugLn('   Nodes at Start:');
+  //for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+  //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
+  FillNestList(lEndNestList, EndLine, FNestList);
+  //DebugLn('   Nodes at End:');
+  //for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+  //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
+
+  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
+  for i := 0 to lMinAnz - 1 do begin
+    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
+    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
+    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
+    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
+    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
+      for j := 0 to length(lEndNestList) - 2 do
+        lEndNestList[j] := lEndNestList[j + 1];
+      SetLength(lEndNestList, Length(lEndNestList) - 1);
+    end else begin
+      break
     end;
+  end;
 
-  begin
-    Result := -1;
-    y := aRow -1;
-
+  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
+    // deeper fold group than StartLine: fold group ends after EndLine
+    // find real EndLine (end line of first remaining fold node)
+    //DebugLn('   Remaining Nodes:');
+    //for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+    //  DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevelOnSameLine in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd]);
     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;
+    found := false;
+    repeat
+        inc(lEndLine);
+        if lEndLine > Lines.Count then break;
+        HL.FoldNodeInfo[ToIdx(lEndLine)].ClearFilter;
+        lNodeList := HL.FoldNodeInfo[ToIdx(lEndLine)];
+        lNodeList.AddReference;
+        try
+          lNodeList.ActionFilter := [sfaOutline, sfaClose];
+          i := 0;
+          repeat
+            lNode := lNodeList[i];
+            if not (sfaInvalid in lNode.FoldAction)
+            and (lNode.FoldType = lEndNestList[0].FoldType)
+            and (lNode.FoldGroup = lEndNestList[0].FoldGroup)
+            and (lNode.NestLvlEnd = lEndNestList[0].NestLvlStart) then begin
+              found := true;
+              break;
+            end;
+            inc(i);
+          until (i >= lNodeList.Count) or found;
+        finally
+          lNodeList.ReleaseReference;
         end;
+    until found;
 
-        //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;
+  if lEndLine > EndLine then begin
+    //DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
+    InvalidateSynLines(EndLine + 1 , lEndLine);
+  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;
+procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
 var
-  EndFoldLine,y : integer;
+  old: TSynEditStrings;
 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);
+  old := Lines;
+  if Assigned(old)
+  and (AValue <> old) then begin
+    // Änderung:
+    // Remove Changehandler
+    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
   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}
+  inherited SetLines(AValue);
+  if (AValue <> old) then begin
+    // Änderung:
+    if Assigned(AValue) then begin
+      // Add Changehandler
+      SetLength(FFirstCharacterColumn, AValue.Count);
+      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
+    end else begin
+      SetLength(FFirstCharacterColumn, 0);
+    end;
+  end;
 end;
 
-procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
-var Y : integer;
+procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
+                                        aIndex, aCount: Integer);
+var
+  absCount,
+  idx, i: Integer;
 begin
-  Y := Caret.LineBytePos.y;
-  if Y = FCaretY then exit;
-
-  FCaretY := Y;
-  FPrevCaretText := Caret.LineText;
+  //DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  idx := ToIdx(aIndex);
+  if aCount < 0 then begin
+    // Zeilen entfernt
+    absCount := Abs(aCount);
+    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
+      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
+    end;
+  end;
+  SetLength(FFirstCharacterColumn, Sender.Count);
+  if (aCount > 0) then begin
+    if idx >= 0 then begin
+      // Zeilen eingefügt
+      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
+        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
+      end;
+      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do FFirstCharacterColumn[i] := 0;
+    end else begin
+      // erste Zeilen werden eingefügt
+      for i := 0 to Length(FFirstCharacterColumn) - 1 do FFirstCharacterColumn[i] := 0;
+    end;
+  end;
 end;
 
-
-
 end.
 

Pascal Riekenberg

2016-07-30 07:06

reporter   ~0093901

Sorry, forgot to disable debug output. -> new patch (v3)

Martin Friebe

2016-07-31 19:10

manager  

ide_opts_syn_outline_DO_NOT_SVN_commit.diff (15,196 bytes)
Index: ide/editoroptions.pp
===================================================================
--- ide/editoroptions.pp	(revision 52759)
+++ ide/editoroptions.pp	(working copy)
@@ -1396,6 +1396,8 @@
 
     // Code Folding
     FUseCodeFolding: Boolean;
+    FUseMarkupWordBracket: Boolean;
+    FUseMarkupOutline: Boolean;
     FReverseFoldPopUpOrder: Boolean;
 
     // Multi window
@@ -1605,6 +1607,10 @@
     // Code Folding
     property UseCodeFolding: Boolean
         read FUseCodeFolding write FUseCodeFolding default True;
+    property UseMarkupWordBracket: Boolean
+        read FUseMarkupWordBracket write FUseMarkupWordBracket default True;
+    property UseMarkupOutline: Boolean
+        read FUseMarkupOutline write FUseMarkupOutline default True;
 
     // Multi window
     property MultiWinEditAccessOrder: TEditorOptionsEditAccessOrderList
@@ -4866,6 +4872,12 @@
     FUseCodeFolding :=
       XMLConfig.GetValue(
       'EditorOptions/CodeFolding/UseCodeFolding', True);
+    FUseMarkupWordBracket :=
+      XMLConfig.GetValue(
+      'EditorOptions/CodeFolding/UseMarkupWordBracket', True);
+    FUseMarkupOutline :=
+      XMLConfig.GetValue(
+      'EditorOptions/CodeFolding/UseMarkupOutline', True);
 
     FUserMouseSettings.LoadFromXml(XMLConfig, 'EditorOptions/Mouse/',
                                   'EditorOptions/General/Editor/', FileVersion);
@@ -5052,6 +5064,10 @@
     // Code Folding
     XMLConfig.SetDeleteValue('EditorOptions/CodeFolding/UseCodeFolding',
         FUseCodeFolding, True);
+    XMLConfig.SetDeleteValue('EditorOptions/CodeFolding/UseMarkupWordBracket',
+        FUseMarkupWordBracket, True);
+    XMLConfig.SetDeleteValue('EditorOptions/CodeFolding/UseMarkupOutline',
+        FUseMarkupOutline, True);
 
     FUserMouseSettings.SaveToXml(XMLConfig, 'EditorOptions/Mouse/');
 
@@ -5369,8 +5385,13 @@
         (* if ReadForOptions=True then Enabled appies only to fmFold,fmHide.
            This allows to store what selection was previously active *)
         if not ReadForOptions then begin
-          if not FoldHl.FoldConfig[idx].Enabled then
+          if (not FoldHl.FoldConfig[idx].Enabled) or (not FUseCodeFolding) then
             FoldHl.FoldConfig[idx].Modes := FoldHl.FoldConfig[idx].Modes - [fmFold, fmHide];
+          if (not FUseMarkupWordBracket) then
+            FoldHl.FoldConfig[idx].Modes := FoldHl.FoldConfig[idx].Modes - [fmMarkup];
+          if (not FUseMarkupOutline) then
+            FoldHl.FoldConfig[idx].Modes := FoldHl.FoldConfig[idx].Modes - [fmOutline];
+
           FoldHl.FoldConfig[idx].Enabled := FoldHl.FoldConfig[idx].Modes <> [];
         end;
       end;
Index: ide/frames/editor_markup_options.lfm
===================================================================
--- ide/frames/editor_markup_options.lfm	(revision 52759)
+++ ide/frames/editor_markup_options.lfm	(working copy)
@@ -317,13 +317,14 @@
     AnchorSideBottom.Control = Owner
     AnchorSideBottom.Side = asrBottom
     Left = 6
-    Height = 156
-    Top = 330
+    Height = 150
+    Top = 380
     Width = 220
     Anchors = [akTop, akLeft, akBottom]
     BorderSpacing.Around = 6
     Constraints.MinHeight = 150
     ItemHeight = 0
+    OnClick = chkKWGroupsClick
     OnClickCheck = chkKWGroupsClickCheck
     OnExit = chkKWGroupsClickCheck
     OnKeyUp = chkKWGroupsKeyUp
@@ -331,11 +332,11 @@
   end
   object LanguageComboBox: TComboBox
     AnchorSideLeft.Control = Owner
-    AnchorSideTop.Control = divKeyWordGroups
+    AnchorSideTop.Control = cbMarkupOutline
     AnchorSideTop.Side = asrBottom
     Left = 6
     Height = 23
-    Top = 301
+    Top = 351
     Width = 200
     BorderSpacing.Left = 6
     BorderSpacing.Top = 6
@@ -353,10 +354,64 @@
     AnchorSideTop.Side = asrCenter
     Left = 212
     Height = 15
-    Top = 305
+    Top = 355
     Width = 80
     BorderSpacing.Left = 6
     Caption = 'LanguageLabel'
     ParentColor = False
   end
+  object cbMarkup: TCheckBox
+    AnchorSideLeft.Control = chkKWGroups
+    AnchorSideLeft.Side = asrBottom
+    AnchorSideTop.Control = chkKWGroups
+    Left = 232
+    Height = 19
+    Top = 380
+    Width = 74
+    BorderSpacing.Left = 6
+    Caption = 'cbMarkup'
+    OnChange = cbMarkupChange
+    TabOrder = 10
+  end
+  object cbOutline: TCheckBox
+    AnchorSideLeft.Control = chkKWGroups
+    AnchorSideLeft.Side = asrBottom
+    AnchorSideTop.Control = cbMarkup
+    AnchorSideTop.Side = asrBottom
+    Left = 232
+    Height = 19
+    Top = 405
+    Width = 72
+    BorderSpacing.Left = 6
+    BorderSpacing.Top = 6
+    Caption = 'cbOutline'
+    OnChange = cbMarkupChange
+    TabOrder = 11
+  end
+  object cbMarkupWordBracket: TCheckBox
+    AnchorSideLeft.Control = Owner
+    AnchorSideTop.Control = divKeyWordGroups
+    AnchorSideTop.Side = asrBottom
+    Left = 6
+    Height = 19
+    Top = 301
+    Width = 142
+    BorderSpacing.Around = 6
+    Caption = 'cbMarkupWordBracket'
+    OnChange = cbMarkupWordBracketChange
+    TabOrder = 12
+  end
+  object cbMarkupOutline: TCheckBox
+    AnchorSideLeft.Control = Owner
+    AnchorSideTop.Control = cbMarkupWordBracket
+    AnchorSideTop.Side = asrBottom
+    Left = 6
+    Height = 19
+    Top = 326
+    Width = 113
+    BorderSpacing.Around = 6
+    Caption = 'cbMarkupOutline'
+    OnChange = cbMarkupOutlineChange
+    TabOrder = 13
+  end
 end
Index: ide/frames/editor_markup_options.pas
===================================================================
--- ide/frames/editor_markup_options.pas	(revision 52759)
+++ ide/frames/editor_markup_options.pas	(working copy)
@@ -38,6 +38,10 @@
     BracketCombo: TComboBox;
     BracketLabel: TLabel;
     BracketLink: TLabel;
+    cbMarkup: TCheckBox;
+    cbOutline: TCheckBox;
+    cbMarkupWordBracket: TCheckBox;
+    cbMarkupOutline: TCheckBox;
     chkKWGroups: TCheckListBox;
     chkExtPasKeywords: TCheckBox;
     divKeyWordGroups: TDividerBevel;
@@ -64,7 +68,11 @@
     procedure BracketLinkClick(Sender: TObject);
     procedure BracketLinkMouseEnter(Sender: TObject);
     procedure BracketLinkMouseLeave(Sender: TObject);
+    procedure cbMarkupChange(Sender: TObject);
+    procedure cbMarkupOutlineChange(Sender: TObject);
+    procedure cbMarkupWordBracketChange(Sender: TObject);
     procedure chkExtPasKeywordsChange(Sender: TObject);
+    procedure chkKWGroupsClick(Sender: TObject);
     procedure chkKWGroupsClickCheck(Sender: TObject);
     procedure chkKWGroupsKeyUp(Sender: TObject; var {%H-}Key: Word; {%H-}Shift: TShiftState);
     procedure dropPasStringKeywordsChange(Sender: TObject);
@@ -79,10 +87,14 @@
   private
     { private declarations }
     FDialog: TAbstractOptionsEditorDialog;
+    FModeLock: Boolean;
     FCurHighlighter: TSrcIDEHighlighter;
     FCurFoldInfo: TEditorOptionsFoldRecord;
+    FUseMarkupWordBracket: Boolean;
+    FUseMarkupOutline: Boolean;
     function GetHighlighter(SynType: TLazSyntaxHighlighter;
       CreateIfNotExists: Boolean): TSrcIDEHighlighter;
+    procedure UpdateMarkupCheckBoxes;
 
   public
     function GetTitle: String; override;
@@ -157,6 +169,46 @@
   (Sender as TLabel).Font.Color := clBlue;
 end;
 
+procedure TEditorMarkupOptionsFrame.cbMarkupChange(Sender: TObject);
+var
+  Hl: TSynCustomFoldHighlighter;
+  Modes: TSynCustomFoldConfigModes;
+  i: LongInt;
+begin
+  if FModeLock then exit;
+  if not (assigned(FCurHighlighter) and
+         (FCurHighlighter is TSynCustomFoldHighlighter)) then exit;
+
+  i := chkKWGroups.ItemIndex;
+  if i < 0 then exit;
+  i := PtrUInt(chkKWGroups.Items.Objects[i]);
+  i := FCurFoldInfo.Info^[i].Index;
+  Hl := TSynCustomFoldHighlighter(FCurHighlighter);
+
+  Modes := [fmMarkup];
+  if Sender = cbOutline then
+    Modes := [fmOutline];
+
+  if TCheckBox(Sender).Checked then
+    Hl.FoldConfig[i].Modes := Hl.FoldConfig[i].Modes + Modes
+  else
+    Hl.FoldConfig[i].Modes := Hl.FoldConfig[i].Modes - Modes;
+
+  chkKWGroups.Checked[chkKWGroups.ItemIndex] := cbMarkup.Checked or cbOutline.Checked;
+end;
+
+procedure TEditorMarkupOptionsFrame.cbMarkupOutlineChange(Sender: TObject);
+begin
+  FUseMarkupOutline := cbMarkupOutline.Checked;
+  LanguageComboBoxExit(nil);
+end;
+
+procedure TEditorMarkupOptionsFrame.cbMarkupWordBracketChange(Sender: TObject);
+begin
+  FUseMarkupWordBracket := cbMarkupWordBracket.Checked;
+  LanguageComboBoxExit(nil);
+end;
+
 procedure TEditorMarkupOptionsFrame.chkExtPasKeywordsChange(Sender: TObject);
 begin
   GeneralPage.PasExtendedKeywordsMode := chkExtPasKeywords.Checked;
@@ -163,26 +215,39 @@
   GeneralPage.UpdatePrevieEdits;
 end;
 
+procedure TEditorMarkupOptionsFrame.chkKWGroupsClick(Sender: TObject);
+begin
+  UpdateMarkupCheckBoxes;
+end;
+
 procedure TEditorMarkupOptionsFrame.chkKWGroupsClickCheck(Sender: TObject);
 var
-  i, j, idx: Integer;
+  i, j, idx, i1: Integer;
   Hl: TSynCustomFoldHighlighter;
+  FMask: TSynCustomFoldConfigModes;
 begin
   if not (assigned(FCurHighlighter) and
          (FCurHighlighter is TSynCustomFoldHighlighter)) then exit;
   Hl := TSynCustomFoldHighlighter(FCurHighlighter);
+  FMask := [fmMarkup, fmOutline];
+  if not FUseMarkupWordBracket then FMask := FMask - [fmMarkup];
+  if not FUseMarkupOutline then FMask := FMask - [fmOutline];
 
   j := 0;
-  for i := 0 to FCurFoldInfo.Count - 1 do begin
+  for i1 := 0 to chkKWGroups.Count - 1 do begin
+    i := PtrUInt(chkKWGroups.Items.Objects[i1]);
     idx := FCurFoldInfo.Info^[i].Index;
-    if fmMarkup in Hl.FoldConfig[idx].SupportedModes then begin
-      if chkKWGroups.Checked[j] then
-        Hl.FoldConfig[idx].Modes := Hl.FoldConfig[idx].Modes + [fmMarkup]
+    if Hl.FoldConfig[idx].SupportedModes * [fmMarkup, fmOutline] <> [] then begin
+      if not chkKWGroups.Checked[j] then
+        Hl.FoldConfig[idx].Modes := Hl.FoldConfig[idx].Modes - FMask
       else
-        Hl.FoldConfig[idx].Modes := Hl.FoldConfig[idx].Modes - [fmMarkup];
+      if Hl.FoldConfig[idx].Modes * FMask = [] then
+        Hl.FoldConfig[idx].Modes := Hl.FoldConfig[idx].Modes + FMask;
       inc(j);
     end;
   end;
+
+  UpdateMarkupCheckBoxes;
 end;
 
 procedure TEditorMarkupOptionsFrame.chkKWGroupsKeyUp(Sender: TObject; var Key: Word;
@@ -217,13 +282,13 @@
 
 procedure TEditorMarkupOptionsFrame.LanguageComboBoxExit(Sender: TObject);
 var
-  ComboBox: TComboBox absolute Sender;
   tp: TLazSyntaxHighlighter;
   i, j: Integer;
   Hl: TSynCustomFoldHighlighter;
+  FMask: TSynCustomFoldConfigModes;
 begin
   tp := EditorOpts.HighlighterList
-          [EditorOpts.HighlighterList.FindByName(ComboBox.Text)].TheType;
+          [EditorOpts.HighlighterList.FindByName(LanguageComboBox.Text)].TheType;
   FCurHighlighter := GetHighlighter(tp, True);
   FCurFoldInfo := EditorOptionsFoldDefaults[tp];
 
@@ -232,14 +297,18 @@
          (FCurHighlighter is TSynCustomFoldHighlighter)) then exit;
   Hl := TSynCustomFoldHighlighter(FCurHighlighter);
 
+  FMask := [fmMarkup, fmOutline];
+  if not FUseMarkupWordBracket then FMask := FMask - [fmMarkup];
+  if not FUseMarkupOutline then FMask := FMask - [fmOutline];
   for i := 0 to FCurFoldInfo.Count - 1 do begin
-    if Hl.FoldConfig[FCurFoldInfo.Info^[i].Index].SupportedModes * [fmMarkup{, fmOutline}] <> [] then begin
+    if Hl.FoldConfig[FCurFoldInfo.Info^[i].Index].SupportedModes * FMask <> [] then begin
       j := chkKWGroups.Items.Add(FCurFoldInfo.Info^[i].Name);
       chkKWGroups.Checked[j] :=
-        (Hl.FoldConfig[FCurFoldInfo.Info^[i].Index].Modes * [fmMarkup{, fmOutline}] <> []);
+        (Hl.FoldConfig[FCurFoldInfo.Info^[i].Index].Modes * FMask <> []);
       chkKWGroups.Items.Objects[j] := TObject({%H-}Pointer(PtrUInt(i)));
     end;
   end;
+  UpdateMarkupCheckBoxes;
 end;
 
 procedure TEditorMarkupOptionsFrame.LanguageComboBoxKeyDown(Sender: TObject;
@@ -276,6 +345,40 @@
   Result := FoldPage.GetHighlighter(SynType, CreateIfNotExists);
 end;
 
+procedure TEditorMarkupOptionsFrame.UpdateMarkupCheckBoxes;
+var
+  i: LongInt;
+  FMask: TSynCustomFoldConfigModes;
+  Hl: TSynCustomFoldHighlighter;
+begin
+  if not (assigned(FCurHighlighter) and
+         (FCurHighlighter is TSynCustomFoldHighlighter)) then exit;
+  Hl := TSynCustomFoldHighlighter(FCurHighlighter);
+  FModeLock := True;
+  i := chkKWGroups.ItemIndex;
+  if i >= 0 then
+    i := PtrUInt(chkKWGroups.Items.Objects[i]);
+
+  if i >= 0 then begin
+    i := FCurFoldInfo.Info^[i].Index;
+    FMask := [fmMarkup, fmOutline];
+    if not FUseMarkupWordBracket then FMask := FMask - [fmMarkup];
+    if not FUseMarkupOutline then FMask := FMask - [fmOutline];
+
+    cbMarkup.Enabled := fmMarkup in Hl.FoldConfig[i].SupportedModes * FMask;
+    cbMarkup.Checked := fmMarkup in Hl.FoldConfig[i].Modes * FMask;
+    cbOutline.Enabled := fmOutline in Hl.FoldConfig[i].SupportedModes * FMask;
+    cbOutline.Checked := fmOutline in Hl.FoldConfig[i].Modes * FMask;
+  end else
+  begin
+    cbMarkup.Enabled  := false;
+    cbMarkup.Checked  := false;
+    cbOutline.Enabled := false;
+    cbOutline.Checked := false;
+  end;
+  FModeLock := False;
+end;
+
 function TEditorMarkupOptionsFrame.GetTitle: String;
 begin
   Result := lisAutoMarkup;
@@ -314,6 +417,11 @@
   LanguageLabel.Caption := dlgLang;
   divKeyWordGroups.Caption := dlgPasKeywordsMatches;
 
+  cbMarkup.Caption := dlgPasKeywordsMarkup;
+  cbOutline.Caption := dlgPasKeywordsOutline;
+  cbMarkupWordBracket.Caption := dlgMarkupWordBracket;
+  cbMarkupOutline.Caption := dlgMarkupOutline;
+
   with LanguageComboBox.Items do begin
     BeginUpdate;
     for i := 0 to EditorOpts.HighlighterList.Count - 1 do begin
@@ -342,11 +450,17 @@
 
     chkExtPasKeywords.Checked := PasExtendedKeywordsMode;
     dropPasStringKeywords.ItemIndex := ord(PasStringKeywordMode);
+
+    FUseMarkupWordBracket := UseMarkupWordBracket;
+    FUseMarkupOutline := UseMarkupOutline;
   end;
   AutoDelayTrackBarChange(nil);
 
   LanguageComboBox.ItemIndex := 0;
   LanguageComboBoxExit(LanguageComboBox);
+  cbMarkupOutline.Checked := FUseMarkupOutline;
+  cbMarkupWordBracket.Checked := FUseMarkupWordBracket;
+  UpdateMarkupCheckBoxes;
 end;
 
 procedure TEditorMarkupOptionsFrame.WriteSettings(AOptions: TAbstractIDEOptions);
@@ -369,6 +483,9 @@
 
     PasExtendedKeywordsMode := chkExtPasKeywords.Checked;
     PasStringKeywordMode := TSynPasStringMode(dropPasStringKeywords.ItemIndex);
+
+    UseMarkupWordBracket := FUseMarkupWordBracket;
+    UseMarkupOutline := FUseMarkupOutline;
   end;
 end;
 
Index: ide/lazarusidestrconsts.pas
===================================================================
--- ide/lazarusidestrconsts.pas	(revision 52759)
+++ ide/lazarusidestrconsts.pas	(working copy)
@@ -1897,6 +1897,10 @@
   dlgBracketMatchGroup = 'Matching bracket pairs';
   dlgPasExtKeywordsGroup = 'Extended Pascal Keyword Options';
   dlgPasKeywordsMatches = 'Matching Keywords';
+  dlgPasKeywordsMarkup = 'Markup (on caret)';
+  dlgPasKeywordsOutline = 'Outline';
+  dlgMarkupWordBracket = 'Word Brackets on caret (global)';
+  dlgMarkupOutline = 'Outline (global)';
   dlgPasExtKeywords = 'Highlight control statements as keywords';
   dlgPasStringKeywords = 'Highlight "String" keyword(s)';
   dlgPasStringKeywordsOptDefault = 'Default';

Martin Friebe

2016-07-31 19:12

manager   ~0093922

until this is done, I attached the code that will be added to IDE options.
Also compile IDE with
-DSynWithOutlineMarkup

Pascal Riekenberg

2016-08-01 13:30

reporter  

syneditmarkupfoldcoloring.pas_v4.patch (37,102 bytes)
Index: syneditmarkupfoldcoloring.pas
===================================================================
--- syneditmarkupfoldcoloring.pas	(revision 52763)
+++ syneditmarkupfoldcoloring.pas	(working copy)
@@ -50,12 +50,14 @@
 unit SynEditMarkupFoldColoring;
 
 {$mode objfpc}{$H+}
+{.$define SynEditMarkupFoldColoringDebug}
 
 interface
 
 uses
   Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
-  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
+  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
+  LazSynEditText;
 
 type
 
@@ -66,37 +68,39 @@
     Border  : Boolean;
     Ignore  : Boolean; //no color no line
     SrcNode : TSynFoldNodeInfo;
-    LevelBefore, LevelAfter : integer;//needed by non nest nodes
+    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
   end;
 
   TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
   TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
-
   { TSynEditMarkupFoldColors }
 
   TSynEditMarkupFoldColors = class(TSynEditMarkup)
   private
+    function GetFirstCharacterColumn(index: Integer): Byte;
+  private
+    FHighlighter: TSynCustomFoldHighlighter;
     FNestList: TLazSynEditNestedFoldsList;
+
+    // cache
+    FFirstCharacterColumn: Array of Byte;
+    FEndLine: Array of Integer;
+
     FDefaultGroup: integer;
-     // Physical Position
-    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
+    FFoldColorInfos: TMarkupFoldColorInfos;
     Colors : array of TColor;
-
-    {%region invalidating}
-    CurrentY : integer;  //??
-    FCaretY : integer;    // flag identify for refresh begin______
-    FPrevCaretText : string;  // flag identify for refresh begin______
-    {%endregion}
-
+    FPreparedRow: integer;
+    FLastNode: TSynFoldNodeInfo;
     procedure DoMarkupParentFoldAtRow(aRow: Integer);
     procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
-
-    function GetFoldHighLighter: TSynCustomFoldHighlighter;
     procedure SetDefaultGroup(AValue: integer);
+    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
   protected
     // Notifications about Changes to the text
     procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
-    procedure DoCaretChanged(Sender: TObject); override;
+    procedure SetLines(const AValue: TSynEditStrings); override;
+    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
+    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
   public
     constructor Create(ASynEdit : TSynEditBase);
     destructor Destroy; override;
@@ -115,36 +119,21 @@
 
 implementation
 uses
-  SynEdit,SynEditTypes, SynEditMiscProcs;
+  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
+{$IFDEF SynEditMarkupFoldColoringDebug}
+  , SynHighlighterPas
+{$ENDIF}
+  ;
 
-  {%region Sorting FoldInfo -fold}
-  function CompareFI(Item1, Item2: Pointer): Integer;
-  begin
-    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
-    if result = 0 then
-        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
-    if result = 0 then
-        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
-          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
+{$IFDEF SynEditMarkupFoldColoringDebug}
+function FoldTypeToStr(p_FoldType: Pointer): String;
+begin
+  WriteStr(Result, TPascalCodeFoldBlockType(p_FoldType));
+  while length(Result) < 17 do Result := Result + ' ';
+end;
+{$ENDIF}
 
-    SetLength(result, Length(a));
-    for i := 0 to Pred(l.Count) do
-      result[i] := PMarkupFoldColorInfo(l[i])^;
-     l.Free;
-  end;
-  {%endregion}
-
 { TSynEditMarkupFoldColors }
 
 constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
@@ -151,23 +140,36 @@
 begin
   inherited Create(ASynEdit);
 
-  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
+  if Assigned(Lines) then begin
+    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
+    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+    SetLength(FFirstCharacterColumn, Lines.Count);
+    SetLength(FEndLine, Lines.Count);
+  end;
+
+  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(FHighlighter)
+  and not (FHighlighter  is TSynCustomFoldHighlighter) then
+    FHighlighter := nil;
+
+  FDefaultGroup := 0;
+
+  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
   FNestList.ResetFilter;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
-  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
-  FNestList.IncludeOpeningOnLine := True; //False; //
+  FNestList.FoldGroup := FDefaultGroup;
+  FNestList.FoldFlags :=  [sfbIncludeDisabled];
+  FNestList.IncludeOpeningOnLine := True;
 
   MarkupInfo.Foreground := clGreen;
-  MarkupInfo.Background := clNone; //clFuchsia;
+  MarkupInfo.Background := clNone;
   MarkupInfo.Style := [];
   MarkupInfo.StyleMask := [];
-  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
+  MarkupInfo.FrameEdges:= sfeLeft;
 
   SetLength(Colors, 5);
   Colors[0] := clRed;
   Colors[1] := $000098F7; //orange
   Colors[2] := $0022CC40; //green
-  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
   Colors[3] := $00FF682A; //blue
   Colors[4] := $00CF00C4; //purple
 end;
@@ -174,6 +176,10 @@
 
 destructor TSynEditMarkupFoldColors.Destroy;
 begin
+  if Assigned(Lines) then begin
+    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+  end;
   FreeAndNil(FNestList);
   inherited Destroy;
 end;
@@ -182,23 +188,27 @@
   const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
   const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
 var
-  i,x2both : integer;
+  i, x2both: integer;
 begin
   Result := nil;
-  if (CurrentY = aRow) then begin
+  if not Assigned(FHighlighter) then exit;
+  if (FPreparedRow = aRow) then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
+    {$ENDIF}
 
-    x2both := -3; //flag
-    for i := 0 to length(FHighlights)-1 do
-      with FHighlights[i] do
+    x2both := -3;
+    for i := 0 to length(FFoldColorInfos)-1 do
+      with FFoldColorInfos[i] do
         if not Ignore
         and (X < X2)
         and (ColorIdx >= 0)
         and (aStartCol.Logical >= x)
-        and (aStartCol.Logical < X2) then
-        begin
-          //MarkupInfo.FrameColor:= clGreen; //debug
-          if x2both = -3 then //first call flag
-          begin
+        and (aStartCol.Logical < X2) then begin
+          {$IFDEF SynEditMarkupFoldColoringDebug}
+          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
+          {$ENDIF}
+          if x2both = -3 then begin //first call flag
             MarkupInfo.FrameColor:= clNone;
             MarkupInfo.Foreground:= clNone;
             MarkupInfo.Background:= clNone;
@@ -209,25 +219,14 @@
           Result := MarkupInfo;
           x2both := max(x2both, x2);
           MarkupInfo.SetFrameBoundsLog(x, x2both);
-          if Border then
-          begin
+          if Border then begin
             MarkupInfo.FrameColor:= Colors[ColorIdx];
-            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
-          end
-          else
+            MarkupInfo.FrameEdges:= sfeLeft;
+          end else begin
+            MarkupInfo.FrameColor:= clNone;
+            MarkupInfo.FrameEdges:= sfeNone;
             MarkupInfo.Foreground := Colors[ColorIdx];
-
-          //MarkupInfo.FrameEdges:= sfeAround; //debug
-
-          {//2nd debug
-          if x > x2 then
-          begin
-            MarkupInfo.Background:= clYellow;
-            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
-            MarkupInfo.FrameColor:= clBlue; //debug
-          end;}
-
-          //break;
+          end;
         end;
   end;
 end;
@@ -237,36 +236,85 @@
   const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
 var i : integer;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
+  {$ENDIF}
+  if not Assigned(FHighlighter)
+  or (FPreparedRow <> aRow) then
+    exit;
+
   ANextLog := -1;
   ANextPhys := -1;
-  if (CurrentY = aRow)  then
-  for i := 0 to length(FHighlights)-1  do
-    with FHighlights[i] do
-    begin
-      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
-        //continue;
-      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
-      begin
-        ANextLog := FHighlights[i].X;
+  for i := 0 to length(FFoldColorInfos)-1  do
+    with FFoldColorInfos[i] do begin
+      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
+        ANextLog := FFoldColorInfos[i].X;
         break;
       end;
     end;
 end;
 
+function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
+var
+  l: String;
+  p: Integer;
+begin
+  l := SynEdit.Lines[index];
+  p := 1;
+  while not (l[p] in [#13, #10, #0])
+  and (l[p] = #32) do inc(p);
+  if p > 255 then p := 255;
+  Result := p;
+end;
+
 procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
 var
-  i,lvl,z : integer; //iterate parents fold
+  i,lvl,z: integer;
 
   procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
+<<<<<<< .mine
+  var
+    p, s, l: integer;
+||||||| .r52761
+  var x,j : integer;
+=======
+>>>>>>> .r52763
   begin
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    // get column of first character in row
+    s := Length(FFirstCharacterColumn);
+    if (s <= ANode.LineIndex)
+    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
+      p := FirstCharacterColumn[ANode.LineIndex];
+      if s > ANode.LineIndex then begin
+        FFirstCharacterColumn[ANode.LineIndex] := p;
+      end else begin
+        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    s := Length(FEndLine);
+    if (s <= ANode.LineIndex)
+    or (FEndLine[ANode.LineIndex] = 0) then begin
+      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
+      if s > ANode.LineIndex then begin
+        FEndLine[ANode.LineIndex] := l;
+      end else begin
+        DebugLn('!!! FEndLine-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z+1);
+    with FFoldColorInfos[z] do begin
+
       SrcNode:= ANode; //needed by close node
-      Border := ANode.LineIndex + 1 <> aRow;
-      X  := ANode.LogXStart + 1;
-      Y  := aRow;//ANode.LineIndex + 1;
-      X2 := X+1; //ANode.LogXEnd + 1;
+      Border := ToPos(ANode.LineIndex) <> aRow;
+      if s <= ANode.LineIndex then
+        X  := p
+      else
+        X  := FFirstCharacterColumn[ANode.LineIndex];
+      Y  := aRow;
+      X2 := X + 1;
       Ignore := False;
 
       if Border and (sfaOutlineNoLine in ANode.FoldAction) then
@@ -273,7 +321,9 @@
         Ignore := True;
       if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
         Ignore := True;
-        ColorIdx := lvl mod (length(Colors))
+      Level := lvl;
+      ColorIdx := Max(0, lvl) mod (length(Colors));
+      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
     end;
   end;
 
@@ -281,60 +331,62 @@
   y, lvlB,lvlA: Integer;
   TmpNode: TSynFoldNodeInfo;
   NestCount : integer;
-  procedure Later(var J:integer);
-  begin
-    inc(J);
-  end;
-  function Allowed(J: integer):boolean;
-  begin
-    result := J < NestCount;
-  end;
 
 begin
-  y := aRow-1;
+  y := ToIdx(aRow);
   FNestList.Line := y;
   NestCount := FNestList.Count;
+  FHighlighter.CurrentLines := Lines;
 
   lvl := 0;
   i := 0;
-  while Allowed(i) do
-  begin
-
+  while i < NestCount do begin
     TmpNode := FNestList.HLNode[i];
-    //find till valid
-    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
-    begin
-      Later(i);
-      TmpNode := FNestList.HLNode[i];
-    end;
+    if (sfaOutline in TmpNode.FoldAction)
+    and not (sfaInvalid in TmpNode.FoldAction) then
+      //avoid bug of IncludeOpeningOnLine := False;
+      if (sfaOpen in TmpNode.FoldAction)
+      and (TmpNode.LineIndex + 1 = aRow) then begin
+        {do nothing here}
+      end else begin
+        lvlB := lvl;
 
-    if (sfaOutline in TmpNode.FoldAction ) then
-    //avoid bug of IncludeOpeningOnLine := False;
-    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
-    begin {do nothing here} end
-    else
-    begin
-      lvlB := lvl;
+        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
+          inc(lvl)
+        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+          dec(lvl);
+        //if (FLastNode.LineIndex >= 0)
+        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+        // inc(lvl);
 
-      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-        inc(lvl);
-      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
-        dec(lvl);
+        AddVerticalLine(TmpNode);
 
-      AddVerticalLine(TmpNode);
-      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-        inc(lvl);
+        //if (z > 0)
+        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
+        //  // if child is on same x-pos keep level
+        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
+        //    lvl := FFoldColorInfos[z - 1].Level;
+        //    FFoldColorInfos[z].Level := lvl;
+        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+        //  end;
+        //end;
 
-      lvlA := lvl;
+        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
+          inc(lvl);
 
-      with FHighlights[z] do begin
-        LevelBefore := lvlB;
-        LevelAfter  := lvlA;
+        if sfaOpen in TmpNode.FoldAction then
+          FLastNode := TmpNode;
+
+        lvlA := lvl;
+
+        with FFoldColorInfos[z] do begin
+          LevelBefore := lvlB;
+          LevelAfter  := lvlA;
+        end;
       end;
-    end;
-
-    Later(i);
-    //break; //debug
+    inc(i);
   end;
 end;
 
@@ -345,33 +397,29 @@
   procedure AddHighlight( ANode: TSynFoldNodeInfo );
   var x,j : integer;
   begin
-        //don't replace; don't add when already found
     x  := ANode.LogXStart + 1;
-
     if ANode.LogXStart < ANode.LogXEnd then
-    for j := 0 to Pred(length(FHighlights)) do
-      if (FHighlights[j].X = x)
-      and (FHighlights[j].Border)
-      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
-      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
+    for j := 0 to Pred(length(FFoldColorInfos)) do
+      if (FFoldColorInfos[j].X = x)
+      and (FFoldColorInfos[j].Border)
+      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
+      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
       then begin
-       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
-       FHighlights[j].Border := False
-
+       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
+       FFoldColorInfos[j].Border := False
       end;
 
-    //exit; //debug
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z + 1);
+    with FFoldColorInfos[z] do begin
       Border := False;
       SrcNode:= ANode; //needed by close node
       Y  := ANode.LineIndex + 1;
       X  := ANode.LogXStart + 1;
       X2 := ANode.LogXEnd + 1;
-      //ColorIdx := lvl;
+      Level := lvl;
       if not (sfaOutlineNocolor in ANode.FoldAction) then
-         ColorIdx := lvl mod (length(Colors))
+         ColorIdx := Max(0, lvl) mod (length(Colors))
       else
          ColorIdx := -1;
     end;
@@ -378,91 +426,105 @@
   end;
 
 var
-  y,i,j,lvlB,lvlA : integer;
-  HL: TSynCustomFoldHighlighter;
+  LineIdx,i,j,lvlB,lvlA : integer;
   NodeList: TLazSynFoldNodeInfoList;
   TmpNode: TSynFoldNodeInfo;
-  Found : boolean;
+  Found: boolean;
 begin
-  y := aRow -1;
+  LineIdx := ToIdx(aRow);
 
-  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-  HL.CurrentLines := Lines;
-  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
+  FHighlighter.CurrentLines := Lines;
+  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
 
-  NodeList := HL.FoldNodeInfo[y];
+  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
   NodeList.AddReference;
   try
     NodeList.ActionFilter := [sfaOutline];
-    //NodeList.FoldFlags:= [sfbIncludeDisabled];
     lvl := 0;
-    J := Length(FHighlights)-1;
+    J := Length(FFoldColorInfos) - 1;
     if J >=0 then
-      lvl := max(0,FHighlights[J].LevelAfter);
+      lvl := max(0,FFoldColorInfos[J].LevelAfter);
     i := 0;
     repeat
       TmpNode := NodeList[i];
 
-      //find till valid
-      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
-      begin
-        inc(i);
-        TmpNode := NodeList[i];
-      end;
-      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
-        if sfaOpen in TmpNode.FoldAction then
-        begin
+      if not (sfaInvalid in TmpNode.FoldAction)
+      and (sfaOutline in TmpNode.FoldAction) then begin
+        if sfaOpen in TmpNode.FoldAction then begin
           lvlB := lvl;
 
           if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-            inc(lvl);
-          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+            inc(lvl)
+          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
             dec(lvl);
+          //if (FLastNode.LineIndex >= 0)
+          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+          // inc(lvl);
 
           AddHighlight(TmpNode);
-          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
+
+          //if (z > 0)
+          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
+          //begin
+          //  // if child is on same x-pos keep level
+          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
+          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
+          //    lvl := FFoldColorInfos[z - 1].Level;
+          //    FFoldColorInfos[z].Level := lvl;
+          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+          //  end;
+          //end;
+
+          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
             inc(lvl);
+
           lvlA := lvl;
 
-          with FHighlights[z] do begin
+          if sfaOpen in TmpNode.FoldAction then
+            FLastNode := TmpNode;
+
+          with FFoldColorInfos[z] do begin
             LevelBefore := lvlB;
             LevelAfter  := lvlA;
           end;
-
-        end
-        else
-        if sfaClose in TmpNode.FoldAction then
-        begin
+        end else if sfaClose in TmpNode.FoldAction then begin
           Found := False;
-          for j := Length(FHighlights)-1 downto 0 do begin
-            with FHighlights[j].SrcNode do begin
-              if  (FoldType = TmpNode.FoldType) and
-                (FoldGroup = TmpNode.FoldGroup) and
-                (sfaOpen in FoldAction) and
-                // (FoldLvlEnd = TmpNode.FoldLvlStart)
-                (NestLvlEnd = TmpNode.NestLvlStart)
-
-                then begin
-                  lvl := FHighlights[j].ColorIdx;
-                  lvlB := FHighlights[j].LevelBefore;
-                  Found := True;
-                  break;
-                end;
+          for j := Length(FFoldColorInfos)-1 downto 0 do begin
+            with FFoldColorInfos[j].SrcNode do begin
+              if (FoldType = TmpNode.FoldType)
+              and (FoldGroup = TmpNode.FoldGroup)
+              and (sfaOpen in FoldAction)
+              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
+                lvlB := lvl;
+                lvl := FFoldColorInfos[j].Level;
+                lvlA := FFoldColorInfos[j].LevelAfter;
+                FLastNode := TmpNode;
+                Found := True;
+                break;
+              end;
             end;
           end;
           if Found then begin
             AddHighlight(TmpNode);
-            lvl := lvlB;
+            with FFoldColorInfos[z] do begin
+              LevelBefore := lvlB;
+              LevelAfter  := lvlA;
+            end;
+            // if found opening position is behind closing position:
+            // delete this as it does not have to be drawn
+            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
+              for j := j to z - 1 do begin
+                FFoldColorInfos[j] := FFoldColorInfos[j+1];
+              end;
+              SetLength(FFoldColorInfos, z);
+            end;
           end;
-
-          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-            //inc(lvl);
         end;
       end;
-
       inc(i);
     until i >= NodeList.Count;
-
   finally
     NodeList.ReleaseReference;
   end;
@@ -469,197 +531,274 @@
 end;
 
 procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
+var
+  i, LastX, j: Integer;
 begin
-  CurrentY := aRow;
-  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('PrepareMarkupForRow %d', [aRow]);
+  {$ENDIF}
+  if not Assigned(FHighlighter) then exit;
+  FPreparedRow := aRow;
+  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
 
   if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
     exit;
 
-  //DoMarkupFoldAtRow(aRow);
+  // invalidate fLastNode
+  FLastNode.LineIndex := -1;
+
   DoMarkupParentFoldAtRow(aRow);
   DoMarkupParentCloseFoldAtRow(aRow);
-  //DoMarkupRangeFoldAtRow(aRow);
 
-  FHighlights := SortLeftMostFI(FHighlights);
+  // delete parents with bigger x
+  // to keep out mis indented blocks
+  LastX := MaxInt;
+  for i := length(FFoldColorInfos) - 1 downto 0 do begin
+    if FFoldColorInfos[i].X > LastX then begin
+      for j := i to length(FFoldColorInfos) - 2 do begin
+        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
+      end;
+      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
+    end;
+    LastX := FFoldColorInfos[i].X;
+  end;
 end;
 
 procedure TSynEditMarkupFoldColors.EndMarkup;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('EndMarkup');
+  {$ENDIF}
   inherited EndMarkup;
   FNestList.Clear; // for next markup start
 end;
 
-function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
-begin
-  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-end;
-
 procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
 begin
   if FDefaultGroup = AValue then Exit;
   FDefaultGroup := AValue;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+  FNestList.FoldGroup := FDefaultGroup;
 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;
+  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
   var
-    y,i,LCnt : integer;
-    HL: TSynCustomFoldHighlighter;
-    NodeList: TLazSynFoldNodeInfoList;
-    TmpNode, CloseNode: TSynFoldNodeInfo;
+    lCount, lLineIdx, i, lAnz: Integer;
+    lNode: TSynFoldNodeInfo;
+  begin
+    lLineIdx := ToIdx(pLine);
+    pNestList.Line := lLineIdx;
+    lCount := pNestList.Count;
+    SetLength(pList, lCount);
+    lAnz := 0;
+    for i := 0 to lCount - 1 do begin
+      lNode := pNestList.HLNode[i];
+      if (sfaInvalid in lNode.FoldAction)
+      or (
+        (sfaOpen in lNode.FoldAction)
+        and (lNode.LineIndex = lLineIdx)
+      ) then
+        Continue;
 
-    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?)
+      pList[i] := lNode;
+      inc(lAnz);
     end;
+    SetLength(pList, lAnz);
+  end;
 
-  begin
-    Result := -1;
-    y := aRow -1;
+var
+  i, lMinAnz, lEndLine, j, l: integer;
+  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
+begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
+  {$ENDIF}
 
-    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
+  if not Assigned(FHighlighter) then exit;
+  FHighlighter.CurrentLines := Lines;
+  if EndLine < 0 then
+    EndLine := StartLine
+  else
+    // endline seems to be the first line after the change
+    EndLine := EndLine - 1;
+  lEndLine := EndLine;
 
-    NodeList := HL.FoldNodeInfo[y];
-    NodeList.AddReference;
-    try
-      NodeList.ActionFilter := [sfaOpen];
-      i := 0;
-      repeat
-        TmpNode := NodeList[i];
+  SetLength(lStartNestList, 0);
+  SetLength(lEndNestList, 0);
 
-        if TmpNode.LogXStart < X-1 then
-        begin
-          inc(i);
-          continue;
-        end;
+  FillNestList(lStartNestList, StartLine, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at Start:');
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-        //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;
+  FillNestList(lEndNestList, EndLine, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at End:');
+  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-        inc(i);
-      until i >= NodeList.Count;
-
-    finally
-      NodeList.ReleaseReference;
+  // delete all nodes in lEndNodeList which where active at StartLine
+  // to get the nodes which reach behind EndLine
+  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
+  for i := 0 to lMinAnz - 1 do begin
+    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
+    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
+    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
+    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
+    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
+      for j := 0 to length(lEndNestList) - 2 do
+        lEndNestList[j] := lEndNestList[j + 1];
+      SetLength(lEndNestList, Length(lEndNestList) - 1);
+    end else begin
+      break
     end;
   end;
 
+  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
+    // deeper fold group than StartLine: fold group ends after EndLine
+    // find real EndLine (end line of first remaining fold node)
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   Remaining Nodes:');
+    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex]]);
+    {$ENDIF}
+    // does position of first character change for remaining node?
+    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
+      // position of first character changed -> find endline
+      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
+  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;
+  // check for changes of endline for node which are active at StartLine
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    if sfaOutline in FoldAction then begin
+      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
+      if l <> FEndLine[LineIndex] then begin
+        FEndLine[LineIndex] := l;
+        lEndLine := Max(lEndLine, l);
       end;
     end;
 
-    if n < 0 then exit;
+  // invalidate cache
+  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
+    FFirstCharacterColumn[i] := 0;
+    FEndLine[i] := 0;
+  end;
 
-    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);
+  if lEndLine > EndLine then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
+    {$ENDIF}
+    InvalidateSynLines(EndLine + 1 , lEndLine);
+  end;
+end;
+
+procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
+var
+  old: TSynEditStrings;
+begin
+  old := Lines;
+  if Assigned(old)
+  and (AValue <> old) then begin
+    // change:
+    // remove Changehandler
+    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+  end;
+  inherited SetLines(AValue);
+  if (AValue <> old) then begin
+    // change:
+    if Assigned(AValue) then begin
+      // set cache size
+      SetLength(FFirstCharacterColumn, AValue.Count);
+      SetLength(FEndLine, AValue.Count);
+      // add Changehandler
+      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
+      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+    end else begin
+      // clear cache
+      SetLength(FFirstCharacterColumn, 0);
+      SetLength(FEndLine, 0);
     end;
+  end;
+end;
 
-  end;
+procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
+                                        aIndex, aCount: Integer);
 var
-  EndFoldLine,y : integer;
+  absCount,
+  idx, i: 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);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  idx := ToIdx(aIndex);
+  if aCount < 0 then begin
+    // lines deleted
+    absCount := Abs(aCount);
+    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
+      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
+      FEndLine[i] := FEndLine[i + absCount];
+    end;
   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}
+  SetLength(FFirstCharacterColumn, Sender.Count);
+  SetLength(FEndLine, Sender.Count);
+  if (aCount > 0) then begin
+    if idx >= 0 then begin
+      // lines added
+      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
+        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
+        FEndLine[i + aCount] := FEndLine[i];
+      end;
+      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end else begin
+      // first lines will be inserted
+      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end;
+  end;
 end;
 
-procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
-var Y : integer;
+procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
+  aIndex, aCount: Integer);
+var
+  newHighlighter: TSynCustomFoldHighlighter;
 begin
-  Y := Caret.LineBytePos.y;
-  if Y = FCaretY then exit;
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  if (aIndex <> -1)
+  or (aCount <> -1) then
+    exit;
 
-  FCaretY := Y;
-  FPrevCaretText := Caret.LineText;
-end;
+  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(newHighlighter)
+  and not (newHighlighter is TSynCustomFoldHighlighter) then
+    newHighlighter := nil;
 
+  if (newHighlighter = FHighlighter) then
+    exit;
 
+  FHighlighter := newHighlighter;
 
+  FreeAndNil(FNestList);
+  if Assigned(FHighlighter) then begin
+    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
+    FNestList.ResetFilter;
+    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
+    FNestList.IncludeOpeningOnLine := True; //False; //
+  end;
+end;
+
 end.
 

Pascal Riekenberg

2016-08-01 14:15

reporter  

syneditmarkupfoldcoloring.pas_v5.patch (37,593 bytes)
Index: syneditmarkupfoldcoloring.pas
===================================================================
--- syneditmarkupfoldcoloring.pas	(revision 52763)
+++ syneditmarkupfoldcoloring.pas	(working copy)
@@ -50,12 +50,14 @@
 unit SynEditMarkupFoldColoring;
 
 {$mode objfpc}{$H+}
+{$define SynEditMarkupFoldColoringDebug}
 
 interface
 
 uses
   Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
-  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
+  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
+  LazSynEditText;
 
 type
 
@@ -66,37 +68,39 @@
     Border  : Boolean;
     Ignore  : Boolean; //no color no line
     SrcNode : TSynFoldNodeInfo;
-    LevelBefore, LevelAfter : integer;//needed by non nest nodes
+    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
   end;
 
   TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
   TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
-
   { TSynEditMarkupFoldColors }
 
   TSynEditMarkupFoldColors = class(TSynEditMarkup)
   private
+    function GetFirstCharacterColumn(index: Integer): Byte;
+  private
+    FHighlighter: TSynCustomFoldHighlighter;
     FNestList: TLazSynEditNestedFoldsList;
+
+    // cache
+    FFirstCharacterColumn: Array of Byte;
+    FEndLine: Array of Integer;
+
     FDefaultGroup: integer;
-     // Physical Position
-    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
+    FFoldColorInfos: TMarkupFoldColorInfos;
     Colors : array of TColor;
-
-    {%region invalidating}
-    CurrentY : integer;  //??
-    FCaretY : integer;    // flag identify for refresh begin______
-    FPrevCaretText : string;  // flag identify for refresh begin______
-    {%endregion}
-
+    FPreparedRow: integer;
+    FLastNode: TSynFoldNodeInfo;
     procedure DoMarkupParentFoldAtRow(aRow: Integer);
     procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
-
-    function GetFoldHighLighter: TSynCustomFoldHighlighter;
     procedure SetDefaultGroup(AValue: integer);
+    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
   protected
     // Notifications about Changes to the text
     procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
-    procedure DoCaretChanged(Sender: TObject); override;
+    procedure SetLines(const AValue: TSynEditStrings); override;
+    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
+    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
   public
     constructor Create(ASynEdit : TSynEditBase);
     destructor Destroy; override;
@@ -115,36 +119,21 @@
 
 implementation
 uses
-  SynEdit,SynEditTypes, SynEditMiscProcs;
+  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
+{$IFDEF SynEditMarkupFoldColoringDebug}
+  , SynHighlighterPas
+{$ENDIF}
+  ;
 
-  {%region Sorting FoldInfo -fold}
-  function CompareFI(Item1, Item2: Pointer): Integer;
-  begin
-    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
-    if result = 0 then
-        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
-    if result = 0 then
-        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
-          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
+{$IFDEF SynEditMarkupFoldColoringDebug}
+function FoldTypeToStr(p_FoldType: Pointer): String;
+begin
+  WriteStr(Result, TPascalCodeFoldBlockType(p_FoldType));
+  while length(Result) < 17 do Result := Result + ' ';
+end;
+{$ENDIF}
 
-    SetLength(result, Length(a));
-    for i := 0 to Pred(l.Count) do
-      result[i] := PMarkupFoldColorInfo(l[i])^;
-     l.Free;
-  end;
-  {%endregion}
-
 { TSynEditMarkupFoldColors }
 
 constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
@@ -151,23 +140,36 @@
 begin
   inherited Create(ASynEdit);
 
-  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
+  if Assigned(Lines) then begin
+    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
+    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+    SetLength(FFirstCharacterColumn, Lines.Count);
+    SetLength(FEndLine, Lines.Count);
+  end;
+
+  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(FHighlighter)
+  and not (FHighlighter  is TSynCustomFoldHighlighter) then
+    FHighlighter := nil;
+
+  FDefaultGroup := 0;
+
+  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
   FNestList.ResetFilter;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
-  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
-  FNestList.IncludeOpeningOnLine := True; //False; //
+  FNestList.FoldGroup := FDefaultGroup;
+  FNestList.FoldFlags :=  [sfbIncludeDisabled];
+  FNestList.IncludeOpeningOnLine := True;
 
   MarkupInfo.Foreground := clGreen;
-  MarkupInfo.Background := clNone; //clFuchsia;
+  MarkupInfo.Background := clNone;
   MarkupInfo.Style := [];
   MarkupInfo.StyleMask := [];
-  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
+  MarkupInfo.FrameEdges:= sfeLeft;
 
   SetLength(Colors, 5);
   Colors[0] := clRed;
   Colors[1] := $000098F7; //orange
   Colors[2] := $0022CC40; //green
-  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
   Colors[3] := $00FF682A; //blue
   Colors[4] := $00CF00C4; //purple
 end;
@@ -174,6 +176,10 @@
 
 destructor TSynEditMarkupFoldColors.Destroy;
 begin
+  if Assigned(Lines) then begin
+    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+  end;
   FreeAndNil(FNestList);
   inherited Destroy;
 end;
@@ -182,23 +188,27 @@
   const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
   const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
 var
-  i,x2both : integer;
+  i, x2both: integer;
 begin
   Result := nil;
-  if (CurrentY = aRow) then begin
+  if not Assigned(FHighlighter) then exit;
+  if (FPreparedRow = aRow) then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
+    {$ENDIF}
 
-    x2both := -3; //flag
-    for i := 0 to length(FHighlights)-1 do
-      with FHighlights[i] do
+    x2both := -3;
+    for i := 0 to length(FFoldColorInfos)-1 do
+      with FFoldColorInfos[i] do
         if not Ignore
         and (X < X2)
         and (ColorIdx >= 0)
         and (aStartCol.Logical >= x)
-        and (aStartCol.Logical < X2) then
-        begin
-          //MarkupInfo.FrameColor:= clGreen; //debug
-          if x2both = -3 then //first call flag
-          begin
+        and (aStartCol.Logical < X2) then begin
+          {$IFDEF SynEditMarkupFoldColoringDebug}
+          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
+          {$ENDIF}
+          if x2both = -3 then begin //first call flag
             MarkupInfo.FrameColor:= clNone;
             MarkupInfo.Foreground:= clNone;
             MarkupInfo.Background:= clNone;
@@ -209,25 +219,14 @@
           Result := MarkupInfo;
           x2both := max(x2both, x2);
           MarkupInfo.SetFrameBoundsLog(x, x2both);
-          if Border then
-          begin
+          if Border then begin
             MarkupInfo.FrameColor:= Colors[ColorIdx];
-            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
-          end
-          else
+            MarkupInfo.FrameEdges:= sfeLeft;
+          end else begin
+            MarkupInfo.FrameColor:= clNone;
+            MarkupInfo.FrameEdges:= sfeNone;
             MarkupInfo.Foreground := Colors[ColorIdx];
-
-          //MarkupInfo.FrameEdges:= sfeAround; //debug
-
-          {//2nd debug
-          if x > x2 then
-          begin
-            MarkupInfo.Background:= clYellow;
-            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
-            MarkupInfo.FrameColor:= clBlue; //debug
-          end;}
-
-          //break;
+          end;
         end;
   end;
 end;
@@ -237,36 +236,80 @@
   const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
 var i : integer;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
+  {$ENDIF}
+  if not Assigned(FHighlighter)
+  or (FPreparedRow <> aRow) then
+    exit;
+
   ANextLog := -1;
   ANextPhys := -1;
-  if (CurrentY = aRow)  then
-  for i := 0 to length(FHighlights)-1  do
-    with FHighlights[i] do
-    begin
-      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
-        //continue;
-      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
-      begin
-        ANextLog := FHighlights[i].X;
+  for i := 0 to length(FFoldColorInfos)-1  do
+    with FFoldColorInfos[i] do begin
+      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
+        ANextLog := FFoldColorInfos[i].X;
         break;
       end;
     end;
 end;
 
+function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
+var
+  l: String;
+  p: Integer;
+begin
+  l := SynEdit.Lines[index];
+  p := 1;
+  while not (l[p] in [#13, #10, #0])
+  and (l[p] = #32) do inc(p);
+  if p > 255 then p := 255;
+  Result := p;
+end;
+
 procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
 var
-  i,lvl,z : integer; //iterate parents fold
+  i,lvl,z: integer;
 
   procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
+  var
+    p, s, l: integer;
   begin
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    // get column of first character in row
+    s := Length(FFirstCharacterColumn);
+    if (s <= ANode.LineIndex)
+    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
+      p := FirstCharacterColumn[ANode.LineIndex];
+      if s > ANode.LineIndex then begin
+        FFirstCharacterColumn[ANode.LineIndex] := p;
+      end else begin
+        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    s := Length(FEndLine);
+    if (s <= ANode.LineIndex)
+    or (FEndLine[ANode.LineIndex] = 0) then begin
+      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
+      if s > ANode.LineIndex then begin
+        FEndLine[ANode.LineIndex] := l;
+      end else begin
+        DebugLn('!!! FEndLine-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z+1);
+    with FFoldColorInfos[z] do begin
+
       SrcNode:= ANode; //needed by close node
-      Border := ANode.LineIndex + 1 <> aRow;
-      X  := ANode.LogXStart + 1;
-      Y  := aRow;//ANode.LineIndex + 1;
-      X2 := X+1; //ANode.LogXEnd + 1;
+      Border := ToPos(ANode.LineIndex) <> aRow;
+      if s <= ANode.LineIndex then
+        X  := p
+      else
+        X  := FFirstCharacterColumn[ANode.LineIndex];
+      Y  := aRow;
+      X2 := X + 1;
       Ignore := False;
 
       if Border and (sfaOutlineNoLine in ANode.FoldAction) then
@@ -273,7 +316,9 @@
         Ignore := True;
       if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
         Ignore := True;
-        ColorIdx := lvl mod (length(Colors))
+      Level := lvl;
+      ColorIdx := Max(0, lvl) mod (length(Colors));
+      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
     end;
   end;
 
@@ -281,60 +326,62 @@
   y, lvlB,lvlA: Integer;
   TmpNode: TSynFoldNodeInfo;
   NestCount : integer;
-  procedure Later(var J:integer);
-  begin
-    inc(J);
-  end;
-  function Allowed(J: integer):boolean;
-  begin
-    result := J < NestCount;
-  end;
 
 begin
-  y := aRow-1;
+  y := ToIdx(aRow);
   FNestList.Line := y;
   NestCount := FNestList.Count;
+  FHighlighter.CurrentLines := Lines;
 
   lvl := 0;
   i := 0;
-  while Allowed(i) do
-  begin
-
+  while i < NestCount do begin
     TmpNode := FNestList.HLNode[i];
-    //find till valid
-    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
-    begin
-      Later(i);
-      TmpNode := FNestList.HLNode[i];
-    end;
+    if (sfaOutline in TmpNode.FoldAction)
+    and not (sfaInvalid in TmpNode.FoldAction) then
+      //avoid bug of IncludeOpeningOnLine := False;
+      if (sfaOpen in TmpNode.FoldAction)
+      and (TmpNode.LineIndex + 1 = aRow) then begin
+        {do nothing here}
+      end else begin
+        lvlB := lvl;
 
-    if (sfaOutline in TmpNode.FoldAction ) then
-    //avoid bug of IncludeOpeningOnLine := False;
-    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
-    begin {do nothing here} end
-    else
-    begin
-      lvlB := lvl;
+        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
+          inc(lvl)
+        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+          dec(lvl);
+        //if (FLastNode.LineIndex >= 0)
+        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+        // inc(lvl);
 
-      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-        inc(lvl);
-      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
-        dec(lvl);
+        AddVerticalLine(TmpNode);
 
-      AddVerticalLine(TmpNode);
-      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-        inc(lvl);
+        //if (z > 0)
+        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
+        //  // if child is on same x-pos keep level
+        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
+        //    lvl := FFoldColorInfos[z - 1].Level;
+        //    FFoldColorInfos[z].Level := lvl;
+        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+        //  end;
+        //end;
 
-      lvlA := lvl;
+        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
+          inc(lvl);
 
-      with FHighlights[z] do begin
-        LevelBefore := lvlB;
-        LevelAfter  := lvlA;
+        if sfaOpen in TmpNode.FoldAction then
+          FLastNode := TmpNode;
+
+        lvlA := lvl;
+
+        with FFoldColorInfos[z] do begin
+          LevelBefore := lvlB;
+          LevelAfter  := lvlA;
+        end;
       end;
-    end;
-
-    Later(i);
-    //break; //debug
+    inc(i);
   end;
 end;
 
@@ -345,33 +392,29 @@
   procedure AddHighlight( ANode: TSynFoldNodeInfo );
   var x,j : integer;
   begin
-        //don't replace; don't add when already found
     x  := ANode.LogXStart + 1;
-
     if ANode.LogXStart < ANode.LogXEnd then
-    for j := 0 to Pred(length(FHighlights)) do
-      if (FHighlights[j].X = x)
-      and (FHighlights[j].Border)
-      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
-      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
+    for j := 0 to Pred(length(FFoldColorInfos)) do
+      if (FFoldColorInfos[j].X = x)
+      and (FFoldColorInfos[j].Border)
+      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
+      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
       then begin
-       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
-       FHighlights[j].Border := False
-
+       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
+       FFoldColorInfos[j].Border := False
       end;
 
-    //exit; //debug
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z + 1);
+    with FFoldColorInfos[z] do begin
       Border := False;
       SrcNode:= ANode; //needed by close node
       Y  := ANode.LineIndex + 1;
       X  := ANode.LogXStart + 1;
       X2 := ANode.LogXEnd + 1;
-      //ColorIdx := lvl;
+      Level := lvl;
       if not (sfaOutlineNocolor in ANode.FoldAction) then
-         ColorIdx := lvl mod (length(Colors))
+         ColorIdx := Max(0, lvl) mod (length(Colors))
       else
          ColorIdx := -1;
     end;
@@ -378,91 +421,105 @@
   end;
 
 var
-  y,i,j,lvlB,lvlA : integer;
-  HL: TSynCustomFoldHighlighter;
+  LineIdx,i,j,lvlB,lvlA : integer;
   NodeList: TLazSynFoldNodeInfoList;
   TmpNode: TSynFoldNodeInfo;
-  Found : boolean;
+  Found: boolean;
 begin
-  y := aRow -1;
+  LineIdx := ToIdx(aRow);
 
-  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-  HL.CurrentLines := Lines;
-  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
+  FHighlighter.CurrentLines := Lines;
+  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
 
-  NodeList := HL.FoldNodeInfo[y];
+  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
   NodeList.AddReference;
   try
     NodeList.ActionFilter := [sfaOutline];
-    //NodeList.FoldFlags:= [sfbIncludeDisabled];
     lvl := 0;
-    J := Length(FHighlights)-1;
+    J := Length(FFoldColorInfos) - 1;
     if J >=0 then
-      lvl := max(0,FHighlights[J].LevelAfter);
+      lvl := max(0,FFoldColorInfos[J].LevelAfter);
     i := 0;
     repeat
       TmpNode := NodeList[i];
 
-      //find till valid
-      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
-      begin
-        inc(i);
-        TmpNode := NodeList[i];
-      end;
-      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
-        if sfaOpen in TmpNode.FoldAction then
-        begin
+      if not (sfaInvalid in TmpNode.FoldAction)
+      and (sfaOutline in TmpNode.FoldAction) then begin
+        if sfaOpen in TmpNode.FoldAction then begin
           lvlB := lvl;
 
           if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-            inc(lvl);
-          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+            inc(lvl)
+          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
             dec(lvl);
+          //if (FLastNode.LineIndex >= 0)
+          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+          // inc(lvl);
 
           AddHighlight(TmpNode);
-          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
+
+          //if (z > 0)
+          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
+          //begin
+          //  // if child is on same x-pos keep level
+          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
+          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
+          //    lvl := FFoldColorInfos[z - 1].Level;
+          //    FFoldColorInfos[z].Level := lvl;
+          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+          //  end;
+          //end;
+
+          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
             inc(lvl);
+
           lvlA := lvl;
 
-          with FHighlights[z] do begin
+          if sfaOpen in TmpNode.FoldAction then
+            FLastNode := TmpNode;
+
+          with FFoldColorInfos[z] do begin
             LevelBefore := lvlB;
             LevelAfter  := lvlA;
           end;
-
-        end
-        else
-        if sfaClose in TmpNode.FoldAction then
-        begin
+        end else if sfaClose in TmpNode.FoldAction then begin
           Found := False;
-          for j := Length(FHighlights)-1 downto 0 do begin
-            with FHighlights[j].SrcNode do begin
-              if  (FoldType = TmpNode.FoldType) and
-                (FoldGroup = TmpNode.FoldGroup) and
-                (sfaOpen in FoldAction) and
-                // (FoldLvlEnd = TmpNode.FoldLvlStart)
-                (NestLvlEnd = TmpNode.NestLvlStart)
-
-                then begin
-                  lvl := FHighlights[j].ColorIdx;
-                  lvlB := FHighlights[j].LevelBefore;
-                  Found := True;
-                  break;
-                end;
+          for j := Length(FFoldColorInfos)-1 downto 0 do begin
+            with FFoldColorInfos[j].SrcNode do begin
+              if (FoldType = TmpNode.FoldType)
+              and (FoldGroup = TmpNode.FoldGroup)
+              and (sfaOpen in FoldAction)
+              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
+                lvlB := lvl;
+                lvl := FFoldColorInfos[j].Level;
+                lvlA := FFoldColorInfos[j].LevelAfter;
+                FLastNode := TmpNode;
+                Found := True;
+                break;
+              end;
             end;
           end;
           if Found then begin
             AddHighlight(TmpNode);
-            lvl := lvlB;
+            with FFoldColorInfos[z] do begin
+              LevelBefore := lvlB;
+              LevelAfter  := lvlA;
+            end;
+            // if found opening position is behind closing position:
+            // delete this as it does not have to be drawn
+            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
+              for j := j to z - 1 do begin
+                FFoldColorInfos[j] := FFoldColorInfos[j+1];
+              end;
+              SetLength(FFoldColorInfos, z);
+            end;
           end;
-
-          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-            //inc(lvl);
         end;
       end;
-
       inc(i);
     until i >= NodeList.Count;
-
   finally
     NodeList.ReleaseReference;
   end;
@@ -469,197 +526,277 @@
 end;
 
 procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
+var
+  i, LastX, j: Integer;
 begin
-  CurrentY := aRow;
-  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('PrepareMarkupForRow %d', [aRow]);
+  {$ENDIF}
+  if not Assigned(FHighlighter) then exit;
+  FPreparedRow := aRow;
+  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
 
   if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
     exit;
 
-  //DoMarkupFoldAtRow(aRow);
+  // invalidate fLastNode
+  FLastNode.LineIndex := -1;
+
   DoMarkupParentFoldAtRow(aRow);
   DoMarkupParentCloseFoldAtRow(aRow);
-  //DoMarkupRangeFoldAtRow(aRow);
 
-  FHighlights := SortLeftMostFI(FHighlights);
+  // delete parents with bigger x
+  // to keep out mis indented blocks
+  LastX := MaxInt;
+  for i := length(FFoldColorInfos) - 1 downto 0 do begin
+    if FFoldColorInfos[i].X > LastX then begin
+      for j := i to length(FFoldColorInfos) - 2 do begin
+        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
+      end;
+      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
+    end;
+    LastX := FFoldColorInfos[i].X;
+  end;
 end;
 
 procedure TSynEditMarkupFoldColors.EndMarkup;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('EndMarkup');
+  {$ENDIF}
   inherited EndMarkup;
   FNestList.Clear; // for next markup start
 end;
 
-function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
-begin
-  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-end;
-
 procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
 begin
   if FDefaultGroup = AValue then Exit;
   FDefaultGroup := AValue;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+  FNestList.FoldGroup := FDefaultGroup;
 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;
+  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
   var
-    y,i,LCnt : integer;
-    HL: TSynCustomFoldHighlighter;
-    NodeList: TLazSynFoldNodeInfoList;
-    TmpNode, CloseNode: TSynFoldNodeInfo;
+    lCount, lLineIdx, i, lAnz: Integer;
+    lNode: TSynFoldNodeInfo;
+  begin
+    lLineIdx := ToIdx(pLine);
+    pNestList.Line := lLineIdx;
+    lCount := pNestList.Count;
+    SetLength(pList, lCount);
+    lAnz := 0;
+    for i := 0 to lCount - 1 do begin
+      lNode := pNestList.HLNode[i];
+      if (sfaInvalid in lNode.FoldAction)
+      or (
+        (sfaOpen in lNode.FoldAction)
+        and (lNode.LineIndex = lLineIdx)
+      ) then
+        Continue;
 
-    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?)
+      pList[i] := lNode;
+      inc(lAnz);
     end;
+    SetLength(pList, lAnz);
+  end;
 
-  begin
-    Result := -1;
-    y := aRow -1;
+var
+  i, lMinAnz, lEndLine, j, l: integer;
+  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
+begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
+  {$ENDIF}
 
-    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
+  if not Assigned(FHighlighter) then exit;
+  FHighlighter.CurrentLines := Lines;
+  if EndLine < 0 then
+    EndLine := StartLine
+  else
+    // endline seems to be the first line after the change
+    EndLine := EndLine - 1;
+  lEndLine := EndLine;
 
-    NodeList := HL.FoldNodeInfo[y];
-    NodeList.AddReference;
-    try
-      NodeList.ActionFilter := [sfaOpen];
-      i := 0;
-      repeat
-        TmpNode := NodeList[i];
+  SetLength(lStartNestList, 0);
+  SetLength(lEndNestList, 0);
 
-        if TmpNode.LogXStart < X-1 then
-        begin
-          inc(i);
-          continue;
-        end;
+  FillNestList(lStartNestList, StartLine, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at Start:');
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-        //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;
+  FillNestList(lEndNestList, EndLine, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at End:');
+  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-        inc(i);
-      until i >= NodeList.Count;
-
-    finally
-      NodeList.ReleaseReference;
+  // delete all nodes in lEndNodeList which where active at StartLine
+  // to get the nodes which reach behind EndLine
+  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
+  for i := 0 to lMinAnz - 1 do begin
+    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
+    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
+    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
+    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
+    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
+      for j := 0 to length(lEndNestList) - 2 do
+        lEndNestList[j] := lEndNestList[j + 1];
+      SetLength(lEndNestList, Length(lEndNestList) - 1);
+    end else begin
+      break
     end;
   end;
 
+  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
+    // deeper fold group than StartLine: fold group ends after EndLine
+    // find real EndLine (end line of first remaining fold node)
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   Remaining Nodes:');
+    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex]]);
+    {$ENDIF}
+    // does position of first character change for remaining node?
+    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
+      // position of first character changed -> find endline
+      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
+  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;
+  // check for changes of endline for node which are active at StartLine
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    if sfaOutline in FoldAction then begin
+      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
+      if l <> FEndLine[LineIndex] then begin
+        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
+        FEndLine[LineIndex] := l;
+        {$IFDEF SynEditMarkupFoldColoringDebug}
+        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+        {$ENDIF}
       end;
     end;
 
-    if n < 0 then exit;
+  // invalidate cache
+  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
+    FFirstCharacterColumn[i] := 0;
+    FEndLine[i] := 0;
+  end;
 
-    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);
+  if lEndLine > EndLine then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
+    {$ENDIF}
+    InvalidateSynLines(EndLine + 1 , lEndLine);
+  end;
+end;
+
+procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
+var
+  old: TSynEditStrings;
+begin
+  old := Lines;
+  if Assigned(old)
+  and (AValue <> old) then begin
+    // change:
+    // remove Changehandler
+    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+  end;
+  inherited SetLines(AValue);
+  if (AValue <> old) then begin
+    // change:
+    if Assigned(AValue) then begin
+      // set cache size
+      SetLength(FFirstCharacterColumn, AValue.Count);
+      SetLength(FEndLine, AValue.Count);
+      // add Changehandler
+      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
+      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+    end else begin
+      // clear cache
+      SetLength(FFirstCharacterColumn, 0);
+      SetLength(FEndLine, 0);
     end;
+  end;
+end;
 
-  end;
+procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
+                                        aIndex, aCount: Integer);
 var
-  EndFoldLine,y : integer;
+  absCount,
+  idx, i: 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);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  idx := ToIdx(aIndex);
+  if aCount < 0 then begin
+    // lines deleted
+    absCount := Abs(aCount);
+    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
+      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
+      FEndLine[i] := FEndLine[i + absCount];
+    end;
   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}
+  SetLength(FFirstCharacterColumn, Sender.Count);
+  SetLength(FEndLine, Sender.Count);
+  if (aCount > 0) then begin
+    if idx >= 0 then begin
+      // lines added
+      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
+        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
+        FEndLine[i + aCount] := FEndLine[i];
+      end;
+      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end else begin
+      // first lines will be inserted
+      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end;
+  end;
 end;
 
-procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
-var Y : integer;
+procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
+  aIndex, aCount: Integer);
+var
+  newHighlighter: TSynCustomFoldHighlighter;
 begin
-  Y := Caret.LineBytePos.y;
-  if Y = FCaretY then exit;
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  if (aIndex <> -1)
+  or (aCount <> -1) then
+    exit;
 
-  FCaretY := Y;
-  FPrevCaretText := Caret.LineText;
-end;
+  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(newHighlighter)
+  and not (newHighlighter is TSynCustomFoldHighlighter) then
+    newHighlighter := nil;
 
+  if (newHighlighter = FHighlighter) then
+    exit;
 
+  FHighlighter := newHighlighter;
 
+  FreeAndNil(FNestList);
+  if Assigned(FHighlighter) then begin
+    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
+    FNestList.ResetFilter;
+    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
+    FNestList.IncludeOpeningOnLine := True; //False; //
+  end;
+end;
+
 end.
 

Pascal Riekenberg

2016-08-01 14:18

reporter  

syneditmarkupfoldcoloring.pas_v6.patch (37,594 bytes)
Index: syneditmarkupfoldcoloring.pas
===================================================================
--- syneditmarkupfoldcoloring.pas	(revision 52763)
+++ syneditmarkupfoldcoloring.pas	(working copy)
@@ -50,12 +50,14 @@
 unit SynEditMarkupFoldColoring;
 
 {$mode objfpc}{$H+}
+{.$define SynEditMarkupFoldColoringDebug}
 
 interface
 
 uses
   Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
-  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
+  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
+  LazSynEditText;
 
 type
 
@@ -66,37 +68,39 @@
     Border  : Boolean;
     Ignore  : Boolean; //no color no line
     SrcNode : TSynFoldNodeInfo;
-    LevelBefore, LevelAfter : integer;//needed by non nest nodes
+    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
   end;
 
   TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
   TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
-
   { TSynEditMarkupFoldColors }
 
   TSynEditMarkupFoldColors = class(TSynEditMarkup)
   private
+    function GetFirstCharacterColumn(index: Integer): Byte;
+  private
+    FHighlighter: TSynCustomFoldHighlighter;
     FNestList: TLazSynEditNestedFoldsList;
+
+    // cache
+    FFirstCharacterColumn: Array of Byte;
+    FEndLine: Array of Integer;
+
     FDefaultGroup: integer;
-     // Physical Position
-    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
+    FFoldColorInfos: TMarkupFoldColorInfos;
     Colors : array of TColor;
-
-    {%region invalidating}
-    CurrentY : integer;  //??
-    FCaretY : integer;    // flag identify for refresh begin______
-    FPrevCaretText : string;  // flag identify for refresh begin______
-    {%endregion}
-
+    FPreparedRow: integer;
+    FLastNode: TSynFoldNodeInfo;
     procedure DoMarkupParentFoldAtRow(aRow: Integer);
     procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
-
-    function GetFoldHighLighter: TSynCustomFoldHighlighter;
     procedure SetDefaultGroup(AValue: integer);
+    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
   protected
     // Notifications about Changes to the text
     procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
-    procedure DoCaretChanged(Sender: TObject); override;
+    procedure SetLines(const AValue: TSynEditStrings); override;
+    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
+    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
   public
     constructor Create(ASynEdit : TSynEditBase);
     destructor Destroy; override;
@@ -115,36 +119,21 @@
 
 implementation
 uses
-  SynEdit,SynEditTypes, SynEditMiscProcs;
+  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
+{$IFDEF SynEditMarkupFoldColoringDebug}
+  , SynHighlighterPas
+{$ENDIF}
+  ;
 
-  {%region Sorting FoldInfo -fold}
-  function CompareFI(Item1, Item2: Pointer): Integer;
-  begin
-    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
-    if result = 0 then
-        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
-    if result = 0 then
-        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
-          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
+{$IFDEF SynEditMarkupFoldColoringDebug}
+function FoldTypeToStr(p_FoldType: Pointer): String;
+begin
+  WriteStr(Result, TPascalCodeFoldBlockType(p_FoldType));
+  while length(Result) < 17 do Result := Result + ' ';
+end;
+{$ENDIF}
 
-    SetLength(result, Length(a));
-    for i := 0 to Pred(l.Count) do
-      result[i] := PMarkupFoldColorInfo(l[i])^;
-     l.Free;
-  end;
-  {%endregion}
-
 { TSynEditMarkupFoldColors }
 
 constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
@@ -151,23 +140,36 @@
 begin
   inherited Create(ASynEdit);
 
-  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
+  if Assigned(Lines) then begin
+    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
+    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+    SetLength(FFirstCharacterColumn, Lines.Count);
+    SetLength(FEndLine, Lines.Count);
+  end;
+
+  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(FHighlighter)
+  and not (FHighlighter  is TSynCustomFoldHighlighter) then
+    FHighlighter := nil;
+
+  FDefaultGroup := 0;
+
+  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
   FNestList.ResetFilter;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
-  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
-  FNestList.IncludeOpeningOnLine := True; //False; //
+  FNestList.FoldGroup := FDefaultGroup;
+  FNestList.FoldFlags :=  [sfbIncludeDisabled];
+  FNestList.IncludeOpeningOnLine := True;
 
   MarkupInfo.Foreground := clGreen;
-  MarkupInfo.Background := clNone; //clFuchsia;
+  MarkupInfo.Background := clNone;
   MarkupInfo.Style := [];
   MarkupInfo.StyleMask := [];
-  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
+  MarkupInfo.FrameEdges:= sfeLeft;
 
   SetLength(Colors, 5);
   Colors[0] := clRed;
   Colors[1] := $000098F7; //orange
   Colors[2] := $0022CC40; //green
-  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
   Colors[3] := $00FF682A; //blue
   Colors[4] := $00CF00C4; //purple
 end;
@@ -174,6 +176,10 @@
 
 destructor TSynEditMarkupFoldColors.Destroy;
 begin
+  if Assigned(Lines) then begin
+    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+  end;
   FreeAndNil(FNestList);
   inherited Destroy;
 end;
@@ -182,23 +188,27 @@
   const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
   const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
 var
-  i,x2both : integer;
+  i, x2both: integer;
 begin
   Result := nil;
-  if (CurrentY = aRow) then begin
+  if not Assigned(FHighlighter) then exit;
+  if (FPreparedRow = aRow) then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
+    {$ENDIF}
 
-    x2both := -3; //flag
-    for i := 0 to length(FHighlights)-1 do
-      with FHighlights[i] do
+    x2both := -3;
+    for i := 0 to length(FFoldColorInfos)-1 do
+      with FFoldColorInfos[i] do
         if not Ignore
         and (X < X2)
         and (ColorIdx >= 0)
         and (aStartCol.Logical >= x)
-        and (aStartCol.Logical < X2) then
-        begin
-          //MarkupInfo.FrameColor:= clGreen; //debug
-          if x2both = -3 then //first call flag
-          begin
+        and (aStartCol.Logical < X2) then begin
+          {$IFDEF SynEditMarkupFoldColoringDebug}
+          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
+          {$ENDIF}
+          if x2both = -3 then begin //first call flag
             MarkupInfo.FrameColor:= clNone;
             MarkupInfo.Foreground:= clNone;
             MarkupInfo.Background:= clNone;
@@ -209,25 +219,14 @@
           Result := MarkupInfo;
           x2both := max(x2both, x2);
           MarkupInfo.SetFrameBoundsLog(x, x2both);
-          if Border then
-          begin
+          if Border then begin
             MarkupInfo.FrameColor:= Colors[ColorIdx];
-            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
-          end
-          else
+            MarkupInfo.FrameEdges:= sfeLeft;
+          end else begin
+            MarkupInfo.FrameColor:= clNone;
+            MarkupInfo.FrameEdges:= sfeNone;
             MarkupInfo.Foreground := Colors[ColorIdx];
-
-          //MarkupInfo.FrameEdges:= sfeAround; //debug
-
-          {//2nd debug
-          if x > x2 then
-          begin
-            MarkupInfo.Background:= clYellow;
-            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
-            MarkupInfo.FrameColor:= clBlue; //debug
-          end;}
-
-          //break;
+          end;
         end;
   end;
 end;
@@ -237,36 +236,80 @@
   const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
 var i : integer;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
+  {$ENDIF}
+  if not Assigned(FHighlighter)
+  or (FPreparedRow <> aRow) then
+    exit;
+
   ANextLog := -1;
   ANextPhys := -1;
-  if (CurrentY = aRow)  then
-  for i := 0 to length(FHighlights)-1  do
-    with FHighlights[i] do
-    begin
-      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
-        //continue;
-      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
-      begin
-        ANextLog := FHighlights[i].X;
+  for i := 0 to length(FFoldColorInfos)-1  do
+    with FFoldColorInfos[i] do begin
+      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
+        ANextLog := FFoldColorInfos[i].X;
         break;
       end;
     end;
 end;
 
+function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
+var
+  l: String;
+  p: Integer;
+begin
+  l := SynEdit.Lines[index];
+  p := 1;
+  while not (l[p] in [#13, #10, #0])
+  and (l[p] = #32) do inc(p);
+  if p > 255 then p := 255;
+  Result := p;
+end;
+
 procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
 var
-  i,lvl,z : integer; //iterate parents fold
+  i,lvl,z: integer;
 
   procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
+  var
+    p, s, l: integer;
   begin
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    // get column of first character in row
+    s := Length(FFirstCharacterColumn);
+    if (s <= ANode.LineIndex)
+    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
+      p := FirstCharacterColumn[ANode.LineIndex];
+      if s > ANode.LineIndex then begin
+        FFirstCharacterColumn[ANode.LineIndex] := p;
+      end else begin
+        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    s := Length(FEndLine);
+    if (s <= ANode.LineIndex)
+    or (FEndLine[ANode.LineIndex] = 0) then begin
+      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
+      if s > ANode.LineIndex then begin
+        FEndLine[ANode.LineIndex] := l;
+      end else begin
+        DebugLn('!!! FEndLine-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z+1);
+    with FFoldColorInfos[z] do begin
+
       SrcNode:= ANode; //needed by close node
-      Border := ANode.LineIndex + 1 <> aRow;
-      X  := ANode.LogXStart + 1;
-      Y  := aRow;//ANode.LineIndex + 1;
-      X2 := X+1; //ANode.LogXEnd + 1;
+      Border := ToPos(ANode.LineIndex) <> aRow;
+      if s <= ANode.LineIndex then
+        X  := p
+      else
+        X  := FFirstCharacterColumn[ANode.LineIndex];
+      Y  := aRow;
+      X2 := X + 1;
       Ignore := False;
 
       if Border and (sfaOutlineNoLine in ANode.FoldAction) then
@@ -273,7 +316,9 @@
         Ignore := True;
       if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
         Ignore := True;
-        ColorIdx := lvl mod (length(Colors))
+      Level := lvl;
+      ColorIdx := Max(0, lvl) mod (length(Colors));
+      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
     end;
   end;
 
@@ -281,60 +326,62 @@
   y, lvlB,lvlA: Integer;
   TmpNode: TSynFoldNodeInfo;
   NestCount : integer;
-  procedure Later(var J:integer);
-  begin
-    inc(J);
-  end;
-  function Allowed(J: integer):boolean;
-  begin
-    result := J < NestCount;
-  end;
 
 begin
-  y := aRow-1;
+  y := ToIdx(aRow);
   FNestList.Line := y;
   NestCount := FNestList.Count;
+  FHighlighter.CurrentLines := Lines;
 
   lvl := 0;
   i := 0;
-  while Allowed(i) do
-  begin
-
+  while i < NestCount do begin
     TmpNode := FNestList.HLNode[i];
-    //find till valid
-    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
-    begin
-      Later(i);
-      TmpNode := FNestList.HLNode[i];
-    end;
+    if (sfaOutline in TmpNode.FoldAction)
+    and not (sfaInvalid in TmpNode.FoldAction) then
+      //avoid bug of IncludeOpeningOnLine := False;
+      if (sfaOpen in TmpNode.FoldAction)
+      and (TmpNode.LineIndex + 1 = aRow) then begin
+        {do nothing here}
+      end else begin
+        lvlB := lvl;
 
-    if (sfaOutline in TmpNode.FoldAction ) then
-    //avoid bug of IncludeOpeningOnLine := False;
-    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
-    begin {do nothing here} end
-    else
-    begin
-      lvlB := lvl;
+        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
+          inc(lvl)
+        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+          dec(lvl);
+        //if (FLastNode.LineIndex >= 0)
+        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+        // inc(lvl);
 
-      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-        inc(lvl);
-      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
-        dec(lvl);
+        AddVerticalLine(TmpNode);
 
-      AddVerticalLine(TmpNode);
-      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-        inc(lvl);
+        //if (z > 0)
+        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
+        //  // if child is on same x-pos keep level
+        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
+        //    lvl := FFoldColorInfos[z - 1].Level;
+        //    FFoldColorInfos[z].Level := lvl;
+        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+        //  end;
+        //end;
 
-      lvlA := lvl;
+        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
+          inc(lvl);
 
-      with FHighlights[z] do begin
-        LevelBefore := lvlB;
-        LevelAfter  := lvlA;
+        if sfaOpen in TmpNode.FoldAction then
+          FLastNode := TmpNode;
+
+        lvlA := lvl;
+
+        with FFoldColorInfos[z] do begin
+          LevelBefore := lvlB;
+          LevelAfter  := lvlA;
+        end;
       end;
-    end;
-
-    Later(i);
-    //break; //debug
+    inc(i);
   end;
 end;
 
@@ -345,33 +392,29 @@
   procedure AddHighlight( ANode: TSynFoldNodeInfo );
   var x,j : integer;
   begin
-        //don't replace; don't add when already found
     x  := ANode.LogXStart + 1;
-
     if ANode.LogXStart < ANode.LogXEnd then
-    for j := 0 to Pred(length(FHighlights)) do
-      if (FHighlights[j].X = x)
-      and (FHighlights[j].Border)
-      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
-      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
+    for j := 0 to Pred(length(FFoldColorInfos)) do
+      if (FFoldColorInfos[j].X = x)
+      and (FFoldColorInfos[j].Border)
+      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
+      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
       then begin
-       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
-       FHighlights[j].Border := False
-
+       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
+       FFoldColorInfos[j].Border := False
       end;
 
-    //exit; //debug
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z + 1);
+    with FFoldColorInfos[z] do begin
       Border := False;
       SrcNode:= ANode; //needed by close node
       Y  := ANode.LineIndex + 1;
       X  := ANode.LogXStart + 1;
       X2 := ANode.LogXEnd + 1;
-      //ColorIdx := lvl;
+      Level := lvl;
       if not (sfaOutlineNocolor in ANode.FoldAction) then
-         ColorIdx := lvl mod (length(Colors))
+         ColorIdx := Max(0, lvl) mod (length(Colors))
       else
          ColorIdx := -1;
     end;
@@ -378,91 +421,105 @@
   end;
 
 var
-  y,i,j,lvlB,lvlA : integer;
-  HL: TSynCustomFoldHighlighter;
+  LineIdx,i,j,lvlB,lvlA : integer;
   NodeList: TLazSynFoldNodeInfoList;
   TmpNode: TSynFoldNodeInfo;
-  Found : boolean;
+  Found: boolean;
 begin
-  y := aRow -1;
+  LineIdx := ToIdx(aRow);
 
-  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-  HL.CurrentLines := Lines;
-  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
+  FHighlighter.CurrentLines := Lines;
+  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
 
-  NodeList := HL.FoldNodeInfo[y];
+  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
   NodeList.AddReference;
   try
     NodeList.ActionFilter := [sfaOutline];
-    //NodeList.FoldFlags:= [sfbIncludeDisabled];
     lvl := 0;
-    J := Length(FHighlights)-1;
+    J := Length(FFoldColorInfos) - 1;
     if J >=0 then
-      lvl := max(0,FHighlights[J].LevelAfter);
+      lvl := max(0,FFoldColorInfos[J].LevelAfter);
     i := 0;
     repeat
       TmpNode := NodeList[i];
 
-      //find till valid
-      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
-      begin
-        inc(i);
-        TmpNode := NodeList[i];
-      end;
-      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
-        if sfaOpen in TmpNode.FoldAction then
-        begin
+      if not (sfaInvalid in TmpNode.FoldAction)
+      and (sfaOutline in TmpNode.FoldAction) then begin
+        if sfaOpen in TmpNode.FoldAction then begin
           lvlB := lvl;
 
           if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-            inc(lvl);
-          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+            inc(lvl)
+          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
             dec(lvl);
+          //if (FLastNode.LineIndex >= 0)
+          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+          // inc(lvl);
 
           AddHighlight(TmpNode);
-          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
+
+          //if (z > 0)
+          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
+          //begin
+          //  // if child is on same x-pos keep level
+          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
+          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
+          //    lvl := FFoldColorInfos[z - 1].Level;
+          //    FFoldColorInfos[z].Level := lvl;
+          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+          //  end;
+          //end;
+
+          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
             inc(lvl);
+
           lvlA := lvl;
 
-          with FHighlights[z] do begin
+          if sfaOpen in TmpNode.FoldAction then
+            FLastNode := TmpNode;
+
+          with FFoldColorInfos[z] do begin
             LevelBefore := lvlB;
             LevelAfter  := lvlA;
           end;
-
-        end
-        else
-        if sfaClose in TmpNode.FoldAction then
-        begin
+        end else if sfaClose in TmpNode.FoldAction then begin
           Found := False;
-          for j := Length(FHighlights)-1 downto 0 do begin
-            with FHighlights[j].SrcNode do begin
-              if  (FoldType = TmpNode.FoldType) and
-                (FoldGroup = TmpNode.FoldGroup) and
-                (sfaOpen in FoldAction) and
-                // (FoldLvlEnd = TmpNode.FoldLvlStart)
-                (NestLvlEnd = TmpNode.NestLvlStart)
-
-                then begin
-                  lvl := FHighlights[j].ColorIdx;
-                  lvlB := FHighlights[j].LevelBefore;
-                  Found := True;
-                  break;
-                end;
+          for j := Length(FFoldColorInfos)-1 downto 0 do begin
+            with FFoldColorInfos[j].SrcNode do begin
+              if (FoldType = TmpNode.FoldType)
+              and (FoldGroup = TmpNode.FoldGroup)
+              and (sfaOpen in FoldAction)
+              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
+                lvlB := lvl;
+                lvl := FFoldColorInfos[j].Level;
+                lvlA := FFoldColorInfos[j].LevelAfter;
+                FLastNode := TmpNode;
+                Found := True;
+                break;
+              end;
             end;
           end;
           if Found then begin
             AddHighlight(TmpNode);
-            lvl := lvlB;
+            with FFoldColorInfos[z] do begin
+              LevelBefore := lvlB;
+              LevelAfter  := lvlA;
+            end;
+            // if found opening position is behind closing position:
+            // delete this as it does not have to be drawn
+            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
+              for j := j to z - 1 do begin
+                FFoldColorInfos[j] := FFoldColorInfos[j+1];
+              end;
+              SetLength(FFoldColorInfos, z);
+            end;
           end;
-
-          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-            //inc(lvl);
         end;
       end;
-
       inc(i);
     until i >= NodeList.Count;
-
   finally
     NodeList.ReleaseReference;
   end;
@@ -469,197 +526,277 @@
 end;
 
 procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
+var
+  i, LastX, j: Integer;
 begin
-  CurrentY := aRow;
-  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('PrepareMarkupForRow %d', [aRow]);
+  {$ENDIF}
+  if not Assigned(FHighlighter) then exit;
+  FPreparedRow := aRow;
+  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
 
   if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
     exit;
 
-  //DoMarkupFoldAtRow(aRow);
+  // invalidate fLastNode
+  FLastNode.LineIndex := -1;
+
   DoMarkupParentFoldAtRow(aRow);
   DoMarkupParentCloseFoldAtRow(aRow);
-  //DoMarkupRangeFoldAtRow(aRow);
 
-  FHighlights := SortLeftMostFI(FHighlights);
+  // delete parents with bigger x
+  // to keep out mis indented blocks
+  LastX := MaxInt;
+  for i := length(FFoldColorInfos) - 1 downto 0 do begin
+    if FFoldColorInfos[i].X > LastX then begin
+      for j := i to length(FFoldColorInfos) - 2 do begin
+        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
+      end;
+      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
+    end;
+    LastX := FFoldColorInfos[i].X;
+  end;
 end;
 
 procedure TSynEditMarkupFoldColors.EndMarkup;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('EndMarkup');
+  {$ENDIF}
   inherited EndMarkup;
   FNestList.Clear; // for next markup start
 end;
 
-function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
-begin
-  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-end;
-
 procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
 begin
   if FDefaultGroup = AValue then Exit;
   FDefaultGroup := AValue;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+  FNestList.FoldGroup := FDefaultGroup;
 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;
+  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
   var
-    y,i,LCnt : integer;
-    HL: TSynCustomFoldHighlighter;
-    NodeList: TLazSynFoldNodeInfoList;
-    TmpNode, CloseNode: TSynFoldNodeInfo;
+    lCount, lLineIdx, i, lAnz: Integer;
+    lNode: TSynFoldNodeInfo;
+  begin
+    lLineIdx := ToIdx(pLine);
+    pNestList.Line := lLineIdx;
+    lCount := pNestList.Count;
+    SetLength(pList, lCount);
+    lAnz := 0;
+    for i := 0 to lCount - 1 do begin
+      lNode := pNestList.HLNode[i];
+      if (sfaInvalid in lNode.FoldAction)
+      or (
+        (sfaOpen in lNode.FoldAction)
+        and (lNode.LineIndex = lLineIdx)
+      ) then
+        Continue;
 
-    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?)
+      pList[i] := lNode;
+      inc(lAnz);
     end;
+    SetLength(pList, lAnz);
+  end;
 
-  begin
-    Result := -1;
-    y := aRow -1;
+var
+  i, lMinAnz, lEndLine, j, l: integer;
+  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
+begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
+  {$ENDIF}
 
-    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
+  if not Assigned(FHighlighter) then exit;
+  FHighlighter.CurrentLines := Lines;
+  if EndLine < 0 then
+    EndLine := StartLine
+  else
+    // endline seems to be the first line after the change
+    EndLine := EndLine - 1;
+  lEndLine := EndLine;
 
-    NodeList := HL.FoldNodeInfo[y];
-    NodeList.AddReference;
-    try
-      NodeList.ActionFilter := [sfaOpen];
-      i := 0;
-      repeat
-        TmpNode := NodeList[i];
+  SetLength(lStartNestList, 0);
+  SetLength(lEndNestList, 0);
 
-        if TmpNode.LogXStart < X-1 then
-        begin
-          inc(i);
-          continue;
-        end;
+  FillNestList(lStartNestList, StartLine, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at Start:');
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-        //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;
+  FillNestList(lEndNestList, EndLine, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at End:');
+  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-        inc(i);
-      until i >= NodeList.Count;
-
-    finally
-      NodeList.ReleaseReference;
+  // delete all nodes in lEndNodeList which where active at StartLine
+  // to get the nodes which reach behind EndLine
+  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
+  for i := 0 to lMinAnz - 1 do begin
+    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
+    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
+    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
+    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
+    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
+      for j := 0 to length(lEndNestList) - 2 do
+        lEndNestList[j] := lEndNestList[j + 1];
+      SetLength(lEndNestList, Length(lEndNestList) - 1);
+    end else begin
+      break
     end;
   end;
 
+  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
+    // deeper fold group than StartLine: fold group ends after EndLine
+    // find real EndLine (end line of first remaining fold node)
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   Remaining Nodes:');
+    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex]]);
+    {$ENDIF}
+    // does position of first character change for remaining node?
+    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
+      // position of first character changed -> find endline
+      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
+  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;
+  // check for changes of endline for node which are active at StartLine
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    if sfaOutline in FoldAction then begin
+      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
+      if l <> FEndLine[LineIndex] then begin
+        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
+        FEndLine[LineIndex] := l;
+        {$IFDEF SynEditMarkupFoldColoringDebug}
+        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+        {$ENDIF}
       end;
     end;
 
-    if n < 0 then exit;
+  // invalidate cache
+  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
+    FFirstCharacterColumn[i] := 0;
+    FEndLine[i] := 0;
+  end;
 
-    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);
+  if lEndLine > EndLine then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
+    {$ENDIF}
+    InvalidateSynLines(EndLine + 1 , lEndLine);
+  end;
+end;
+
+procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
+var
+  old: TSynEditStrings;
+begin
+  old := Lines;
+  if Assigned(old)
+  and (AValue <> old) then begin
+    // change:
+    // remove Changehandler
+    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+  end;
+  inherited SetLines(AValue);
+  if (AValue <> old) then begin
+    // change:
+    if Assigned(AValue) then begin
+      // set cache size
+      SetLength(FFirstCharacterColumn, AValue.Count);
+      SetLength(FEndLine, AValue.Count);
+      // add Changehandler
+      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
+      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+    end else begin
+      // clear cache
+      SetLength(FFirstCharacterColumn, 0);
+      SetLength(FEndLine, 0);
     end;
+  end;
+end;
 
-  end;
+procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
+                                        aIndex, aCount: Integer);
 var
-  EndFoldLine,y : integer;
+  absCount,
+  idx, i: 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);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  idx := ToIdx(aIndex);
+  if aCount < 0 then begin
+    // lines deleted
+    absCount := Abs(aCount);
+    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
+      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
+      FEndLine[i] := FEndLine[i + absCount];
+    end;
   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}
+  SetLength(FFirstCharacterColumn, Sender.Count);
+  SetLength(FEndLine, Sender.Count);
+  if (aCount > 0) then begin
+    if idx >= 0 then begin
+      // lines added
+      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
+        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
+        FEndLine[i + aCount] := FEndLine[i];
+      end;
+      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end else begin
+      // first lines will be inserted
+      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end;
+  end;
 end;
 
-procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
-var Y : integer;
+procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
+  aIndex, aCount: Integer);
+var
+  newHighlighter: TSynCustomFoldHighlighter;
 begin
-  Y := Caret.LineBytePos.y;
-  if Y = FCaretY then exit;
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  if (aIndex <> -1)
+  or (aCount <> -1) then
+    exit;
 
-  FCaretY := Y;
-  FPrevCaretText := Caret.LineText;
-end;
+  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(newHighlighter)
+  and not (newHighlighter is TSynCustomFoldHighlighter) then
+    newHighlighter := nil;
 
+  if (newHighlighter = FHighlighter) then
+    exit;
 
+  FHighlighter := newHighlighter;
 
+  FreeAndNil(FNestList);
+  if Assigned(FHighlighter) then begin
+    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
+    FNestList.ResetFilter;
+    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
+    FNestList.IncludeOpeningOnLine := True; //False; //
+  end;
+end;
+
 end.
 

Pascal Riekenberg

2016-08-15 09:27

reporter  

syneditmarkupfoldcoloring.pas_v7.patch (37,607 bytes)
Index: syneditmarkupfoldcoloring.pas
===================================================================
--- syneditmarkupfoldcoloring.pas	(revision 52808)
+++ syneditmarkupfoldcoloring.pas	(working copy)
@@ -50,12 +50,14 @@
 unit SynEditMarkupFoldColoring;
 
 {$mode objfpc}{$H+}
+{.$define SynEditMarkupFoldColoringDebug}
 
 interface
 
 uses
   Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
-  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
+  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
+  LazSynEditText;
 
 type
 
@@ -66,37 +68,39 @@
     Border  : Boolean;
     Ignore  : Boolean; //no color no line
     SrcNode : TSynFoldNodeInfo;
-    LevelBefore, LevelAfter : integer;//needed by non nest nodes
+    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
   end;
 
   TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
   TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
-
   { TSynEditMarkupFoldColors }
 
   TSynEditMarkupFoldColors = class(TSynEditMarkup)
   private
+    function GetFirstCharacterColumn(index: Integer): Byte;
+  private
+    FHighlighter: TSynCustomFoldHighlighter;
     FNestList: TLazSynEditNestedFoldsList;
+
+    // cache
+    FFirstCharacterColumn: Array of Byte;
+    FEndLine: Array of Integer;
+
     FDefaultGroup: integer;
-     // Physical Position
-    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
+    FFoldColorInfos: TMarkupFoldColorInfos;
     Colors : array of TColor;
-
-    {%region invalidating}
-    CurrentY : integer;  //??
-    FCaretY : integer;    // flag identify for refresh begin______
-    FPrevCaretText : string;  // flag identify for refresh begin______
-    {%endregion}
-
+    FPreparedRow: integer;
+    FLastNode: TSynFoldNodeInfo;
     procedure DoMarkupParentFoldAtRow(aRow: Integer);
     procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
-
-    function GetFoldHighLighter: TSynCustomFoldHighlighter;
     procedure SetDefaultGroup(AValue: integer);
+    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
   protected
     // Notifications about Changes to the text
     procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
-    procedure DoCaretChanged(Sender: TObject); override;
+    procedure SetLines(const AValue: TSynEditStrings); override;
+    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
+    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
   public
     constructor Create(ASynEdit : TSynEditBase);
     destructor Destroy; override;
@@ -115,36 +119,21 @@
 
 implementation
 uses
-  SynEdit,SynEditTypes, SynEditMiscProcs;
+  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
+{$IFDEF SynEditMarkupFoldColoringDebug}
+  , SynHighlighterPas
+{$ENDIF}
+  ;
 
-  {%region Sorting FoldInfo -fold}
-  function CompareFI(Item1, Item2: Pointer): Integer;
-  begin
-    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
-    if result = 0 then
-        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
-    if result = 0 then
-        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
-          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
+{$IFDEF SynEditMarkupFoldColoringDebug}
+function FoldTypeToStr(p_FoldType: Pointer): String;
+begin
+  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
+  while length(Result) < 17 do Result := Result + ' ';
+end;
+{$ENDIF}
 
-    SetLength(result, Length(a));
-    for i := 0 to Pred(l.Count) do
-      result[i] := PMarkupFoldColorInfo(l[i])^;
-     l.Free;
-  end;
-  {%endregion}
-
 { TSynEditMarkupFoldColors }
 
 constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
@@ -151,23 +140,36 @@
 begin
   inherited Create(ASynEdit);
 
-  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
+  if Assigned(Lines) then begin
+    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
+    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+    SetLength(FFirstCharacterColumn, Lines.Count);
+    SetLength(FEndLine, Lines.Count);
+  end;
+
+  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(FHighlighter)
+  and not (FHighlighter  is TSynCustomFoldHighlighter) then
+    FHighlighter := nil;
+
+  FDefaultGroup := 0;
+
+  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
   FNestList.ResetFilter;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
-  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
-  FNestList.IncludeOpeningOnLine := True; //False; //
+  FNestList.FoldGroup := FDefaultGroup;
+  FNestList.FoldFlags :=  [sfbIncludeDisabled];
+  FNestList.IncludeOpeningOnLine := True;
 
   MarkupInfo.Foreground := clGreen;
-  MarkupInfo.Background := clNone; //clFuchsia;
+  MarkupInfo.Background := clNone;
   MarkupInfo.Style := [];
   MarkupInfo.StyleMask := [];
-  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
+  MarkupInfo.FrameEdges:= sfeLeft;
 
   SetLength(Colors, 5);
   Colors[0] := clRed;
   Colors[1] := $000098F7; //orange
   Colors[2] := $0022CC40; //green
-  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
   Colors[3] := $00FF682A; //blue
   Colors[4] := $00CF00C4; //purple
 end;
@@ -174,6 +176,10 @@
 
 destructor TSynEditMarkupFoldColors.Destroy;
 begin
+  if Assigned(Lines) then begin
+    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+  end;
   FreeAndNil(FNestList);
   inherited Destroy;
 end;
@@ -182,23 +188,27 @@
   const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
   const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
 var
-  i,x2both : integer;
+  i, x2both: integer;
 begin
   Result := nil;
-  if (CurrentY = aRow) then begin
+  if not Assigned(FHighlighter) then exit;
+  if (FPreparedRow = aRow) then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
+    {$ENDIF}
 
-    x2both := -3; //flag
-    for i := 0 to length(FHighlights)-1 do
-      with FHighlights[i] do
+    x2both := -3;
+    for i := 0 to length(FFoldColorInfos)-1 do
+      with FFoldColorInfos[i] do
         if not Ignore
         and (X < X2)
         and (ColorIdx >= 0)
         and (aStartCol.Logical >= x)
-        and (aStartCol.Logical < X2) then
-        begin
-          //MarkupInfo.FrameColor:= clGreen; //debug
-          if x2both = -3 then //first call flag
-          begin
+        and (aStartCol.Logical < X2) then begin
+          {$IFDEF SynEditMarkupFoldColoringDebug}
+          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
+          {$ENDIF}
+          if x2both = -3 then begin //first call flag
             MarkupInfo.FrameColor:= clNone;
             MarkupInfo.Foreground:= clNone;
             MarkupInfo.Background:= clNone;
@@ -209,25 +219,14 @@
           Result := MarkupInfo;
           x2both := max(x2both, x2);
           MarkupInfo.SetFrameBoundsLog(x, x2both);
-          if Border then
-          begin
+          if Border then begin
             MarkupInfo.FrameColor:= Colors[ColorIdx];
-            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
-          end
-          else
+            MarkupInfo.FrameEdges:= sfeLeft;
+          end else begin
+            MarkupInfo.FrameColor:= clNone;
+            MarkupInfo.FrameEdges:= sfeNone;
             MarkupInfo.Foreground := Colors[ColorIdx];
-
-          //MarkupInfo.FrameEdges:= sfeAround; //debug
-
-          {//2nd debug
-          if x > x2 then
-          begin
-            MarkupInfo.Background:= clYellow;
-            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
-            MarkupInfo.FrameColor:= clBlue; //debug
-          end;}
-
-          //break;
+          end;
         end;
   end;
 end;
@@ -237,36 +236,80 @@
   const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
 var i : integer;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
+  {$ENDIF}
+  if not Assigned(FHighlighter)
+  or (FPreparedRow <> aRow) then
+    exit;
+
   ANextLog := -1;
   ANextPhys := -1;
-  if (CurrentY = aRow)  then
-  for i := 0 to length(FHighlights)-1  do
-    with FHighlights[i] do
-    begin
-      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
-        //continue;
-      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
-      begin
-        ANextLog := FHighlights[i].X;
+  for i := 0 to length(FFoldColorInfos)-1  do
+    with FFoldColorInfos[i] do begin
+      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
+        ANextLog := FFoldColorInfos[i].X;
         break;
       end;
     end;
 end;
 
+function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
+var
+  l: String;
+  p: Integer;
+begin
+  l := SynEdit.Lines[index];
+  p := 1;
+  while not (l[p] in [#13, #10, #0])
+  and (l[p] = #32) do inc(p);
+  if p > 255 then p := 255;
+  Result := p;
+end;
+
 procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
 var
-  i,lvl,z : integer; //iterate parents fold
+  i,lvl,z: integer;
 
   procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
+  var
+    p, s, l: integer;
   begin
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    // get column of first character in row
+    s := Length(FFirstCharacterColumn);
+    if (s <= ANode.LineIndex)
+    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
+      p := FirstCharacterColumn[ANode.LineIndex];
+      if s > ANode.LineIndex then begin
+        FFirstCharacterColumn[ANode.LineIndex] := p;
+      end else begin
+        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    s := Length(FEndLine);
+    if (s <= ANode.LineIndex)
+    or (FEndLine[ANode.LineIndex] = 0) then begin
+      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
+      if s > ANode.LineIndex then begin
+        FEndLine[ANode.LineIndex] := l;
+      end else begin
+        DebugLn('!!! FEndLine-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z+1);
+    with FFoldColorInfos[z] do begin
+
       SrcNode:= ANode; //needed by close node
-      Border := ANode.LineIndex + 1 <> aRow;
-      X  := ANode.LogXStart + 1;
-      Y  := aRow;//ANode.LineIndex + 1;
-      X2 := X+1; //ANode.LogXEnd + 1;
+      Border := ToPos(ANode.LineIndex) <> aRow;
+      if s <= ANode.LineIndex then
+        X  := p
+      else
+        X  := FFirstCharacterColumn[ANode.LineIndex];
+      Y  := aRow;
+      X2 := X + 1;
       Ignore := False;
 
       if Border and (sfaOutlineNoLine in ANode.FoldAction) then
@@ -273,7 +316,9 @@
         Ignore := True;
       if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
         Ignore := True;
-        ColorIdx := lvl mod (length(Colors))
+      Level := lvl;
+      ColorIdx := Max(0, lvl) mod (length(Colors));
+      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
     end;
   end;
 
@@ -281,60 +326,62 @@
   y, lvlB,lvlA: Integer;
   TmpNode: TSynFoldNodeInfo;
   NestCount : integer;
-  procedure Later(var J:integer);
-  begin
-    inc(J);
-  end;
-  function Allowed(J: integer):boolean;
-  begin
-    result := J < NestCount;
-  end;
 
 begin
-  y := aRow-1;
+  y := ToIdx(aRow);
   FNestList.Line := y;
   NestCount := FNestList.Count;
+  FHighlighter.CurrentLines := Lines;
 
   lvl := 0;
   i := 0;
-  while Allowed(i) do
-  begin
-
+  while i < NestCount do begin
     TmpNode := FNestList.HLNode[i];
-    //find till valid
-    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
-    begin
-      Later(i);
-      TmpNode := FNestList.HLNode[i];
-    end;
+    if (sfaOutline in TmpNode.FoldAction)
+    and not (sfaInvalid in TmpNode.FoldAction) then
+      //avoid bug of IncludeOpeningOnLine := False;
+      if (sfaOpen in TmpNode.FoldAction)
+      and (TmpNode.LineIndex + 1 = aRow) then begin
+        {do nothing here}
+      end else begin
+        lvlB := lvl;
 
-    if (sfaOutline in TmpNode.FoldAction ) then
-    //avoid bug of IncludeOpeningOnLine := False;
-    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
-    begin {do nothing here} end
-    else
-    begin
-      lvlB := lvl;
+        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
+          inc(lvl)
+        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+          dec(lvl);
+        //if (FLastNode.LineIndex >= 0)
+        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+        // inc(lvl);
 
-      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-        inc(lvl);
-      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
-        dec(lvl);
+        AddVerticalLine(TmpNode);
 
-      AddVerticalLine(TmpNode);
-      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-        inc(lvl);
+        //if (z > 0)
+        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
+        //  // if child is on same x-pos keep level
+        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
+        //    lvl := FFoldColorInfos[z - 1].Level;
+        //    FFoldColorInfos[z].Level := lvl;
+        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+        //  end;
+        //end;
 
-      lvlA := lvl;
+        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
+          inc(lvl);
 
-      with FHighlights[z] do begin
-        LevelBefore := lvlB;
-        LevelAfter  := lvlA;
+        if sfaOpen in TmpNode.FoldAction then
+          FLastNode := TmpNode;
+
+        lvlA := lvl;
+
+        with FFoldColorInfos[z] do begin
+          LevelBefore := lvlB;
+          LevelAfter  := lvlA;
+        end;
       end;
-    end;
-
-    Later(i);
-    //break; //debug
+    inc(i);
   end;
 end;
 
@@ -345,33 +392,29 @@
   procedure AddHighlight( ANode: TSynFoldNodeInfo );
   var x,j : integer;
   begin
-        //don't replace; don't add when already found
     x  := ANode.LogXStart + 1;
-
     if ANode.LogXStart < ANode.LogXEnd then
-    for j := 0 to Pred(length(FHighlights)) do
-      if (FHighlights[j].X = x)
-      and (FHighlights[j].Border)
-      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
-      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
+    for j := 0 to Pred(length(FFoldColorInfos)) do
+      if (FFoldColorInfos[j].X = x)
+      and (FFoldColorInfos[j].Border)
+      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
+      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
       then begin
-       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
-       FHighlights[j].Border := False
-
+       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
+       FFoldColorInfos[j].Border := False
       end;
 
-    //exit; //debug
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z + 1);
+    with FFoldColorInfos[z] do begin
       Border := False;
       SrcNode:= ANode; //needed by close node
       Y  := ANode.LineIndex + 1;
       X  := ANode.LogXStart + 1;
       X2 := ANode.LogXEnd + 1;
-      //ColorIdx := lvl;
+      Level := lvl;
       if not (sfaOutlineNocolor in ANode.FoldAction) then
-         ColorIdx := lvl mod (length(Colors))
+         ColorIdx := Max(0, lvl) mod (length(Colors))
       else
          ColorIdx := -1;
     end;
@@ -378,91 +421,105 @@
   end;
 
 var
-  y,i,j,lvlB,lvlA : integer;
-  HL: TSynCustomFoldHighlighter;
+  LineIdx,i,j,lvlB,lvlA : integer;
   NodeList: TLazSynFoldNodeInfoList;
   TmpNode: TSynFoldNodeInfo;
-  Found : boolean;
+  Found: boolean;
 begin
-  y := aRow -1;
+  LineIdx := ToIdx(aRow);
 
-  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-  HL.CurrentLines := Lines;
-  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
+  FHighlighter.CurrentLines := Lines;
+  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
 
-  NodeList := HL.FoldNodeInfo[y];
+  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
   NodeList.AddReference;
   try
     NodeList.ActionFilter := [sfaOutline];
-    //NodeList.FoldFlags:= [sfbIncludeDisabled];
     lvl := 0;
-    J := Length(FHighlights)-1;
+    J := Length(FFoldColorInfos) - 1;
     if J >=0 then
-      lvl := max(0,FHighlights[J].LevelAfter);
+      lvl := max(0,FFoldColorInfos[J].LevelAfter);
     i := 0;
     repeat
       TmpNode := NodeList[i];
 
-      //find till valid
-      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
-      begin
-        inc(i);
-        TmpNode := NodeList[i];
-      end;
-      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
-        if sfaOpen in TmpNode.FoldAction then
-        begin
+      if not (sfaInvalid in TmpNode.FoldAction)
+      and (sfaOutline in TmpNode.FoldAction) then begin
+        if sfaOpen in TmpNode.FoldAction then begin
           lvlB := lvl;
 
           if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-            inc(lvl);
-          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+            inc(lvl)
+          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
             dec(lvl);
+          //if (FLastNode.LineIndex >= 0)
+          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+          // inc(lvl);
 
           AddHighlight(TmpNode);
-          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
+
+          //if (z > 0)
+          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
+          //begin
+          //  // if child is on same x-pos keep level
+          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
+          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
+          //    lvl := FFoldColorInfos[z - 1].Level;
+          //    FFoldColorInfos[z].Level := lvl;
+          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+          //  end;
+          //end;
+
+          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
             inc(lvl);
+
           lvlA := lvl;
 
-          with FHighlights[z] do begin
+          if sfaOpen in TmpNode.FoldAction then
+            FLastNode := TmpNode;
+
+          with FFoldColorInfos[z] do begin
             LevelBefore := lvlB;
             LevelAfter  := lvlA;
           end;
-
-        end
-        else
-        if sfaClose in TmpNode.FoldAction then
-        begin
+        end else if sfaClose in TmpNode.FoldAction then begin
           Found := False;
-          for j := Length(FHighlights)-1 downto 0 do begin
-            with FHighlights[j].SrcNode do begin
-              if  (FoldType = TmpNode.FoldType) and
-                (FoldGroup = TmpNode.FoldGroup) and
-                (sfaOpen in FoldAction) and
-                // (FoldLvlEnd = TmpNode.FoldLvlStart)
-                (NestLvlEnd = TmpNode.NestLvlStart)
-
-                then begin
-                  lvl := FHighlights[j].ColorIdx;
-                  lvlB := FHighlights[j].LevelBefore;
-                  Found := True;
-                  break;
-                end;
+          for j := Length(FFoldColorInfos)-1 downto 0 do begin
+            with FFoldColorInfos[j].SrcNode do begin
+              if (FoldType = TmpNode.FoldType)
+              and (FoldGroup = TmpNode.FoldGroup)
+              and (sfaOpen in FoldAction)
+              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
+                lvlB := lvl;
+                lvl := FFoldColorInfos[j].Level;
+                lvlA := FFoldColorInfos[j].LevelAfter;
+                FLastNode := TmpNode;
+                Found := True;
+                break;
+              end;
             end;
           end;
           if Found then begin
             AddHighlight(TmpNode);
-            lvl := lvlB;
+            with FFoldColorInfos[z] do begin
+              LevelBefore := lvlB;
+              LevelAfter  := lvlA;
+            end;
+            // if found opening position is behind closing position:
+            // delete this as it does not have to be drawn
+            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
+              for j := j to z - 1 do begin
+                FFoldColorInfos[j] := FFoldColorInfos[j+1];
+              end;
+              SetLength(FFoldColorInfos, z);
+            end;
           end;
-
-          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-            //inc(lvl);
         end;
       end;
-
       inc(i);
     until i >= NodeList.Count;
-
   finally
     NodeList.ReleaseReference;
   end;
@@ -469,197 +526,277 @@
 end;
 
 procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
+var
+  i, LastX, j: Integer;
 begin
-  CurrentY := aRow;
-  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('PrepareMarkupForRow %d', [aRow]);
+  {$ENDIF}
+  if not Assigned(FHighlighter) then exit;
+  FPreparedRow := aRow;
+  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
 
   if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
     exit;
 
-  //DoMarkupFoldAtRow(aRow);
+  // invalidate fLastNode
+  FLastNode.LineIndex := -1;
+
   DoMarkupParentFoldAtRow(aRow);
   DoMarkupParentCloseFoldAtRow(aRow);
-  //DoMarkupRangeFoldAtRow(aRow);
 
-  FHighlights := SortLeftMostFI(FHighlights);
+  // delete parents with bigger x
+  // to keep out mis indented blocks
+  LastX := MaxInt;
+  for i := length(FFoldColorInfos) - 1 downto 0 do begin
+    if FFoldColorInfos[i].X > LastX then begin
+      for j := i to length(FFoldColorInfos) - 2 do begin
+        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
+      end;
+      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
+    end;
+    LastX := FFoldColorInfos[i].X;
+  end;
 end;
 
 procedure TSynEditMarkupFoldColors.EndMarkup;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('EndMarkup');
+  {$ENDIF}
   inherited EndMarkup;
   FNestList.Clear; // for next markup start
 end;
 
-function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
-begin
-  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-end;
-
 procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
 begin
   if FDefaultGroup = AValue then Exit;
   FDefaultGroup := AValue;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+  FNestList.FoldGroup := FDefaultGroup;
 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;
+  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
   var
-    y,i,LCnt : integer;
-    HL: TSynCustomFoldHighlighter;
-    NodeList: TLazSynFoldNodeInfoList;
-    TmpNode, CloseNode: TSynFoldNodeInfo;
+    lCount, lLineIdx, i, lAnz: Integer;
+    lNode: TSynFoldNodeInfo;
+  begin
+    lLineIdx := ToIdx(pLine);
+    pNestList.Line := lLineIdx;
+    lCount := pNestList.Count;
+    SetLength(pList, lCount);
+    lAnz := 0;
+    for i := 0 to lCount - 1 do begin
+      lNode := pNestList.HLNode[i];
+      if (sfaInvalid in lNode.FoldAction)
+      or (
+        (sfaOpen in lNode.FoldAction)
+        and (lNode.LineIndex = lLineIdx)
+      ) then
+        Continue;
 
-    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?)
+      pList[i] := lNode;
+      inc(lAnz);
     end;
+    SetLength(pList, lAnz);
+  end;
 
-  begin
-    Result := -1;
-    y := aRow -1;
+var
+  i, lMinAnz, lEndLine, j, l: integer;
+  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
+begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
+  {$ENDIF}
 
-    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
+  if not Assigned(FHighlighter) then exit;
+  FHighlighter.CurrentLines := Lines;
+  if EndLine < 0 then
+    EndLine := StartLine
+  else
+    // endline seems to be the first line after the change
+    EndLine := EndLine - 1;
+  lEndLine := EndLine;
 
-    NodeList := HL.FoldNodeInfo[y];
-    NodeList.AddReference;
-    try
-      NodeList.ActionFilter := [sfaOpen];
-      i := 0;
-      repeat
-        TmpNode := NodeList[i];
+  SetLength(lStartNestList, 0);
+  SetLength(lEndNestList, 0);
 
-        if TmpNode.LogXStart < X-1 then
-        begin
-          inc(i);
-          continue;
-        end;
+  FillNestList(lStartNestList, StartLine, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at Start:');
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-        //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;
+  FillNestList(lEndNestList, EndLine + 1, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at End:');
+  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-        inc(i);
-      until i >= NodeList.Count;
-
-    finally
-      NodeList.ReleaseReference;
+  // delete all nodes in lEndNodeList which where active at StartLine
+  // to get the nodes which reach behind EndLine
+  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
+  for i := 0 to lMinAnz - 1 do begin
+    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
+    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
+    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
+    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
+    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
+      for j := 0 to length(lEndNestList) - 2 do
+        lEndNestList[j] := lEndNestList[j + 1];
+      SetLength(lEndNestList, Length(lEndNestList) - 1);
+    end else begin
+      break
     end;
   end;
 
+  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
+    // deeper fold group than StartLine: fold group ends after EndLine
+    // find real EndLine (end line of first remaining fold node)
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   Remaining Nodes:');
+    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex]]);
+    {$ENDIF}
+    // does position of first character change for remaining node?
+    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
+      // position of first character changed -> find endline
+      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
+  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;
+  // check for changes of endline for node which are active at StartLine
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    if sfaOutline in FoldAction then begin
+      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
+      if l <> FEndLine[LineIndex] then begin
+        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
+        FEndLine[LineIndex] := l;
+        {$IFDEF SynEditMarkupFoldColoringDebug}
+        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+        {$ENDIF}
       end;
     end;
 
-    if n < 0 then exit;
+  // invalidate cache
+  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
+    FFirstCharacterColumn[i] := 0;
+    FEndLine[i] := 0;
+  end;
 
-    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);
+  if lEndLine > EndLine then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
+    {$ENDIF}
+    InvalidateSynLines(EndLine + 1 , lEndLine);
+  end;
+end;
+
+procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
+var
+  old: TSynEditStrings;
+begin
+  old := Lines;
+  if Assigned(old)
+  and (AValue <> old) then begin
+    // change:
+    // remove Changehandler
+    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+  end;
+  inherited SetLines(AValue);
+  if (AValue <> old) then begin
+    // change:
+    if Assigned(AValue) then begin
+      // set cache size
+      SetLength(FFirstCharacterColumn, AValue.Count);
+      SetLength(FEndLine, AValue.Count);
+      // add Changehandler
+      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
+      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+    end else begin
+      // clear cache
+      SetLength(FFirstCharacterColumn, 0);
+      SetLength(FEndLine, 0);
     end;
+  end;
+end;
 
-  end;
+procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
+                                        aIndex, aCount: Integer);
 var
-  EndFoldLine,y : integer;
+  absCount,
+  idx, i: 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);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  idx := ToIdx(aIndex);
+  if aCount < 0 then begin
+    // lines deleted
+    absCount := Abs(aCount);
+    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
+      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
+      FEndLine[i] := FEndLine[i + absCount];
+    end;
   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}
+  SetLength(FFirstCharacterColumn, Sender.Count);
+  SetLength(FEndLine, Sender.Count);
+  if (aCount > 0) then begin
+    if idx >= 0 then begin
+      // lines added
+      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
+        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
+        FEndLine[i + aCount] := FEndLine[i];
+      end;
+      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end else begin
+      // first lines will be inserted
+      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end;
+  end;
 end;
 
-procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
-var Y : integer;
+procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
+  aIndex, aCount: Integer);
+var
+  newHighlighter: TSynCustomFoldHighlighter;
 begin
-  Y := Caret.LineBytePos.y;
-  if Y = FCaretY then exit;
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  if (aIndex <> -1)
+  or (aCount <> -1) then
+    exit;
 
-  FCaretY := Y;
-  FPrevCaretText := Caret.LineText;
-end;
+  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(newHighlighter)
+  and not (newHighlighter is TSynCustomFoldHighlighter) then
+    newHighlighter := nil;
 
+  if (newHighlighter = FHighlighter) then
+    exit;
 
+  FHighlighter := newHighlighter;
 
+  FreeAndNil(FNestList);
+  if Assigned(FHighlighter) then begin
+    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
+    FNestList.ResetFilter;
+    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
+    FNestList.IncludeOpeningOnLine := True; //False; //
+  end;
+end;
+
 end.
 

Pascal Riekenberg

2016-08-16 09:10

reporter  

syneditmarkupfoldcoloring.pas_v8.patch (38,072 bytes)
Index: syneditmarkupfoldcoloring.pas
===================================================================
--- syneditmarkupfoldcoloring.pas	(revision 52808)
+++ syneditmarkupfoldcoloring.pas	(working copy)
@@ -50,12 +50,14 @@
 unit SynEditMarkupFoldColoring;
 
 {$mode objfpc}{$H+}
+{ $define SynEditMarkupFoldColoringDebug}
 
 interface
 
 uses
   Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
-  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
+  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
+  LazSynEditText;
 
 type
 
@@ -66,37 +68,39 @@
     Border  : Boolean;
     Ignore  : Boolean; //no color no line
     SrcNode : TSynFoldNodeInfo;
-    LevelBefore, LevelAfter : integer;//needed by non nest nodes
+    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
   end;
 
   TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
   TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
-
   { TSynEditMarkupFoldColors }
 
   TSynEditMarkupFoldColors = class(TSynEditMarkup)
   private
+    function GetFirstCharacterColumn(index: Integer): Byte;
+  private
+    FHighlighter: TSynCustomFoldHighlighter;
     FNestList: TLazSynEditNestedFoldsList;
+
+    // cache
+    FFirstCharacterColumn: Array of Byte;
+    FEndLine: Array of Integer;
+
     FDefaultGroup: integer;
-     // Physical Position
-    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
+    FFoldColorInfos: TMarkupFoldColorInfos;
     Colors : array of TColor;
-
-    {%region invalidating}
-    CurrentY : integer;  //??
-    FCaretY : integer;    // flag identify for refresh begin______
-    FPrevCaretText : string;  // flag identify for refresh begin______
-    {%endregion}
-
+    FPreparedRow: integer;
+    FLastNode: TSynFoldNodeInfo;
     procedure DoMarkupParentFoldAtRow(aRow: Integer);
     procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
-
-    function GetFoldHighLighter: TSynCustomFoldHighlighter;
     procedure SetDefaultGroup(AValue: integer);
+    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
   protected
     // Notifications about Changes to the text
     procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
-    procedure DoCaretChanged(Sender: TObject); override;
+    procedure SetLines(const AValue: TSynEditStrings); override;
+    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
+    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
   public
     constructor Create(ASynEdit : TSynEditBase);
     destructor Destroy; override;
@@ -115,36 +119,21 @@
 
 implementation
 uses
-  SynEdit,SynEditTypes, SynEditMiscProcs;
+  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
+{$IFDEF SynEditMarkupFoldColoringDebug}
+  , SynHighlighterPas
+{$ENDIF}
+  ;
 
-  {%region Sorting FoldInfo -fold}
-  function CompareFI(Item1, Item2: Pointer): Integer;
-  begin
-    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
-    if result = 0 then
-        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
-    if result = 0 then
-        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
-          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
+{$IFDEF SynEditMarkupFoldColoringDebug}
+function FoldTypeToStr(p_FoldType: Pointer): String;
+begin
+  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
+  while length(Result) < 17 do Result := Result + ' ';
+end;
+{$ENDIF}
 
-    SetLength(result, Length(a));
-    for i := 0 to Pred(l.Count) do
-      result[i] := PMarkupFoldColorInfo(l[i])^;
-     l.Free;
-  end;
-  {%endregion}
-
 { TSynEditMarkupFoldColors }
 
 constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
@@ -151,23 +140,36 @@
 begin
   inherited Create(ASynEdit);
 
-  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
+  if Assigned(Lines) then begin
+    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
+    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+    SetLength(FFirstCharacterColumn, Lines.Count);
+    SetLength(FEndLine, Lines.Count);
+  end;
+
+  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(FHighlighter)
+  and not (FHighlighter  is TSynCustomFoldHighlighter) then
+    FHighlighter := nil;
+
+  FDefaultGroup := 0;
+
+  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
   FNestList.ResetFilter;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
-  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
-  FNestList.IncludeOpeningOnLine := True; //False; //
+  FNestList.FoldGroup := FDefaultGroup;
+  FNestList.FoldFlags :=  [sfbIncludeDisabled];
+  FNestList.IncludeOpeningOnLine := True;
 
   MarkupInfo.Foreground := clGreen;
-  MarkupInfo.Background := clNone; //clFuchsia;
+  MarkupInfo.Background := clNone;
   MarkupInfo.Style := [];
   MarkupInfo.StyleMask := [];
-  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
+  MarkupInfo.FrameEdges:= sfeLeft;
 
   SetLength(Colors, 5);
   Colors[0] := clRed;
   Colors[1] := $000098F7; //orange
   Colors[2] := $0022CC40; //green
-  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
   Colors[3] := $00FF682A; //blue
   Colors[4] := $00CF00C4; //purple
 end;
@@ -174,6 +176,10 @@
 
 destructor TSynEditMarkupFoldColors.Destroy;
 begin
+  if Assigned(Lines) then begin
+    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+  end;
   FreeAndNil(FNestList);
   inherited Destroy;
 end;
@@ -182,23 +188,27 @@
   const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
   const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
 var
-  i,x2both : integer;
+  i, x2both: integer;
 begin
   Result := nil;
-  if (CurrentY = aRow) then begin
+  if not Assigned(FHighlighter) then exit;
+  if (FPreparedRow = aRow) then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
+    {$ENDIF}
 
-    x2both := -3; //flag
-    for i := 0 to length(FHighlights)-1 do
-      with FHighlights[i] do
+    x2both := -3;
+    for i := 0 to length(FFoldColorInfos)-1 do
+      with FFoldColorInfos[i] do
         if not Ignore
         and (X < X2)
         and (ColorIdx >= 0)
         and (aStartCol.Logical >= x)
-        and (aStartCol.Logical < X2) then
-        begin
-          //MarkupInfo.FrameColor:= clGreen; //debug
-          if x2both = -3 then //first call flag
-          begin
+        and (aStartCol.Logical < X2) then begin
+          {$IFDEF SynEditMarkupFoldColoringDebug}
+          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
+          {$ENDIF}
+          if x2both = -3 then begin //first call flag
             MarkupInfo.FrameColor:= clNone;
             MarkupInfo.Foreground:= clNone;
             MarkupInfo.Background:= clNone;
@@ -209,25 +219,14 @@
           Result := MarkupInfo;
           x2both := max(x2both, x2);
           MarkupInfo.SetFrameBoundsLog(x, x2both);
-          if Border then
-          begin
+          if Border then begin
             MarkupInfo.FrameColor:= Colors[ColorIdx];
-            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
-          end
-          else
+            MarkupInfo.FrameEdges:= sfeLeft;
+          end else begin
+            MarkupInfo.FrameColor:= clNone;
+            MarkupInfo.FrameEdges:= sfeNone;
             MarkupInfo.Foreground := Colors[ColorIdx];
-
-          //MarkupInfo.FrameEdges:= sfeAround; //debug
-
-          {//2nd debug
-          if x > x2 then
-          begin
-            MarkupInfo.Background:= clYellow;
-            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
-            MarkupInfo.FrameColor:= clBlue; //debug
-          end;}
-
-          //break;
+          end;
         end;
   end;
 end;
@@ -237,36 +236,80 @@
   const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
 var i : integer;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
+  {$ENDIF}
+  if not Assigned(FHighlighter)
+  or (FPreparedRow <> aRow) then
+    exit;
+
   ANextLog := -1;
   ANextPhys := -1;
-  if (CurrentY = aRow)  then
-  for i := 0 to length(FHighlights)-1  do
-    with FHighlights[i] do
-    begin
-      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
-        //continue;
-      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
-      begin
-        ANextLog := FHighlights[i].X;
+  for i := 0 to length(FFoldColorInfos)-1  do
+    with FFoldColorInfos[i] do begin
+      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
+        ANextLog := FFoldColorInfos[i].X;
         break;
       end;
     end;
 end;
 
+function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
+var
+  l: String;
+  p: Integer;
+begin
+  l := SynEdit.Lines[index];
+  p := 1;
+  while not (l[p] in [#13, #10, #0])
+  and (l[p] = #32) do inc(p);
+  if p > 255 then p := 255;
+  Result := p;
+end;
+
 procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
 var
-  i,lvl,z : integer; //iterate parents fold
+  i,lvl,z: integer;
 
   procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
+  var
+    p, s, l: integer;
   begin
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    // get column of first character in row
+    s := Length(FFirstCharacterColumn);
+    if (s <= ANode.LineIndex)
+    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
+      p := FirstCharacterColumn[ANode.LineIndex];
+      if s > ANode.LineIndex then begin
+        FFirstCharacterColumn[ANode.LineIndex] := p;
+      end else begin
+        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    s := Length(FEndLine);
+    if (s <= ANode.LineIndex)
+    or (FEndLine[ANode.LineIndex] = 0) then begin
+      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
+      if s > ANode.LineIndex then begin
+        FEndLine[ANode.LineIndex] := l;
+      end else begin
+        DebugLn('!!! FEndLine-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z+1);
+    with FFoldColorInfos[z] do begin
+
       SrcNode:= ANode; //needed by close node
-      Border := ANode.LineIndex + 1 <> aRow;
-      X  := ANode.LogXStart + 1;
-      Y  := aRow;//ANode.LineIndex + 1;
-      X2 := X+1; //ANode.LogXEnd + 1;
+      Border := ToPos(ANode.LineIndex) <> aRow;
+      if s <= ANode.LineIndex then
+        X  := p
+      else
+        X  := FFirstCharacterColumn[ANode.LineIndex];
+      Y  := aRow;
+      X2 := X + 1;
       Ignore := False;
 
       if Border and (sfaOutlineNoLine in ANode.FoldAction) then
@@ -273,7 +316,9 @@
         Ignore := True;
       if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
         Ignore := True;
-        ColorIdx := lvl mod (length(Colors))
+      Level := lvl;
+      ColorIdx := Max(0, lvl) mod (length(Colors));
+      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
     end;
   end;
 
@@ -281,60 +326,62 @@
   y, lvlB,lvlA: Integer;
   TmpNode: TSynFoldNodeInfo;
   NestCount : integer;
-  procedure Later(var J:integer);
-  begin
-    inc(J);
-  end;
-  function Allowed(J: integer):boolean;
-  begin
-    result := J < NestCount;
-  end;
 
 begin
-  y := aRow-1;
+  y := ToIdx(aRow);
   FNestList.Line := y;
   NestCount := FNestList.Count;
+  FHighlighter.CurrentLines := Lines;
 
   lvl := 0;
   i := 0;
-  while Allowed(i) do
-  begin
-
+  while i < NestCount do begin
     TmpNode := FNestList.HLNode[i];
-    //find till valid
-    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
-    begin
-      Later(i);
-      TmpNode := FNestList.HLNode[i];
-    end;
+    if (sfaOutline in TmpNode.FoldAction)
+    and not (sfaInvalid in TmpNode.FoldAction) then
+      //avoid bug of IncludeOpeningOnLine := False;
+      if (sfaOpen in TmpNode.FoldAction)
+      and (TmpNode.LineIndex + 1 = aRow) then begin
+        {do nothing here}
+      end else begin
+        lvlB := lvl;
 
-    if (sfaOutline in TmpNode.FoldAction ) then
-    //avoid bug of IncludeOpeningOnLine := False;
-    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
-    begin {do nothing here} end
-    else
-    begin
-      lvlB := lvl;
+        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
+          inc(lvl)
+        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+          dec(lvl);
+        //if (FLastNode.LineIndex >= 0)
+        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+        // inc(lvl);
 
-      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-        inc(lvl);
-      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
-        dec(lvl);
+        AddVerticalLine(TmpNode);
 
-      AddVerticalLine(TmpNode);
-      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-        inc(lvl);
+        //if (z > 0)
+        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
+        //  // if child is on same x-pos keep level
+        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
+        //    lvl := FFoldColorInfos[z - 1].Level;
+        //    FFoldColorInfos[z].Level := lvl;
+        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+        //  end;
+        //end;
 
-      lvlA := lvl;
+        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
+          inc(lvl);
 
-      with FHighlights[z] do begin
-        LevelBefore := lvlB;
-        LevelAfter  := lvlA;
+        if sfaOpen in TmpNode.FoldAction then
+          FLastNode := TmpNode;
+
+        lvlA := lvl;
+
+        with FFoldColorInfos[z] do begin
+          LevelBefore := lvlB;
+          LevelAfter  := lvlA;
+        end;
       end;
-    end;
-
-    Later(i);
-    //break; //debug
+    inc(i);
   end;
 end;
 
@@ -345,33 +392,29 @@
   procedure AddHighlight( ANode: TSynFoldNodeInfo );
   var x,j : integer;
   begin
-        //don't replace; don't add when already found
     x  := ANode.LogXStart + 1;
-
     if ANode.LogXStart < ANode.LogXEnd then
-    for j := 0 to Pred(length(FHighlights)) do
-      if (FHighlights[j].X = x)
-      and (FHighlights[j].Border)
-      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
-      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
+    for j := 0 to Pred(length(FFoldColorInfos)) do
+      if (FFoldColorInfos[j].X = x)
+      and (FFoldColorInfos[j].Border)
+      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
+      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
       then begin
-       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
-       FHighlights[j].Border := False
-
+       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
+       FFoldColorInfos[j].Border := False
       end;
 
-    //exit; //debug
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z + 1);
+    with FFoldColorInfos[z] do begin
       Border := False;
       SrcNode:= ANode; //needed by close node
       Y  := ANode.LineIndex + 1;
       X  := ANode.LogXStart + 1;
       X2 := ANode.LogXEnd + 1;
-      //ColorIdx := lvl;
+      Level := lvl;
       if not (sfaOutlineNocolor in ANode.FoldAction) then
-         ColorIdx := lvl mod (length(Colors))
+         ColorIdx := Max(0, lvl) mod (length(Colors))
       else
          ColorIdx := -1;
     end;
@@ -378,91 +421,105 @@
   end;
 
 var
-  y,i,j,lvlB,lvlA : integer;
-  HL: TSynCustomFoldHighlighter;
+  LineIdx,i,j,lvlB,lvlA : integer;
   NodeList: TLazSynFoldNodeInfoList;
   TmpNode: TSynFoldNodeInfo;
-  Found : boolean;
+  Found: boolean;
 begin
-  y := aRow -1;
+  LineIdx := ToIdx(aRow);
 
-  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-  HL.CurrentLines := Lines;
-  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
+  FHighlighter.CurrentLines := Lines;
+  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
 
-  NodeList := HL.FoldNodeInfo[y];
+  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
   NodeList.AddReference;
   try
     NodeList.ActionFilter := [sfaOutline];
-    //NodeList.FoldFlags:= [sfbIncludeDisabled];
     lvl := 0;
-    J := Length(FHighlights)-1;
+    J := Length(FFoldColorInfos) - 1;
     if J >=0 then
-      lvl := max(0,FHighlights[J].LevelAfter);
+      lvl := max(0,FFoldColorInfos[J].LevelAfter);
     i := 0;
     repeat
       TmpNode := NodeList[i];
 
-      //find till valid
-      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
-      begin
-        inc(i);
-        TmpNode := NodeList[i];
-      end;
-      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
-        if sfaOpen in TmpNode.FoldAction then
-        begin
+      if not (sfaInvalid in TmpNode.FoldAction)
+      and (sfaOutline in TmpNode.FoldAction) then begin
+        if sfaOpen in TmpNode.FoldAction then begin
           lvlB := lvl;
 
           if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-            inc(lvl);
-          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+            inc(lvl)
+          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
             dec(lvl);
+          //if (FLastNode.LineIndex >= 0)
+          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+          // inc(lvl);
 
           AddHighlight(TmpNode);
-          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
+
+          //if (z > 0)
+          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
+          //begin
+          //  // if child is on same x-pos keep level
+          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
+          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
+          //    lvl := FFoldColorInfos[z - 1].Level;
+          //    FFoldColorInfos[z].Level := lvl;
+          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+          //  end;
+          //end;
+
+          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
             inc(lvl);
+
           lvlA := lvl;
 
-          with FHighlights[z] do begin
+          if sfaOpen in TmpNode.FoldAction then
+            FLastNode := TmpNode;
+
+          with FFoldColorInfos[z] do begin
             LevelBefore := lvlB;
             LevelAfter  := lvlA;
           end;
-
-        end
-        else
-        if sfaClose in TmpNode.FoldAction then
-        begin
+        end else if sfaClose in TmpNode.FoldAction then begin
           Found := False;
-          for j := Length(FHighlights)-1 downto 0 do begin
-            with FHighlights[j].SrcNode do begin
-              if  (FoldType = TmpNode.FoldType) and
-                (FoldGroup = TmpNode.FoldGroup) and
-                (sfaOpen in FoldAction) and
-                // (FoldLvlEnd = TmpNode.FoldLvlStart)
-                (NestLvlEnd = TmpNode.NestLvlStart)
-
-                then begin
-                  lvl := FHighlights[j].ColorIdx;
-                  lvlB := FHighlights[j].LevelBefore;
-                  Found := True;
-                  break;
-                end;
+          for j := Length(FFoldColorInfos)-1 downto 0 do begin
+            with FFoldColorInfos[j].SrcNode do begin
+              if (FoldType = TmpNode.FoldType)
+              and (FoldGroup = TmpNode.FoldGroup)
+              and (sfaOpen in FoldAction)
+              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
+                lvlB := lvl;
+                lvl := FFoldColorInfos[j].Level;
+                lvlA := FFoldColorInfos[j].LevelAfter;
+                FLastNode := TmpNode;
+                Found := True;
+                break;
+              end;
             end;
           end;
           if Found then begin
             AddHighlight(TmpNode);
-            lvl := lvlB;
+            with FFoldColorInfos[z] do begin
+              LevelBefore := lvlB;
+              LevelAfter  := lvlA;
+            end;
+            // if found opening position is behind closing position:
+            // delete this as it does not have to be drawn
+            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
+              for j := j to z - 1 do begin
+                FFoldColorInfos[j] := FFoldColorInfos[j+1];
+              end;
+              SetLength(FFoldColorInfos, z);
+            end;
           end;
-
-          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-            //inc(lvl);
         end;
       end;
-
       inc(i);
     until i >= NodeList.Count;
-
   finally
     NodeList.ReleaseReference;
   end;
@@ -469,197 +526,286 @@
 end;
 
 procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
+var
+  i, LastX, j: Integer;
 begin
-  CurrentY := aRow;
-  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('PrepareMarkupForRow %d', [aRow]);
+  {$ENDIF}
+  if not Assigned(FHighlighter) then exit;
+  FPreparedRow := aRow;
+  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
 
   if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
     exit;
 
-  //DoMarkupFoldAtRow(aRow);
+  // invalidate fLastNode
+  FLastNode.LineIndex := -1;
+
   DoMarkupParentFoldAtRow(aRow);
   DoMarkupParentCloseFoldAtRow(aRow);
-  //DoMarkupRangeFoldAtRow(aRow);
 
-  FHighlights := SortLeftMostFI(FHighlights);
+  // delete parents with bigger x
+  // to keep out mis indented blocks
+  LastX := MaxInt;
+  for i := length(FFoldColorInfos) - 1 downto 0 do begin
+    if FFoldColorInfos[i].X > LastX then begin
+      for j := i to length(FFoldColorInfos) - 2 do begin
+        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
+      end;
+      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
+    end;
+    LastX := FFoldColorInfos[i].X;
+  end;
 end;
 
 procedure TSynEditMarkupFoldColors.EndMarkup;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('EndMarkup');
+  {$ENDIF}
   inherited EndMarkup;
   FNestList.Clear; // for next markup start
 end;
 
-function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
-begin
-  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-end;
-
 procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
 begin
   if FDefaultGroup = AValue then Exit;
   FDefaultGroup := AValue;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+  FNestList.FoldGroup := FDefaultGroup;
 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;
+  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
   var
-    y,i,LCnt : integer;
-    HL: TSynCustomFoldHighlighter;
-    NodeList: TLazSynFoldNodeInfoList;
-    TmpNode, CloseNode: TSynFoldNodeInfo;
+    lCount, lLineIdx, i, lAnz: Integer;
+    lNode: TSynFoldNodeInfo;
+  begin
+    lLineIdx := ToIdx(pLine);
+    pNestList.Line := lLineIdx;
+    lCount := pNestList.Count;
+    SetLength(pList, lCount);
+    lAnz := 0;
+    for i := 0 to lCount - 1 do begin
+      lNode := pNestList.HLNode[i];
+      if (sfaInvalid in lNode.FoldAction)
+      or (
+        (sfaOpen in lNode.FoldAction)
+        and (lNode.LineIndex = lLineIdx)
+      ) then
+        Continue;
 
-    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;
+      pList[i] := lNode;
+      inc(lAnz);
+    end;
+    SetLength(pList, lAnz);
+  end;
 
-    begin
-      Result := SearchLine(YIndex, NIndex);
-      if not (sfaInvalid in Result.FoldAction) then
-        exit;
+var
+  i, lMinAnz, lEndLine, j, l, lEnd, lEndLine2: integer;
+  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
+begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
+  {$ENDIF}
 
-      inc(YIndex);
-      while (YIndex < LCnt) and
-            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
-             > StartNode.NestLvlStart)
-      do
-        inc(YIndex);
-      if YIndex = LCnt then
-        exit;
+  if not Assigned(FHighlighter) then exit;
+  FHighlighter.CurrentLines := Lines;
+  if EndLine < 0 then
+    EndLine := StartLine
+  else
+    // endline seems to be the first line after the change
+    EndLine := EndLine - 1;
+  lEndLine := EndLine;
 
-      NIndex := -1;
-      Result := SearchLine(YIndex, NIndex);
+  SetLength(lStartNestList, 0);
+  SetLength(lEndNestList, 0);
 
-      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
-        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
-    end;
+  FillNestList(lStartNestList, StartLine, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at Start:');
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-  begin
-    Result := -1;
-    y := aRow -1;
+  FillNestList(lEndNestList, EndLine + 1, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at End:');
+  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-    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
+  // delete all nodes in lEndNodeList which where active at StartLine
+  // to get the nodes which reach behind EndLine
+  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
+  for i := 0 to lMinAnz - 1 do begin
+    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
+    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
+    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
+    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
+    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
 
-    NodeList := HL.FoldNodeInfo[y];
-    NodeList.AddReference;
-    try
-      NodeList.ActionFilter := [sfaOpen];
-      i := 0;
-      repeat
-        TmpNode := NodeList[i];
+      // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
+      lEnd := FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0);
+      if lEnd >= 0 then lEndLine2 := ToPos(lEnd);
 
-        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;
+      for j := 0 to length(lEndNestList) - 2 do
+        lEndNestList[j] := lEndNestList[j + 1];
+      SetLength(lEndNestList, Length(lEndNestList) - 1);
+    end else begin
+      break
     end;
   end;
 
+  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
+    // deeper fold group than StartLine: fold group ends after EndLine
+    // find real EndLine (end line of first remaining fold node)
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   Remaining Nodes:');
+    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+    {$ENDIF}
+    // does position of first character change for remaining node?
+    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
+      // position of first character changed -> find endline
+      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
 
-  function IsFoldMoved( aRow: Integer ): integer;
-  var S : string;
-    i,n : integer;
-  begin
-    Result := -1;
-    n := -1;
+    // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
+    if lEndLine = 0 then
+      lEndLine := lEndLine2;
+  end;
 
-    S := Caret.LineText;
-    for i := 1 to Min(Length(S), Length(FPrevCaretText)) do
-    begin
-      if S[i] <> FPrevCaretText[i] then
-      begin
-        n := i;
-        break;
+  // check for changes of endline for node which are active at StartLine
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    if sfaOutline in FoldAction then begin
+      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
+      if l <> FEndLine[LineIndex] then begin
+        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
+        FEndLine[LineIndex] := l;
+        {$IFDEF SynEditMarkupFoldColoringDebug}
+        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+        {$ENDIF}
       end;
     end;
 
-    if n < 0 then exit;
+  // invalidate cache
+  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
+    FFirstCharacterColumn[i] := 0;
+    FEndLine[i] := 0;
+  end;
 
-    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);
+  if lEndLine > EndLine then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
+    {$ENDIF}
+    InvalidateSynLines(EndLine + 1 , lEndLine);
+  end;
+end;
+
+procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
+var
+  old: TSynEditStrings;
+begin
+  old := Lines;
+  if Assigned(old)
+  and (AValue <> old) then begin
+    // change:
+    // remove Changehandler
+    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+  end;
+  inherited SetLines(AValue);
+  if (AValue <> old) then begin
+    // change:
+    if Assigned(AValue) then begin
+      // set cache size
+      SetLength(FFirstCharacterColumn, AValue.Count);
+      SetLength(FEndLine, AValue.Count);
+      // add Changehandler
+      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
+      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+    end else begin
+      // clear cache
+      SetLength(FFirstCharacterColumn, 0);
+      SetLength(FEndLine, 0);
     end;
+  end;
+end;
 
-  end;
+procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
+                                        aIndex, aCount: Integer);
 var
-  EndFoldLine,y : integer;
+  absCount,
+  idx, i: 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);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  idx := ToIdx(aIndex);
+  if aCount < 0 then begin
+    // lines deleted
+    absCount := Abs(aCount);
+    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
+      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
+      FEndLine[i] := FEndLine[i + absCount];
+    end;
   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}
+  SetLength(FFirstCharacterColumn, Sender.Count);
+  SetLength(FEndLine, Sender.Count);
+  if (aCount > 0) then begin
+    if idx >= 0 then begin
+      // lines added
+      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
+        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
+        FEndLine[i + aCount] := FEndLine[i];
+      end;
+      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end else begin
+      // first lines will be inserted
+      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end;
+  end;
 end;
 
-procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
-var Y : integer;
+procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
+  aIndex, aCount: Integer);
+var
+  newHighlighter: TSynCustomFoldHighlighter;
 begin
-  Y := Caret.LineBytePos.y;
-  if Y = FCaretY then exit;
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  if (aIndex <> -1)
+  or (aCount <> -1) then
+    exit;
 
-  FCaretY := Y;
-  FPrevCaretText := Caret.LineText;
-end;
+  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(newHighlighter)
+  and not (newHighlighter is TSynCustomFoldHighlighter) then
+    newHighlighter := nil;
 
+  if (newHighlighter = FHighlighter) then
+    exit;
 
+  FHighlighter := newHighlighter;
 
+  FreeAndNil(FNestList);
+  if Assigned(FHighlighter) then begin
+    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
+    FNestList.ResetFilter;
+    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
+    FNestList.IncludeOpeningOnLine := True; //False; //
+  end;
+end;
+
 end.
 

Pascal Riekenberg

2016-09-06 05:46

reporter  

syneditmarkupfoldcoloring.pas_v9.patch (38,461 bytes)
Index: syneditmarkupfoldcoloring.pas
===================================================================
--- syneditmarkupfoldcoloring.pas	(revision 52919)
+++ syneditmarkupfoldcoloring.pas	(working copy)
@@ -50,12 +50,14 @@
 unit SynEditMarkupFoldColoring;
 
 {$mode objfpc}{$H+}
+{ $define SynEditMarkupFoldColoringDebug}
 
 interface
 
 uses
   Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
-  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
+  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
+  LazSynEditText;
 
 type
 
@@ -66,37 +68,39 @@
     Border  : Boolean;
     Ignore  : Boolean; //no color no line
     SrcNode : TSynFoldNodeInfo;
-    LevelBefore, LevelAfter : integer;//needed by non nest nodes
+    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
   end;
 
   TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
   TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
-
   { TSynEditMarkupFoldColors }
 
   TSynEditMarkupFoldColors = class(TSynEditMarkup)
   private
+    function GetFirstCharacterColumn(index: Integer): Byte;
+  private
+    FHighlighter: TSynCustomFoldHighlighter;
     FNestList: TLazSynEditNestedFoldsList;
+
+    // cache
+    FFirstCharacterColumn: Array of Byte;
+    FEndLine: Array of Integer;
+
     FDefaultGroup: integer;
-     // Physical Position
-    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
+    FFoldColorInfos: TMarkupFoldColorInfos;
     Colors : array of TColor;
-
-    {%region invalidating}
-    CurrentY : integer;  //??
-    FCaretY : integer;    // flag identify for refresh begin______
-    FPrevCaretText : string;  // flag identify for refresh begin______
-    {%endregion}
-
+    FPreparedRow: integer;
+    FLastNode: TSynFoldNodeInfo;
     procedure DoMarkupParentFoldAtRow(aRow: Integer);
     procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
-
-    function GetFoldHighLighter: TSynCustomFoldHighlighter;
     procedure SetDefaultGroup(AValue: integer);
+    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
   protected
     // Notifications about Changes to the text
     procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
-    procedure DoCaretChanged(Sender: TObject); override;
+    procedure SetLines(const AValue: TSynEditStrings); override;
+    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
+    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
   public
     constructor Create(ASynEdit : TSynEditBase);
     destructor Destroy; override;
@@ -115,36 +119,21 @@
 
 implementation
 uses
-  SynEdit,SynEditTypes, SynEditMiscProcs;
+  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
+{$IFDEF SynEditMarkupFoldColoringDebug}
+  , SynHighlighterPas
+{$ENDIF}
+  ;
 
-  {%region Sorting FoldInfo -fold}
-  function CompareFI(Item1, Item2: Pointer): Integer;
-  begin
-    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
-    if result = 0 then
-        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
-    if result = 0 then
-        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
-          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
+{$IFDEF SynEditMarkupFoldColoringDebug}
+function FoldTypeToStr(p_FoldType: Pointer): String;
+begin
+  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
+  while length(Result) < 17 do Result := Result + ' ';
+end;
+{$ENDIF}
 
-    SetLength(result, Length(a));
-    for i := 0 to Pred(l.Count) do
-      result[i] := PMarkupFoldColorInfo(l[i])^;
-     l.Free;
-  end;
-  {%endregion}
-
 { TSynEditMarkupFoldColors }
 
 constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
@@ -151,23 +140,36 @@
 begin
   inherited Create(ASynEdit);
 
-  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
+  if Assigned(Lines) then begin
+    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
+    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+    SetLength(FFirstCharacterColumn, Lines.Count);
+    SetLength(FEndLine, Lines.Count);
+  end;
+
+  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(FHighlighter)
+  and not (FHighlighter  is TSynCustomFoldHighlighter) then
+    FHighlighter := nil;
+
+  FDefaultGroup := 0;
+
+  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
   FNestList.ResetFilter;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
-  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
-  FNestList.IncludeOpeningOnLine := True; //False; //
+  FNestList.FoldGroup := FDefaultGroup;
+  FNestList.FoldFlags :=  [sfbIncludeDisabled];
+  FNestList.IncludeOpeningOnLine := True;
 
   MarkupInfo.Foreground := clGreen;
-  MarkupInfo.Background := clNone; //clFuchsia;
+  MarkupInfo.Background := clNone;
   MarkupInfo.Style := [];
   MarkupInfo.StyleMask := [];
-  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
+  MarkupInfo.FrameEdges:= sfeLeft;
 
   SetLength(Colors, 5);
   Colors[0] := clRed;
   Colors[1] := $000098F7; //orange
   Colors[2] := $0022CC40; //green
-  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
   Colors[3] := $00FF682A; //blue
   Colors[4] := $00CF00C4; //purple
 end;
@@ -174,6 +176,10 @@
 
 destructor TSynEditMarkupFoldColors.Destroy;
 begin
+  if Assigned(Lines) then begin
+    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+  end;
   FreeAndNil(FNestList);
   inherited Destroy;
 end;
@@ -182,23 +188,27 @@
   const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
   const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
 var
-  i,x2both : integer;
+  i, x2both: integer;
 begin
   Result := nil;
-  if (CurrentY = aRow) then begin
+  if not Assigned(FHighlighter) then exit;
+  if (FPreparedRow = aRow) then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
+    {$ENDIF}
 
-    x2both := -3; //flag
-    for i := 0 to length(FHighlights)-1 do
-      with FHighlights[i] do
+    x2both := -3;
+    for i := 0 to length(FFoldColorInfos)-1 do
+      with FFoldColorInfos[i] do
         if not Ignore
         and (X < X2)
         and (ColorIdx >= 0)
         and (aStartCol.Logical >= x)
-        and (aStartCol.Logical < X2) then
-        begin
-          //MarkupInfo.FrameColor:= clGreen; //debug
-          if x2both = -3 then //first call flag
-          begin
+        and (aStartCol.Logical < X2) then begin
+          {$IFDEF SynEditMarkupFoldColoringDebug}
+          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
+          {$ENDIF}
+          if x2both = -3 then begin //first call flag
             MarkupInfo.FrameColor:= clNone;
             MarkupInfo.Foreground:= clNone;
             MarkupInfo.Background:= clNone;
@@ -209,25 +219,14 @@
           Result := MarkupInfo;
           x2both := max(x2both, x2);
           MarkupInfo.SetFrameBoundsLog(x, x2both);
-          if Border then
-          begin
+          if Border then begin
             MarkupInfo.FrameColor:= Colors[ColorIdx];
-            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
-          end
-          else
+            MarkupInfo.FrameEdges:= sfeLeft;
+          end else begin
+            MarkupInfo.FrameColor:= clNone;
+            MarkupInfo.FrameEdges:= sfeNone;
             MarkupInfo.Foreground := Colors[ColorIdx];
-
-          //MarkupInfo.FrameEdges:= sfeAround; //debug
-
-          {//2nd debug
-          if x > x2 then
-          begin
-            MarkupInfo.Background:= clYellow;
-            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
-            MarkupInfo.FrameColor:= clBlue; //debug
-          end;}
-
-          //break;
+          end;
         end;
   end;
 end;
@@ -237,36 +236,80 @@
   const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
 var i : integer;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
+  {$ENDIF}
+  if not Assigned(FHighlighter)
+  or (FPreparedRow <> aRow) then
+    exit;
+
   ANextLog := -1;
   ANextPhys := -1;
-  if (CurrentY = aRow)  then
-  for i := 0 to length(FHighlights)-1  do
-    with FHighlights[i] do
-    begin
-      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
-        //continue;
-      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
-      begin
-        ANextLog := FHighlights[i].X;
+  for i := 0 to length(FFoldColorInfos)-1  do
+    with FFoldColorInfos[i] do begin
+      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
+        ANextLog := FFoldColorInfos[i].X;
         break;
       end;
     end;
 end;
 
+function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
+var
+  l: String;
+  p: Integer;
+begin
+  l := SynEdit.Lines[index];
+  p := 1;
+  while not (l[p] in [#13, #10, #0])
+  and (l[p] = #32) do inc(p);
+  if p > 255 then p := 255;
+  Result := p;
+end;
+
 procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
 var
-  i,lvl,z : integer; //iterate parents fold
+  i,lvl,z: integer;
 
   procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
+  var
+    p, s, l: integer;
   begin
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    // get column of first character in row
+    s := Length(FFirstCharacterColumn);
+    if (s <= ANode.LineIndex)
+    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
+      p := FirstCharacterColumn[ANode.LineIndex];
+      if s > ANode.LineIndex then begin
+        FFirstCharacterColumn[ANode.LineIndex] := p;
+      end else begin
+        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    s := Length(FEndLine);
+    if (s <= ANode.LineIndex)
+    or (FEndLine[ANode.LineIndex] = 0) then begin
+      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
+      if s > ANode.LineIndex then begin
+        FEndLine[ANode.LineIndex] := l;
+      end else begin
+        DebugLn('!!! FEndLine-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z+1);
+    with FFoldColorInfos[z] do begin
+
       SrcNode:= ANode; //needed by close node
-      Border := ANode.LineIndex + 1 <> aRow;
-      X  := ANode.LogXStart + 1;
-      Y  := aRow;//ANode.LineIndex + 1;
-      X2 := X+1; //ANode.LogXEnd + 1;
+      Border := ToPos(ANode.LineIndex) <> aRow;
+      if s <= ANode.LineIndex then
+        X  := p
+      else
+        X  := FFirstCharacterColumn[ANode.LineIndex];
+      Y  := aRow;
+      X2 := X + 1;
       Ignore := False;
 
       if Border and (sfaOutlineNoLine in ANode.FoldAction) then
@@ -273,7 +316,9 @@
         Ignore := True;
       if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
         Ignore := True;
-        ColorIdx := lvl mod (length(Colors))
+      Level := lvl;
+      ColorIdx := Max(0, lvl) mod (length(Colors));
+      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
     end;
   end;
 
@@ -281,60 +326,62 @@
   y, lvlB,lvlA: Integer;
   TmpNode: TSynFoldNodeInfo;
   NestCount : integer;
-  procedure Later(var J:integer);
-  begin
-    inc(J);
-  end;
-  function Allowed(J: integer):boolean;
-  begin
-    result := J < NestCount;
-  end;
 
 begin
-  y := aRow-1;
+  y := ToIdx(aRow);
   FNestList.Line := y;
   NestCount := FNestList.Count;
+  FHighlighter.CurrentLines := Lines;
 
   lvl := 0;
   i := 0;
-  while Allowed(i) do
-  begin
-
+  while i < NestCount do begin
     TmpNode := FNestList.HLNode[i];
-    //find till valid
-    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
-    begin
-      Later(i);
-      TmpNode := FNestList.HLNode[i];
-    end;
+    if (sfaOutline in TmpNode.FoldAction)
+    and not (sfaInvalid in TmpNode.FoldAction) then
+      //avoid bug of IncludeOpeningOnLine := False;
+      if (sfaOpen in TmpNode.FoldAction)
+      and (TmpNode.LineIndex + 1 = aRow) then begin
+        {do nothing here}
+      end else begin
+        lvlB := lvl;
 
-    if (sfaOutline in TmpNode.FoldAction ) then
-    //avoid bug of IncludeOpeningOnLine := False;
-    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
-    begin {do nothing here} end
-    else
-    begin
-      lvlB := lvl;
+        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
+          inc(lvl)
+        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+          dec(lvl);
+        //if (FLastNode.LineIndex >= 0)
+        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+        // inc(lvl);
 
-      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-        inc(lvl);
-      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
-        dec(lvl);
+        AddVerticalLine(TmpNode);
 
-      AddVerticalLine(TmpNode);
-      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-        inc(lvl);
+        //if (z > 0)
+        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
+        //  // if child is on same x-pos keep level
+        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
+        //    lvl := FFoldColorInfos[z - 1].Level;
+        //    FFoldColorInfos[z].Level := lvl;
+        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+        //  end;
+        //end;
 
-      lvlA := lvl;
+        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
+          inc(lvl);
 
-      with FHighlights[z] do begin
-        LevelBefore := lvlB;
-        LevelAfter  := lvlA;
+        if sfaOpen in TmpNode.FoldAction then
+          FLastNode := TmpNode;
+
+        lvlA := lvl;
+
+        with FFoldColorInfos[z] do begin
+          LevelBefore := lvlB;
+          LevelAfter  := lvlA;
+        end;
       end;
-    end;
-
-    Later(i);
-    //break; //debug
+    inc(i);
   end;
 end;
 
@@ -345,33 +392,29 @@
   procedure AddHighlight( ANode: TSynFoldNodeInfo );
   var x,j : integer;
   begin
-        //don't replace; don't add when already found
     x  := ANode.LogXStart + 1;
-
     if ANode.LogXStart < ANode.LogXEnd then
-    for j := 0 to Pred(length(FHighlights)) do
-      if (FHighlights[j].X = x)
-      and (FHighlights[j].Border)
-      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
-      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
+    for j := 0 to Pred(length(FFoldColorInfos)) do
+      if (FFoldColorInfos[j].X = x)
+      and (FFoldColorInfos[j].Border)
+      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
+      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
       then begin
-       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
-       FHighlights[j].Border := False
-
+       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
+       FFoldColorInfos[j].Border := False
       end;
 
-    //exit; //debug
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z + 1);
+    with FFoldColorInfos[z] do begin
       Border := False;
       SrcNode:= ANode; //needed by close node
       Y  := ANode.LineIndex + 1;
       X  := ANode.LogXStart + 1;
       X2 := ANode.LogXEnd + 1;
-      //ColorIdx := lvl;
+      Level := lvl;
       if not (sfaOutlineNocolor in ANode.FoldAction) then
-         ColorIdx := lvl mod (length(Colors))
+         ColorIdx := Max(0, lvl) mod (length(Colors))
       else
          ColorIdx := -1;
     end;
@@ -378,91 +421,105 @@
   end;
 
 var
-  y,i,j,lvlB,lvlA : integer;
-  HL: TSynCustomFoldHighlighter;
+  LineIdx,i,j,lvlB,lvlA : integer;
   NodeList: TLazSynFoldNodeInfoList;
   TmpNode: TSynFoldNodeInfo;
-  Found : boolean;
+  Found: boolean;
 begin
-  y := aRow -1;
+  LineIdx := ToIdx(aRow);
 
-  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-  HL.CurrentLines := Lines;
-  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
+  FHighlighter.CurrentLines := Lines;
+  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
 
-  NodeList := HL.FoldNodeInfo[y];
+  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
   NodeList.AddReference;
   try
     NodeList.ActionFilter := [sfaOutline];
-    //NodeList.FoldFlags:= [sfbIncludeDisabled];
     lvl := 0;
-    J := Length(FHighlights)-1;
+    J := Length(FFoldColorInfos) - 1;
     if J >=0 then
-      lvl := max(0,FHighlights[J].LevelAfter);
+      lvl := max(0,FFoldColorInfos[J].LevelAfter);
     i := 0;
     repeat
       TmpNode := NodeList[i];
 
-      //find till valid
-      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
-      begin
-        inc(i);
-        TmpNode := NodeList[i];
-      end;
-      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
-        if sfaOpen in TmpNode.FoldAction then
-        begin
+      if not (sfaInvalid in TmpNode.FoldAction)
+      and (sfaOutline in TmpNode.FoldAction) then begin
+        if sfaOpen in TmpNode.FoldAction then begin
           lvlB := lvl;
 
           if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-            inc(lvl);
-          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+            inc(lvl)
+          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
             dec(lvl);
+          //if (FLastNode.LineIndex >= 0)
+          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+          // inc(lvl);
 
           AddHighlight(TmpNode);
-          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
+
+          //if (z > 0)
+          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
+          //begin
+          //  // if child is on same x-pos keep level
+          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
+          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
+          //    lvl := FFoldColorInfos[z - 1].Level;
+          //    FFoldColorInfos[z].Level := lvl;
+          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+          //  end;
+          //end;
+
+          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
             inc(lvl);
+
           lvlA := lvl;
 
-          with FHighlights[z] do begin
+          if sfaOpen in TmpNode.FoldAction then
+            FLastNode := TmpNode;
+
+          with FFoldColorInfos[z] do begin
             LevelBefore := lvlB;
             LevelAfter  := lvlA;
           end;
-
-        end
-        else
-        if sfaClose in TmpNode.FoldAction then
-        begin
+        end else if sfaClose in TmpNode.FoldAction then begin
           Found := False;
-          for j := Length(FHighlights)-1 downto 0 do begin
-            with FHighlights[j].SrcNode do begin
-              if  (FoldType = TmpNode.FoldType) and
-                (FoldGroup = TmpNode.FoldGroup) and
-                (sfaOpen in FoldAction) and
-                // (FoldLvlEnd = TmpNode.FoldLvlStart)
-                (NestLvlEnd = TmpNode.NestLvlStart)
-
-                then begin
-                  lvl := FHighlights[j].ColorIdx;
-                  lvlB := FHighlights[j].LevelBefore;
-                  Found := True;
-                  break;
-                end;
+          for j := Length(FFoldColorInfos)-1 downto 0 do begin
+            with FFoldColorInfos[j].SrcNode do begin
+              if (FoldType = TmpNode.FoldType)
+              and (FoldGroup = TmpNode.FoldGroup)
+              and (sfaOpen in FoldAction)
+              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
+                lvlB := lvl;
+                lvl := FFoldColorInfos[j].Level;
+                lvlA := FFoldColorInfos[j].LevelAfter;
+                FLastNode := TmpNode;
+                Found := True;
+                break;
+              end;
             end;
           end;
           if Found then begin
             AddHighlight(TmpNode);
-            lvl := lvlB;
+            with FFoldColorInfos[z] do begin
+              LevelBefore := lvlB;
+              LevelAfter  := lvlA;
+            end;
+            // if found opening position is behind closing position:
+            // delete this as it does not have to be drawn
+            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
+              for j := j to z - 1 do begin
+                FFoldColorInfos[j] := FFoldColorInfos[j+1];
+              end;
+              SetLength(FFoldColorInfos, z);
+            end;
           end;
-
-          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-            //inc(lvl);
         end;
       end;
-
       inc(i);
     until i >= NodeList.Count;
-
   finally
     NodeList.ReleaseReference;
   end;
@@ -469,197 +526,294 @@
 end;
 
 procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
+var
+  i, LastX, j: Integer;
 begin
-  CurrentY := aRow;
-  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('PrepareMarkupForRow %d', [aRow]);
+  {$ENDIF}
+  if not Assigned(FHighlighter) then exit;
+  FPreparedRow := aRow;
+  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
 
   if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
     exit;
 
-  //DoMarkupFoldAtRow(aRow);
+  // invalidate fLastNode
+  FLastNode.LineIndex := -1;
+
   DoMarkupParentFoldAtRow(aRow);
   DoMarkupParentCloseFoldAtRow(aRow);
-  //DoMarkupRangeFoldAtRow(aRow);
 
-  FHighlights := SortLeftMostFI(FHighlights);
+  // delete parents with bigger x
+  // to keep out mis indented blocks
+  LastX := MaxInt;
+  for i := length(FFoldColorInfos) - 1 downto 0 do begin
+    if FFoldColorInfos[i].X > LastX then begin
+      for j := i to length(FFoldColorInfos) - 2 do begin
+        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
+      end;
+      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
+    end;
+    LastX := FFoldColorInfos[i].X;
+  end;
 end;
 
 procedure TSynEditMarkupFoldColors.EndMarkup;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('EndMarkup');
+  {$ENDIF}
   inherited EndMarkup;
   FNestList.Clear; // for next markup start
 end;
 
-function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
-begin
-  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-end;
-
 procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
 begin
   if FDefaultGroup = AValue then Exit;
   FDefaultGroup := AValue;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+  FNestList.FoldGroup := FDefaultGroup;
 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;
+  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
   var
-    y,i,LCnt : integer;
-    HL: TSynCustomFoldHighlighter;
-    NodeList: TLazSynFoldNodeInfoList;
-    TmpNode, CloseNode: TSynFoldNodeInfo;
+    lCount, lLineIdx, i, lAnz: Integer;
+    lNode: TSynFoldNodeInfo;
+  begin
+    lLineIdx := ToIdx(pLine);
+    pNestList.Line := lLineIdx;
+    lCount := pNestList.Count;
+    SetLength(pList, lCount);
+    lAnz := 0;
+    for i := 0 to lCount - 1 do begin
+      lNode := pNestList.HLNode[i];
+      if (sfaInvalid in lNode.FoldAction)
+      or (
+        (sfaOpen in lNode.FoldAction)
+        and (lNode.LineIndex = lLineIdx)
+      ) then
+        Continue;
 
-    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;
+      pList[i] := lNode;
+      inc(lAnz);
+    end;
+    SetLength(pList, lAnz);
+  end;
 
-    begin
-      Result := SearchLine(YIndex, NIndex);
-      if not (sfaInvalid in Result.FoldAction) then
-        exit;
+var
+  i, lMinAnz, lEndLine, j, l, lEnd, lEndLine2: integer;
+  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
+begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
+  {$ENDIF}
 
-      inc(YIndex);
-      while (YIndex < LCnt) and
-            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
-             > StartNode.NestLvlStart)
-      do
-        inc(YIndex);
-      if YIndex = LCnt then
-        exit;
+  if not Assigned(FHighlighter) then exit;
+  FHighlighter.CurrentLines := Lines;
+  if EndLine < 0 then
+    EndLine := StartLine
+  else
+    // endline seems to be the first line after the change
+    EndLine := EndLine - 1;
+  lEndLine := EndLine;
 
-      NIndex := -1;
-      Result := SearchLine(YIndex, NIndex);
+  SetLength(lStartNestList, 0);
+  SetLength(lEndNestList, 0);
 
-      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
-        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
-    end;
+  FillNestList(lStartNestList, StartLine, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at Start:');
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-  begin
-    Result := -1;
-    y := aRow -1;
+  FillNestList(lEndNestList, EndLine + 1, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at End:');
+  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-    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
+  // delete all nodes in lEndNodeList which where active at StartLine
+  // to get the nodes which reach behind EndLine
+  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
+  for i := 0 to lMinAnz - 1 do begin
+    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
+    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
+    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
+    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
+    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
 
-    NodeList := HL.FoldNodeInfo[y];
-    NodeList.AddReference;
-    try
-      NodeList.ActionFilter := [sfaOpen];
-      i := 0;
-      repeat
-        TmpNode := NodeList[i];
+      // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
+      lEnd := FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0);
+      if lEnd >= 0 then lEndLine2 := ToPos(lEnd);
 
-        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;
+      for j := 0 to length(lEndNestList) - 2 do
+        lEndNestList[j] := lEndNestList[j + 1];
+      SetLength(lEndNestList, Length(lEndNestList) - 1);
+    end else begin
+      break
     end;
   end;
 
+  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
+    // deeper fold group than StartLine: fold group ends after EndLine
+    // find real EndLine (end line of first remaining fold node)
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   Remaining Nodes:');
+    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+    {$ENDIF}
+    // does position of first character change for remaining node?
+    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
+      // position of first character changed -> find endline
+      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
 
-  function IsFoldMoved( aRow: Integer ): integer;
-  var S : string;
-    i,n : integer;
-  begin
-    Result := -1;
-    n := -1;
+    // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
+    if lEndLine = 0 then
+      lEndLine := lEndLine2;
+  end;
 
-    S := Caret.LineText;
-    for i := 1 to Min(Length(S), Length(FPrevCaretText)) do
-    begin
-      if S[i] <> FPrevCaretText[i] then
-      begin
-        n := i;
-        break;
+  // check for changes of endline for node which are active at StartLine
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    if sfaOutline in FoldAction then begin
+      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
+      if l <> FEndLine[LineIndex] then begin
+        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
+        FEndLine[LineIndex] := l;
+        {$IFDEF SynEditMarkupFoldColoringDebug}
+        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+        {$ENDIF}
       end;
     end;
 
-    if n < 0 then exit;
+  // invalidate cache
+  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
+    FFirstCharacterColumn[i] := 0;
+    FEndLine[i] := 0;
+  end;
 
-    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);
+  if lEndLine > EndLine then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
+    {$ENDIF}
+    InvalidateSynLines(EndLine + 1 , lEndLine);
+  end;
+end;
+
+procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
+var
+  old: TSynEditStrings;
+begin
+  old := Lines;
+  if Assigned(old)
+  and (AValue <> old) then begin
+    // change:
+    // remove Changehandler
+    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+    FreeAndNil(FNestList);
+  end;
+  inherited SetLines(AValue);
+  if (AValue <> old) then begin
+    // change:
+    if Assigned(AValue) then begin
+      // set cache size
+      SetLength(FFirstCharacterColumn, AValue.Count);
+      SetLength(FEndLine, AValue.Count);
+      // add Changehandler
+      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
+      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+      if Assigned(FHighlighter) then begin
+        FNestList := TLazSynEditNestedFoldsList.Create(AValue, FHighlighter);
+        FNestList.ResetFilter;
+        FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+        FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
+        FNestList.IncludeOpeningOnLine := True; //False; //
+      end;
+    end else begin
+      // clear cache
+      SetLength(FFirstCharacterColumn, 0);
+      SetLength(FEndLine, 0);
     end;
+  end;
+end;
 
-  end;
+procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
+                                        aIndex, aCount: Integer);
 var
-  EndFoldLine,y : integer;
+  absCount,
+  idx, i: 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);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  idx := ToIdx(aIndex);
+  if aCount < 0 then begin
+    // lines deleted
+    absCount := Abs(aCount);
+    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
+      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
+      FEndLine[i] := FEndLine[i + absCount];
+    end;
   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}
+  SetLength(FFirstCharacterColumn, Sender.Count);
+  SetLength(FEndLine, Sender.Count);
+  if (aCount > 0) then begin
+    if idx >= 0 then begin
+      // lines added
+      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
+        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
+        FEndLine[i + aCount] := FEndLine[i];
+      end;
+      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end else begin
+      // first lines will be inserted
+      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end;
+  end;
 end;
 
-procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
-var Y : integer;
+procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
+  aIndex, aCount: Integer);
+var
+  newHighlighter: TSynCustomFoldHighlighter;
 begin
-  Y := Caret.LineBytePos.y;
-  if Y = FCaretY then exit;
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  if (aIndex <> -1)
+  or (aCount <> -1) then
+    exit;
 
-  FCaretY := Y;
-  FPrevCaretText := Caret.LineText;
-end;
+  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(newHighlighter)
+  and not (newHighlighter is TSynCustomFoldHighlighter) then
+    newHighlighter := nil;
 
+  if (newHighlighter = FHighlighter) then
+    exit;
 
+  FHighlighter := newHighlighter;
 
+  FreeAndNil(FNestList);
+  if Assigned(FHighlighter) then begin
+    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
+    FNestList.ResetFilter;
+    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
+    FNestList.IncludeOpeningOnLine := True; //False; //
+  end;
+end;
+
 end.
 

Pascal Riekenberg

2016-09-12 12:36

reporter  

syneditmarkupfoldcoloring.pas_v10.patch (38,698 bytes)
Index: syneditmarkupfoldcoloring.pas
===================================================================
--- syneditmarkupfoldcoloring.pas	(revision 52956)
+++ syneditmarkupfoldcoloring.pas	(working copy)
@@ -50,12 +50,14 @@
 unit SynEditMarkupFoldColoring;
 
 {$mode objfpc}{$H+}
+{$ define SynEditMarkupFoldColoringDebug}
 
 interface
 
 uses
   Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
-  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
+  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
+  LazSynEditText;
 
 type
 
@@ -66,37 +68,39 @@
     Border  : Boolean;
     Ignore  : Boolean; //no color no line
     SrcNode : TSynFoldNodeInfo;
-    LevelBefore, LevelAfter : integer;//needed by non nest nodes
+    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
   end;
 
   TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
   TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
-
   { TSynEditMarkupFoldColors }
 
   TSynEditMarkupFoldColors = class(TSynEditMarkup)
   private
+    function GetFirstCharacterColumn(index: Integer): Byte;
+  private
+    FHighlighter: TSynCustomFoldHighlighter;
     FNestList: TLazSynEditNestedFoldsList;
+
+    // cache
+    FFirstCharacterColumn: Array of Byte;
+    FEndLine: Array of Integer;
+
     FDefaultGroup: integer;
-     // Physical Position
-    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
+    FFoldColorInfos: TMarkupFoldColorInfos;
     Colors : array of TColor;
-
-    {%region invalidating}
-    CurrentY : integer;  //??
-    FCaretY : integer;    // flag identify for refresh begin______
-    FPrevCaretText : string;  // flag identify for refresh begin______
-    {%endregion}
-
+    FPreparedRow: integer;
+    FLastNode: TSynFoldNodeInfo;
     procedure DoMarkupParentFoldAtRow(aRow: Integer);
     procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
-
-    function GetFoldHighLighter: TSynCustomFoldHighlighter;
     procedure SetDefaultGroup(AValue: integer);
+    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
   protected
     // Notifications about Changes to the text
     procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
-    procedure DoCaretChanged(Sender: TObject); override;
+    procedure SetLines(const AValue: TSynEditStrings); override;
+    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
+    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
   public
     constructor Create(ASynEdit : TSynEditBase);
     destructor Destroy; override;
@@ -115,36 +119,21 @@
 
 implementation
 uses
-  SynEdit,SynEditTypes, SynEditMiscProcs;
+  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
+{$IFDEF SynEditMarkupFoldColoringDebug}
+  , SynHighlighterPas
+{$ENDIF}
+  ;
 
-  {%region Sorting FoldInfo -fold}
-  function CompareFI(Item1, Item2: Pointer): Integer;
-  begin
-    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
-    if result = 0 then
-        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
-    if result = 0 then
-        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
-          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
+{$IFDEF SynEditMarkupFoldColoringDebug}
+function FoldTypeToStr(p_FoldType: Pointer): String;
+begin
+  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
+  while length(Result) < 17 do Result := Result + ' ';
+end;
+{$ENDIF}
 
-    SetLength(result, Length(a));
-    for i := 0 to Pred(l.Count) do
-      result[i] := PMarkupFoldColorInfo(l[i])^;
-     l.Free;
-  end;
-  {%endregion}
-
 { TSynEditMarkupFoldColors }
 
 constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
@@ -151,23 +140,36 @@
 begin
   inherited Create(ASynEdit);
 
-  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
+  if Assigned(Lines) then begin
+    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
+    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+    SetLength(FFirstCharacterColumn, Lines.Count);
+    SetLength(FEndLine, Lines.Count);
+  end;
+
+  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(FHighlighter)
+  and not (FHighlighter  is TSynCustomFoldHighlighter) then
+    FHighlighter := nil;
+
+  FDefaultGroup := 0;
+
+  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
   FNestList.ResetFilter;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
-  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
-  FNestList.IncludeOpeningOnLine := True; //False; //
+  FNestList.FoldGroup := FDefaultGroup;
+  FNestList.FoldFlags :=  [sfbIncludeDisabled];
+  FNestList.IncludeOpeningOnLine := True;
 
   MarkupInfo.Foreground := clGreen;
-  MarkupInfo.Background := clNone; //clFuchsia;
+  MarkupInfo.Background := clNone;
   MarkupInfo.Style := [];
   MarkupInfo.StyleMask := [];
-  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
+  MarkupInfo.FrameEdges:= sfeLeft;
 
   SetLength(Colors, 5);
   Colors[0] := clRed;
   Colors[1] := $000098F7; //orange
   Colors[2] := $0022CC40; //green
-  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
   Colors[3] := $00FF682A; //blue
   Colors[4] := $00CF00C4; //purple
 end;
@@ -174,6 +176,10 @@
 
 destructor TSynEditMarkupFoldColors.Destroy;
 begin
+  if Assigned(Lines) then begin
+    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+  end;
   FreeAndNil(FNestList);
   inherited Destroy;
 end;
@@ -182,23 +188,27 @@
   const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
   const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
 var
-  i,x2both : integer;
+  i, x2both: integer;
 begin
   Result := nil;
-  if (CurrentY = aRow) then begin
+  if not Assigned(FHighlighter) then exit;
+  if (FPreparedRow = aRow) then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
+    {$ENDIF}
 
-    x2both := -3; //flag
-    for i := 0 to length(FHighlights)-1 do
-      with FHighlights[i] do
+    x2both := -3;
+    for i := 0 to length(FFoldColorInfos)-1 do
+      with FFoldColorInfos[i] do
         if not Ignore
         and (X < X2)
         and (ColorIdx >= 0)
         and (aStartCol.Logical >= x)
-        and (aStartCol.Logical < X2) then
-        begin
-          //MarkupInfo.FrameColor:= clGreen; //debug
-          if x2both = -3 then //first call flag
-          begin
+        and (aStartCol.Logical < X2) then begin
+          {$IFDEF SynEditMarkupFoldColoringDebug}
+          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
+          {$ENDIF}
+          if x2both = -3 then begin //first call flag
             MarkupInfo.FrameColor:= clNone;
             MarkupInfo.Foreground:= clNone;
             MarkupInfo.Background:= clNone;
@@ -209,25 +219,14 @@
           Result := MarkupInfo;
           x2both := max(x2both, x2);
           MarkupInfo.SetFrameBoundsLog(x, x2both);
-          if Border then
-          begin
+          if Border then begin
             MarkupInfo.FrameColor:= Colors[ColorIdx];
-            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
-          end
-          else
+            MarkupInfo.FrameEdges:= sfeLeft;
+          end else begin
+            MarkupInfo.FrameColor:= clNone;
+            MarkupInfo.FrameEdges:= sfeNone;
             MarkupInfo.Foreground := Colors[ColorIdx];
-
-          //MarkupInfo.FrameEdges:= sfeAround; //debug
-
-          {//2nd debug
-          if x > x2 then
-          begin
-            MarkupInfo.Background:= clYellow;
-            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
-            MarkupInfo.FrameColor:= clBlue; //debug
-          end;}
-
-          //break;
+          end;
         end;
   end;
 end;
@@ -237,36 +236,82 @@
   const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
 var i : integer;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
+  {$ENDIF}
+  if not Assigned(FHighlighter)
+  or (FPreparedRow <> aRow) then
+    exit;
+
   ANextLog := -1;
   ANextPhys := -1;
-  if (CurrentY = aRow)  then
-  for i := 0 to length(FHighlights)-1  do
-    with FHighlights[i] do
-    begin
-      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
-        //continue;
-      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
-      begin
-        ANextLog := FHighlights[i].X;
+  for i := 0 to length(FFoldColorInfos)-1  do
+    with FFoldColorInfos[i] do begin
+      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
+        ANextLog := FFoldColorInfos[i].X;
         break;
       end;
     end;
 end;
 
+function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
+var
+  l: String;
+  p: Integer;
+begin
+  l := SynEdit.Lines[index];
+  p := 1;
+  while not (l[p] in [#13, #10, #0])
+  and (l[p] = #32) do inc(p);
+  if p > 255 then p := 255;
+  Result := p;
+end;
+
 procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
 var
-  i,lvl,z : integer; //iterate parents fold
+  i,lvl,z: integer;
 
   procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
+  var
+    p, s, l: integer;
   begin
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    // get column of first character in row
+    s := Length(FFirstCharacterColumn);
+    if (s <= ANode.LineIndex)
+    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
+      p := FirstCharacterColumn[ANode.LineIndex];
+      if s > ANode.LineIndex then begin
+        FFirstCharacterColumn[ANode.LineIndex] := p;
+      end else begin
+        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    //DebugLn('  FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), FFirstCharacterColumn[ANode.LineIndex]]);
+    s := Length(FEndLine);
+    if (s <= ANode.LineIndex)
+    or (FEndLine[ANode.LineIndex] = 0) then begin
+      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
+      if s > ANode.LineIndex then begin
+        FEndLine[ANode.LineIndex] := l;
+      end else begin
+        DebugLn('!!! FEndLine-Array too small !!!');
+      end;
+      //DebugLn('  Find Endline: %d: %d', [ToPos(ANode.LineIndex), l]);
+    end;
+    //DebugLn('  EndLine: %d: %d', [ToPos(ANode.LineIndex), FEndLine[ANode.LineIndex]]);
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z+1);
+    with FFoldColorInfos[z] do begin
+
       SrcNode:= ANode; //needed by close node
-      Border := ANode.LineIndex + 1 <> aRow;
-      X  := ANode.LogXStart + 1;
-      Y  := aRow;//ANode.LineIndex + 1;
-      X2 := X+1; //ANode.LogXEnd + 1;
+      Border := ToPos(ANode.LineIndex) <> aRow;
+      if s <= ANode.LineIndex then
+        X  := p
+      else
+        X  := FFirstCharacterColumn[ANode.LineIndex];
+      Y  := aRow;
+      X2 := X + 1;
       Ignore := False;
 
       if Border and (sfaOutlineNoLine in ANode.FoldAction) then
@@ -273,7 +318,9 @@
         Ignore := True;
       if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
         Ignore := True;
-        ColorIdx := lvl mod (length(Colors))
+      Level := lvl;
+      ColorIdx := Max(0, lvl) mod (length(Colors));
+      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
     end;
   end;
 
@@ -281,60 +328,62 @@
   y, lvlB,lvlA: Integer;
   TmpNode: TSynFoldNodeInfo;
   NestCount : integer;
-  procedure Later(var J:integer);
-  begin
-    inc(J);
-  end;
-  function Allowed(J: integer):boolean;
-  begin
-    result := J < NestCount;
-  end;
 
 begin
-  y := aRow-1;
+  y := ToIdx(aRow);
   FNestList.Line := y;
   NestCount := FNestList.Count;
+  FHighlighter.CurrentLines := Lines;
 
   lvl := 0;
   i := 0;
-  while Allowed(i) do
-  begin
-
+  while i < NestCount do begin
     TmpNode := FNestList.HLNode[i];
-    //find till valid
-    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
-    begin
-      Later(i);
-      TmpNode := FNestList.HLNode[i];
-    end;
+    if (sfaOutline in TmpNode.FoldAction)
+    and not (sfaInvalid in TmpNode.FoldAction) then
+      //avoid bug of IncludeOpeningOnLine := False;
+      if (sfaOpen in TmpNode.FoldAction)
+      and (TmpNode.LineIndex + 1 = aRow) then begin
+        {do nothing here}
+      end else begin
+        lvlB := lvl;
 
-    if (sfaOutline in TmpNode.FoldAction ) then
-    //avoid bug of IncludeOpeningOnLine := False;
-    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
-    begin {do nothing here} end
-    else
-    begin
-      lvlB := lvl;
+        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
+          inc(lvl)
+        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+          dec(lvl);
+        //if (FLastNode.LineIndex >= 0)
+        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+        //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+        // inc(lvl);
 
-      if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-        inc(lvl);
-      if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
-        dec(lvl);
+        AddVerticalLine(TmpNode);
 
-      AddVerticalLine(TmpNode);
-      if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-        inc(lvl);
+        //if (z > 0)
+        //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then begin
+        //  // if child is on same x-pos keep level
+        //  if sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction then begin
+        //    lvl := FFoldColorInfos[z - 1].Level;
+        //    FFoldColorInfos[z].Level := lvl;
+        //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+        //  end;
+        //end;
 
-      lvlA := lvl;
+        if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+        {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
+          inc(lvl);
 
-      with FHighlights[z] do begin
-        LevelBefore := lvlB;
-        LevelAfter  := lvlA;
+        if sfaOpen in TmpNode.FoldAction then
+          FLastNode := TmpNode;
+
+        lvlA := lvl;
+
+        with FFoldColorInfos[z] do begin
+          LevelBefore := lvlB;
+          LevelAfter  := lvlA;
+        end;
       end;
-    end;
-
-    Later(i);
-    //break; //debug
+    inc(i);
   end;
 end;
 
@@ -345,33 +394,29 @@
   procedure AddHighlight( ANode: TSynFoldNodeInfo );
   var x,j : integer;
   begin
-        //don't replace; don't add when already found
     x  := ANode.LogXStart + 1;
-
     if ANode.LogXStart < ANode.LogXEnd then
-    for j := 0 to Pred(length(FHighlights)) do
-      if (FHighlights[j].X = x)
-      and (FHighlights[j].Border)
-      and (FHighlights[j].SrcNode.FoldType = ANode.FoldType )
-      and (FHighlights[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
+    for j := 0 to Pred(length(FFoldColorInfos)) do
+      if (FFoldColorInfos[j].X = x)
+      and (FFoldColorInfos[j].Border)
+      and (FFoldColorInfos[j].SrcNode.FoldType = ANode.FoldType )
+      and (FFoldColorInfos[j].SrcNode.FoldLvlEnd = ANode.FoldLvlStart )
       then begin
-       FHighlights[j].X2 := ANode.LogXEnd+1 ;//exit; //
-       FHighlights[j].Border := False
-
+       FFoldColorInfos[j].X2 := ANode.LogXEnd + 1;
+       FFoldColorInfos[j].Border := False
       end;
 
-    //exit; //debug
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z + 1);
+    with FFoldColorInfos[z] do begin
       Border := False;
       SrcNode:= ANode; //needed by close node
       Y  := ANode.LineIndex + 1;
       X  := ANode.LogXStart + 1;
       X2 := ANode.LogXEnd + 1;
-      //ColorIdx := lvl;
+      Level := lvl;
       if not (sfaOutlineNocolor in ANode.FoldAction) then
-         ColorIdx := lvl mod (length(Colors))
+         ColorIdx := Max(0, lvl) mod (length(Colors))
       else
          ColorIdx := -1;
     end;
@@ -378,91 +423,105 @@
   end;
 
 var
-  y,i,j,lvlB,lvlA : integer;
-  HL: TSynCustomFoldHighlighter;
+  LineIdx,i,j,lvlB,lvlA : integer;
   NodeList: TLazSynFoldNodeInfoList;
   TmpNode: TSynFoldNodeInfo;
-  Found : boolean;
+  Found: boolean;
 begin
-  y := aRow -1;
+  LineIdx := ToIdx(aRow);
 
-  HL := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-  HL.CurrentLines := Lines;
-  HL.FoldNodeInfo[y].ClearFilter; // only needed once, in case the line was already used
+  FHighlighter.CurrentLines := Lines;
+  FHighlighter.FoldNodeInfo[LineIdx].ClearFilter; // only needed once, in case the line was already used
 
-  NodeList := HL.FoldNodeInfo[y];
+  NodeList := FHighlighter.FoldNodeInfo[LineIdx];
   NodeList.AddReference;
   try
     NodeList.ActionFilter := [sfaOutline];
-    //NodeList.FoldFlags:= [sfbIncludeDisabled];
     lvl := 0;
-    J := Length(FHighlights)-1;
+    J := Length(FFoldColorInfos) - 1;
     if J >=0 then
-      lvl := max(0,FHighlights[J].LevelAfter);
+      lvl := max(0,FFoldColorInfos[J].LevelAfter);
     i := 0;
     repeat
       TmpNode := NodeList[i];
 
-      //find till valid
-      while (sfaInvalid in TmpNode.FoldAction) and (i + 1 < NodeList.Count) do
-      begin
-        inc(i);
-        TmpNode := NodeList[i];
-      end;
-      if not (sfaInvalid in TmpNode.FoldAction) and (sfaOutline in TmpNode.FoldAction) then begin
-        if sfaOpen in TmpNode.FoldAction then
-        begin
+      if not (sfaInvalid in TmpNode.FoldAction)
+      and (sfaOutline in TmpNode.FoldAction) then begin
+        if sfaOpen in TmpNode.FoldAction then begin
           lvlB := lvl;
 
           if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
-            inc(lvl);
-          if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+            inc(lvl)
+          else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
             dec(lvl);
+          //if (FLastNode.LineIndex >= 0)
+          //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+          //and (FLastNode.LineIndex < TmpNode.LineIndex) then
+          // inc(lvl);
 
           AddHighlight(TmpNode);
-          if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
+
+          //if (z > 0)
+          //and (FFoldColorInfos[z].X = FFoldColorInfos[z - 1].X) then
+          //begin
+          //  // if child is on same x-pos keep level
+          //  if (sfaClose in FFoldColorInfos[z].SrcNode.FoldAction)
+          //  or (sfaOutlineMergeLevelOnWrongCol in FFoldColorInfos[z].SrcNode.FoldAction) then begin
+          //    lvl := FFoldColorInfos[z - 1].Level;
+          //    FFoldColorInfos[z].Level := lvl;
+          //    FFoldColorInfos[z].ColorIdx := Max(0, lvl) mod (length(Colors));
+          //  end;
+          //end;
+
+          if not (sfaOutlineKeepLevel in TmpNode.FoldAction)
+          {and not (sfaOutlineKeepLevelOnSameLine in TmpNode.FoldAction)} then
             inc(lvl);
+
           lvlA := lvl;
 
-          with FHighlights[z] do begin
+          if sfaOpen in TmpNode.FoldAction then
+            FLastNode := TmpNode;
+
+          with FFoldColorInfos[z] do begin
             LevelBefore := lvlB;
             LevelAfter  := lvlA;
           end;
-
-        end
-        else
-        if sfaClose in TmpNode.FoldAction then
-        begin
+        end else if sfaClose in TmpNode.FoldAction then begin
           Found := False;
-          for j := Length(FHighlights)-1 downto 0 do begin
-            with FHighlights[j].SrcNode do begin
-              if  (FoldType = TmpNode.FoldType) and
-                (FoldGroup = TmpNode.FoldGroup) and
-                (sfaOpen in FoldAction) and
-                // (FoldLvlEnd = TmpNode.FoldLvlStart)
-                (NestLvlEnd = TmpNode.NestLvlStart)
-
-                then begin
-                  lvl := FHighlights[j].ColorIdx;
-                  lvlB := FHighlights[j].LevelBefore;
-                  Found := True;
-                  break;
-                end;
+          for j := Length(FFoldColorInfos)-1 downto 0 do begin
+            with FFoldColorInfos[j].SrcNode do begin
+              if (FoldType = TmpNode.FoldType)
+              and (FoldGroup = TmpNode.FoldGroup)
+              and (sfaOpen in FoldAction)
+              and (NestLvlEnd = TmpNode.NestLvlStart) then begin
+                lvlB := lvl;
+                lvl := FFoldColorInfos[j].Level;
+                lvlA := FFoldColorInfos[j].LevelAfter;
+                FLastNode := TmpNode;
+                Found := True;
+                break;
+              end;
             end;
           end;
           if Found then begin
             AddHighlight(TmpNode);
-            lvl := lvlB;
+            with FFoldColorInfos[z] do begin
+              LevelBefore := lvlB;
+              LevelAfter  := lvlA;
+            end;
+            // if found opening position is behind closing position:
+            // delete this as it does not have to be drawn
+            if FFoldColorInfos[j].X > FFoldColorInfos[z].X then begin
+              for j := j to z - 1 do begin
+                FFoldColorInfos[j] := FFoldColorInfos[j+1];
+              end;
+              SetLength(FFoldColorInfos, z);
+            end;
           end;
-
-          //if not( sfaOutlineKeepLevel in TmpNode.FoldAction) then
-            //inc(lvl);
         end;
       end;
-
       inc(i);
     until i >= NodeList.Count;
-
   finally
     NodeList.ReleaseReference;
   end;
@@ -469,197 +528,295 @@
 end;
 
 procedure TSynEditMarkupFoldColors.PrepareMarkupForRow(aRow: Integer);
+var
+  i, LastX, j: Integer;
 begin
-  CurrentY := aRow;
-  SetLength(FHighlights,0); //reset needed to prevent using of invalid area
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('PrepareMarkupForRow %d', [aRow]);
+  {$ENDIF}
+  if not Assigned(FHighlighter) then exit;
+  FPreparedRow := aRow;
+  SetLength(FFoldColorInfos,0); //reset needed to prevent using of invalid area
 
   if not (TCustomSynEdit(self.SynEdit).Highlighter is TSynCustomFoldHighlighter) then
     exit;
 
-  //DoMarkupFoldAtRow(aRow);
+  // invalidate fLastNode
+  FLastNode.LineIndex := -1;
+
   DoMarkupParentFoldAtRow(aRow);
   DoMarkupParentCloseFoldAtRow(aRow);
-  //DoMarkupRangeFoldAtRow(aRow);
 
-  FHighlights := SortLeftMostFI(FHighlights);
+  // delete parents with bigger x
+  // to keep out mis indented blocks
+  LastX := MaxInt;
+  for i := length(FFoldColorInfos) - 1 downto 0 do begin
+    if FFoldColorInfos[i].X > LastX then begin
+      for j := i to length(FFoldColorInfos) - 2 do begin
+        FFoldColorInfos[j] := FFoldColorInfos[j + 1];
+      end;
+      SetLength(FFoldColorInfos, length(FFoldColorInfos) - 1);
+    end;
+    LastX := FFoldColorInfos[i].X;
+  end;
 end;
 
 procedure TSynEditMarkupFoldColors.EndMarkup;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('EndMarkup');
+  {$ENDIF}
   inherited EndMarkup;
+  if not Assigned(FHighlighter) then exit;
   FNestList.Clear; // for next markup start
 end;
 
-function TSynEditMarkupFoldColors.GetFoldHighLighter: TSynCustomFoldHighlighter;
-begin
-  result := TCustomSynEdit(self.SynEdit).Highlighter as TSynCustomFoldHighlighter;
-end;
-
 procedure TSynEditMarkupFoldColors.SetDefaultGroup(AValue: integer);
 begin
   if FDefaultGroup = AValue then Exit;
   FDefaultGroup := AValue;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+  FNestList.FoldGroup := FDefaultGroup;
 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;
+  procedure FillNestList(var pList: TSynFoldNodeInfos; pLine: Integer; pNestList: TLazSynEditNestedFoldsList);
   var
-    y,i,LCnt : integer;
-    HL: TSynCustomFoldHighlighter;
-    NodeList: TLazSynFoldNodeInfoList;
-    TmpNode, CloseNode: TSynFoldNodeInfo;
+    lCount, lLineIdx, i, lAnz: Integer;
+    lNode: TSynFoldNodeInfo;
+  begin
+    lLineIdx := ToIdx(pLine);
+    pNestList.Line := lLineIdx;
+    lCount := pNestList.Count;
+    SetLength(pList, lCount);
+    lAnz := 0;
+    for i := 0 to lCount - 1 do begin
+      lNode := pNestList.HLNode[i];
+      if (sfaInvalid in lNode.FoldAction)
+      or (
+        (sfaOpen in lNode.FoldAction)
+        and (lNode.LineIndex = lLineIdx)
+      ) then
+        Continue;
 
-    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;
+      pList[i] := lNode;
+      inc(lAnz);
+    end;
+    SetLength(pList, lAnz);
+  end;
 
-    begin
-      Result := SearchLine(YIndex, NIndex);
-      if not (sfaInvalid in Result.FoldAction) then
-        exit;
+var
+  i, lMinAnz, lEndLine, j, l, lEnd, lEndLine2: integer;
+  lStartNestList, lEndNestList: array of TSynFoldNodeInfo;
+begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('DoTextChanged %d-%d: %d', [StartLine, EndLine, ACountDiff]);
+  {$ENDIF}
 
-      inc(YIndex);
-      while (YIndex < LCnt) and
-            (HL.FoldBlockMinLevel(YIndex, StartNode.FoldGroup, [sfbIncludeDisabled])
-             > StartNode.NestLvlStart)
-      do
-        inc(YIndex);
-      if YIndex = LCnt then
-        exit;
+  if not Assigned(FHighlighter) then exit;
+  FHighlighter.CurrentLines := Lines;
+  if EndLine < 0 then
+    EndLine := StartLine
+  else
+    // endline seems to be the first line after the change
+    EndLine := EndLine - 1;
+  lEndLine := EndLine;
 
-      NIndex := -1;
-      Result := SearchLine(YIndex, NIndex);
+  SetLength(lStartNestList, 0);
+  SetLength(lEndNestList, 0);
 
-      if (Result.LogXEnd = 0) or (sfaLastLineClose in Result.FoldAction) then
-        Result.FoldAction := [sfaInvalid]; // LastLine closed Node(maybe force-closed?)
-    end;
+  FillNestList(lStartNestList, StartLine, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at Start:');
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-  begin
-    Result := -1;
-    y := aRow -1;
+  FillNestList(lEndNestList, EndLine + 1, FNestList);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   Nodes at End:');
+  for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+    DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+  {$ENDIF}
 
-    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
+  // delete all nodes in lEndNodeList which where active at StartLine
+  // to get the nodes which reach behind EndLine
+  lMinAnz := Min(length(lStartNestList), length(lEndNestList));
+  for i := 0 to lMinAnz - 1 do begin
+    if (lStartNestList[i].FoldGroup = lEndNestList[0].FoldGroup)
+    and (lStartNestList[i].FoldType = lEndNestList[0].FoldType)
+    and (lStartNestList[i].LineIndex = lEndNestList[0].LineIndex)
+    and (lStartNestList[i].LogXStart = lEndNestList[0].LogXStart)
+    and (lStartNestList[i].LogXEnd = lEndNestList[0].LogXEnd) then begin
 
-    NodeList := HL.FoldNodeInfo[y];
-    NodeList.AddReference;
-    try
-      NodeList.ActionFilter := [sfaOpen];
-      i := 0;
-      repeat
-        TmpNode := NodeList[i];
+      // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
+      lEnd := FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0);
+      if lEnd >= 0 then lEndLine2 := ToPos(lEnd);
 
-        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;
+      for j := 0 to length(lEndNestList) - 2 do
+        lEndNestList[j] := lEndNestList[j + 1];
+      SetLength(lEndNestList, Length(lEndNestList) - 1);
+    end else begin
+      break
     end;
   end;
 
+  if (length(lEndNestList) > 0) then with lEndNestList[0] do begin
+    // deeper fold group than StartLine: fold group ends after EndLine
+    // find real EndLine (end line of first remaining fold node)
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   Remaining Nodes:');
+    for i := 0 to length(lEndNestList) - 1 do with lEndNestList[i] do
+      DebugLn('      x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d (cache) -> %d (HL)', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+    {$ENDIF}
+    // does position of first character change for remaining node?
+    if FirstCharacterColumn[lEndNestList[0].LineIndex] <> FFirstCharacterColumn[lEndNestList[0].LineIndex] then
+      // position of first character changed -> find endline
+      lEndLine := ToPos(FHighlighter.FoldEndLine(lEndNestList[0].LineIndex, 0));
 
-  function IsFoldMoved( aRow: Integer ): integer;
-  var S : string;
-    i,n : integer;
-  begin
-    Result := -1;
-    n := -1;
+    // ToDo: Fix workaround for Highlighter.EndLine() not working with sfaInvalid
+    if lEndLine = 0 then
+      lEndLine := lEndLine2;
+  end;
 
-    S := Caret.LineText;
-    for i := 1 to Min(Length(S), Length(FPrevCaretText)) do
-    begin
-      if S[i] <> FPrevCaretText[i] then
-      begin
-        n := i;
-        break;
+  // check for changes of endline for node which are active at StartLine
+  for i := 0 to length(lStartNestList) - 1 do with lStartNestList[i] do
+    if sfaOutline in FoldAction then begin
+      l := ToPos(FHighlighter.FoldEndLine(LineIndex, 0));
+      if l <> FEndLine[LineIndex] then begin
+        lEndLine := Max(lEndLine, Max(l, FEndLine[LineIndex]));
+        FEndLine[LineIndex] := l;
+        {$IFDEF SynEditMarkupFoldColoringDebug}
+        DebugLn('   ** x=%.03d l=%.5d %s %s %s %s lvl=%d/%d endline=%d -> %d', [LogXStart, ToPos(LineIndex), IfThen(sfaOpen in FoldAction, 'O', IfThen(sfaClose in FoldAction, 'C', ' ')), IfThen(sfaOutlineKeepLevel{OnSameLine} in FoldAction ,'K', ' '), IfThen(sfaOutlineForceIndent in FoldAction, '+', IfThen(sfaOutlineMergeParent in FoldAction, '-', ' ')) ,FoldTypeToStr(FoldType), FoldLvlStart, FoldLvlEnd, FEndLine[LineIndex], ToPos(FHighlighter.FoldEndLine(LineIndex, 0))]);
+        {$ENDIF}
       end;
     end;
 
-    if n < 0 then exit;
+  // invalidate cache
+  for i := ToIdx(StartLine) to ToIdx(EndLine) do begin
+    FFirstCharacterColumn[i] := 0;
+    FEndLine[i] := 0;
+  end;
 
-    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);
+  if lEndLine > EndLine then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    DebugLn('   InvalidateSynLines(%d, %d)', [EndLine + 1, lEndLine]);
+    {$ENDIF}
+    InvalidateSynLines(EndLine + 1 , lEndLine);
+  end;
+end;
+
+procedure TSynEditMarkupFoldColors.SetLines(const AValue: TSynEditStrings);
+var
+  old: TSynEditStrings;
+begin
+  old := Lines;
+  if Assigned(old)
+  and (AValue <> old) then begin
+    // change:
+    // remove Changehandler
+    old.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    old.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+    FreeAndNil(FNestList);
+  end;
+  inherited SetLines(AValue);
+  if (AValue <> old) then begin
+    // change:
+    if Assigned(AValue) then begin
+      // set cache size
+      SetLength(FFirstCharacterColumn, AValue.Count);
+      SetLength(FEndLine, AValue.Count);
+      // add Changehandler
+      AValue.AddChangeHandler(senrLineCount, @LinesChanged);
+      AValue.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+      if Assigned(FHighlighter) then begin
+        FNestList := TLazSynEditNestedFoldsList.Create(AValue, FHighlighter);
+        FNestList.ResetFilter;
+        FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+        FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
+        FNestList.IncludeOpeningOnLine := True; //False; //
+      end;
+    end else begin
+      // clear cache
+      SetLength(FFirstCharacterColumn, 0);
+      SetLength(FEndLine, 0);
     end;
+  end;
+end;
 
-  end;
+procedure TSynEditMarkupFoldColors.LinesChanged(Sender: TSynEditStrings;
+                                        aIndex, aCount: Integer);
 var
-  EndFoldLine,y : integer;
+  absCount,
+  idx, i: 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);
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   LinesChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  idx := ToIdx(aIndex);
+  if aCount < 0 then begin
+    // lines deleted
+    absCount := Abs(aCount);
+    for i := idx to Length(FFirstCharacterColumn) - 1 - absCount do begin
+      FFirstCharacterColumn[i] := FFirstCharacterColumn[i + absCount];
+      FEndLine[i] := FEndLine[i + absCount];
+    end;
   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}
+  SetLength(FFirstCharacterColumn, Sender.Count);
+  SetLength(FEndLine, Sender.Count);
+  if (aCount > 0) then begin
+    if idx >= 0 then begin
+      // lines added
+      for i := Length(FFirstCharacterColumn) - 1 - aCount downto idx do begin
+        FFirstCharacterColumn[i + aCount] := FFirstCharacterColumn[i];
+        FEndLine[i + aCount] := FEndLine[i];
+      end;
+      for i := idx to Min(idx + aCount, Length(FFirstCharacterColumn) - 1) do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end else begin
+      // first lines will be inserted
+      for i := 0 to Length(FFirstCharacterColumn) - 1 do begin
+        FFirstCharacterColumn[i] := 0;
+        FEndLine[i] := 0;
+      end;
+    end;
+  end;
 end;
 
-procedure TSynEditMarkupFoldColors.DoCaretChanged(Sender: TObject);
-var Y : integer;
+procedure TSynEditMarkupFoldColors.HighlightChanged(Sender: TSynEditStrings;
+  aIndex, aCount: Integer);
+var
+  newHighlighter: TSynCustomFoldHighlighter;
 begin
-  Y := Caret.LineBytePos.y;
-  if Y = FCaretY then exit;
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  DebugLn('   HighlightChanged: aIndex=%d aCount=%d', [aIndex, aCount]);
+  {$ENDIF}
+  if (aIndex <> -1)
+  or (aCount <> -1) then
+    exit;
 
-  FCaretY := Y;
-  FPrevCaretText := Caret.LineText;
-end;
+  newHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(newHighlighter)
+  and not (newHighlighter is TSynCustomFoldHighlighter) then
+    newHighlighter := nil;
 
+  if (newHighlighter = FHighlighter) then
+    exit;
 
+  FHighlighter := newHighlighter;
 
+  FreeAndNil(FNestList);
+  if Assigned(FHighlighter) then begin
+    FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
+    FNestList.ResetFilter;
+    FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
+    FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
+    FNestList.IncludeOpeningOnLine := True; //False; //
+  end;
+end;
+
 end.
 

Pascal Riekenberg

2016-09-12 12:37

reporter   ~0094606

Updated patch due to AV for files with no highlighter.

Pascal Riekenberg

2016-09-16 13:22

reporter  

syneditmarkupfoldcoloring.pas_v11.patch (38,997 bytes)
Index: syneditmarkupfoldcoloring.pas
===================================================================
--- syneditmarkupfoldcoloring.pas	(revision 52981)
+++ syneditmarkupfoldcoloring.pas	(working copy)
@@ -50,12 +50,14 @@
 unit SynEditMarkupFoldColoring;
 
 {$mode objfpc}{$H+}
+{$ define SynEditMarkupFoldColoringDebug}
 
 interface
 
 uses
   Classes, SysUtils,Graphics, SynEditMarkup, SynEditMiscClasses, Controls,
-  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase;
+  LCLProc, SynEditFoldedView, SynEditHighlighter, SynEditHighlighterFoldBase,
+  LazSynEditText;
 
 type
 
@@ -66,37 +68,39 @@
     Border  : Boolean;
     Ignore  : Boolean; //no color no line
     SrcNode : TSynFoldNodeInfo;
-    LevelBefore, LevelAfter : integer;//needed by non nest nodes
+    LevelBefore, Level, LevelAfter : integer;//needed by non nest nodes
   end;
 
   TMarkupFoldColorInfos = array of TMarkupFoldColorInfo;
   TSynFoldNodeInfos     = array of TSynFoldNodeInfo; //for quick compare detection
-
   { TSynEditMarkupFoldColors }
 
   TSynEditMarkupFoldColors = class(TSynEditMarkup)
   private
+    function GetFirstCharacterColumn(index: Integer): Byte;
+  private
+    FHighlighter: TSynCustomFoldHighlighter;
     FNestList: TLazSynEditNestedFoldsList;
+
+    // cache
+    FFirstCharacterColumn: Array of Byte;
+    FEndLine: Array of Integer;
+
     FDefaultGroup: integer;
-     // Physical Position
-    FHighlights : TMarkupFoldColorInfos; //array of TMarkupFoldColorInfo;
+    FFoldColorInfos: TMarkupFoldColorInfos;
     Colors : array of TColor;
-
-    {%region invalidating}
-    CurrentY : integer;  //??
-    FCaretY : integer;    // flag identify for refresh begin______
-    FPrevCaretText : string;  // flag identify for refresh begin______
-    {%endregion}
-
+    FPreparedRow: integer;
+    FLastNode: TSynFoldNodeInfo;
     procedure DoMarkupParentFoldAtRow(aRow: Integer);
     procedure DoMarkupParentCloseFoldAtRow(aRow: Integer);
-
-    function GetFoldHighLighter: TSynCustomFoldHighlighter;
     procedure SetDefaultGroup(AValue: integer);
+    property FirstCharacterColumn[index: Integer]: Byte read GetFirstCharacterColumn;
   protected
     // Notifications about Changes to the text
     procedure DoTextChanged({%H-}StartLine, EndLine, {%H-}ACountDiff: Integer); override; // 1 based
-    procedure DoCaretChanged(Sender: TObject); override;
+    procedure SetLines(const AValue: TSynEditStrings); override;
+    procedure LinesChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
+    procedure HighlightChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
   public
     constructor Create(ASynEdit : TSynEditBase);
     destructor Destroy; override;
@@ -115,36 +119,21 @@
 
 implementation
 uses
-  SynEdit,SynEditTypes, SynEditMiscProcs;
+  SynEdit, SynEditTypes, SynEditMiscProcs, Dialogs, strutils
+{$IFDEF SynEditMarkupFoldColoringDebug}
+  , SynHighlighterPas
+{$ENDIF}
+  ;
 
-  {%region Sorting FoldInfo -fold}
-  function CompareFI(Item1, Item2: Pointer): Integer;
-  begin
-    result := PMarkupFoldColorInfo(Item1)^.X - PMarkupFoldColorInfo(Item2)^.X;
-    if result = 0 then
-        result := PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item2)^.X2;
-    if result = 0 then
-        result := (PMarkupFoldColorInfo(Item1)^.X2 - PMarkupFoldColorInfo(Item1)^.X)
-          - (PMarkupFoldColorInfo(Item2)^.X2 - 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);
+{$IFDEF SynEditMarkupFoldColoringDebug}
+function FoldTypeToStr(p_FoldType: Pointer): String;
+begin
+  WriteStr(Result, TPascalCodeFoldBlockType(PtrUInt(p_FoldType)));
+  while length(Result) < 17 do Result := Result + ' ';
+end;
+{$ENDIF}
 
-    SetLength(result, Length(a));
-    for i := 0 to Pred(l.Count) do
-      result[i] := PMarkupFoldColorInfo(l[i])^;
-     l.Free;
-  end;
-  {%endregion}
-
 { TSynEditMarkupFoldColors }
 
 constructor TSynEditMarkupFoldColors.Create(ASynEdit: TSynEditBase);
@@ -151,23 +140,36 @@
 begin
   inherited Create(ASynEdit);
 
-  FNestList := TLazSynEditNestedFoldsList.Create(Lines, GetFoldHighLighter);
+  if Assigned(Lines) then begin
+    Lines.AddChangeHandler(senrLineCount, @LinesChanged);
+    Lines.AddChangeHandler(senrHighlightChanged, @HighlightChanged);
+    SetLength(FFirstCharacterColumn, Lines.Count);
+    SetLength(FEndLine, Lines.Count);
+  end;
+
+  FHighlighter := TSynCustomFoldHighlighter(TCustomSynEdit(self.SynEdit).Highlighter);
+  if Assigned(FHighlighter)
+  and not (FHighlighter  is TSynCustomFoldHighlighter) then
+    FHighlighter := nil;
+
+  FDefaultGroup := 0;
+
+  FNestList := TLazSynEditNestedFoldsList.Create(Lines, FHighlighter);
   FNestList.ResetFilter;
-  FNestList.FoldGroup := FDefaultGroup;//1;//FOLDGROUP_PASCAL;
-  FNestList.FoldFlags :=  [sfbIncludeDisabled]; //[];//
-  FNestList.IncludeOpeningOnLine := True; //False; //
+  FNestList.FoldGroup := FDefaultGroup;
+  FNestList.FoldFlags :=  [sfbIncludeDisabled];
+  FNestList.IncludeOpeningOnLine := True;
 
   MarkupInfo.Foreground := clGreen;
-  MarkupInfo.Background := clNone; //clFuchsia;
+  MarkupInfo.Background := clNone;
   MarkupInfo.Style := [];
   MarkupInfo.StyleMask := [];
-  MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//sfeBottom;//
+  MarkupInfo.FrameEdges:= sfeLeft;
 
   SetLength(Colors, 5);
   Colors[0] := clRed;
   Colors[1] := $000098F7; //orange
   Colors[2] := $0022CC40; //green
-  //Colors[3] := $00D5D500; // $0098CC42; // $00D1D54A; // teal
   Colors[3] := $00FF682A; //blue
   Colors[4] := $00CF00C4; //purple
 end;
@@ -174,6 +176,10 @@
 
 destructor TSynEditMarkupFoldColors.Destroy;
 begin
+  if Assigned(Lines) then begin
+    Lines.RemoveChangeHandler(senrLineCount, @LinesChanged);
+    Lines.RemoveChangeHandler(senrHighlightChanged, @HighlightChanged);
+  end;
   FreeAndNil(FNestList);
   inherited Destroy;
 end;
@@ -182,23 +188,27 @@
   const aRow: Integer; const aStartCol: TLazSynDisplayTokenBound;
   const AnRtlInfo: TLazSynDisplayRtlInfo): TSynSelectedColor;
 var
-  i,x2both : integer;
+  i, x2both: integer;
 begin
   Result := nil;
-  if (CurrentY = aRow) then begin
+  if not Assigned(FHighlighter) then exit;
+  if (FPreparedRow = aRow) then begin
+    {$IFDEF SynEditMarkupFoldColoringDebug}
+    //DebugLn('   GetMarkupAttributeAtRowCol %d/%d', [aRow, aStartCol.Logical]);
+    {$ENDIF}
 
-    x2both := -3; //flag
-    for i := 0 to length(FHighlights)-1 do
-      with FHighlights[i] do
+    x2both := -3;
+    for i := 0 to length(FFoldColorInfos)-1 do
+      with FFoldColorInfos[i] do
         if not Ignore
         and (X < X2)
         and (ColorIdx >= 0)
         and (aStartCol.Logical >= x)
-        and (aStartCol.Logical < X2) then
-        begin
-          //MarkupInfo.FrameColor:= clGreen; //debug
-          if x2both = -3 then //first call flag
-          begin
+        and (aStartCol.Logical < X2) then begin
+          {$IFDEF SynEditMarkupFoldColoringDebug}
+          //DebugLn('      X=%d X2=%d Y=%d, C=%d B=%s I=%s', [X, X2, Y, ColorIdx, IfThen(Border, 'X', '-'), IfThen(Ignore, 'X', '-')]);
+          {$ENDIF}
+          if x2both = -3 then begin //first call flag
             MarkupInfo.FrameColor:= clNone;
             MarkupInfo.Foreground:= clNone;
             MarkupInfo.Background:= clNone;
@@ -209,25 +219,14 @@
           Result := MarkupInfo;
           x2both := max(x2both, x2);
           MarkupInfo.SetFrameBoundsLog(x, x2both);
-          if Border then
-          begin
+          if Border then begin
             MarkupInfo.FrameColor:= Colors[ColorIdx];
-            MarkupInfo.FrameEdges:= sfeLeft;//sfeAround;//
-          end
-          else
+            MarkupInfo.FrameEdges:= sfeLeft;
+          end else begin
+            MarkupInfo.FrameColor:= clNone;
+            MarkupInfo.FrameEdges:= sfeNone;
             MarkupInfo.Foreground := Colors[ColorIdx];
-
-          //MarkupInfo.FrameEdges:= sfeAround; //debug
-
-          {//2nd debug
-          if x > x2 then
-          begin
-            MarkupInfo.Background:= clYellow;
-            MarkupInfo.SetFrameBoundsLog(x-1, x2+20);
-            MarkupInfo.FrameColor:= clBlue; //debug
-          end;}
-
-          //break;
+          end;
         end;
   end;
 end;
@@ -237,36 +236,82 @@
   const AnRtlInfo: TLazSynDisplayRtlInfo; out ANextPhys, ANextLog: Integer);
 var i : integer;
 begin
+  {$IFDEF SynEditMarkupFoldColoringDebug}
+  //DebugLn('GetNextMarkupColAfterRowCol %d/%d', [aRow, aStartCol.Logical]);
+  {$ENDIF}
+  if not Assigned(FHighlighter)
+  or (FPreparedRow <> aRow) then
+    exit;
+
   ANextLog := -1;
   ANextPhys := -1;
-  if (CurrentY = aRow)  then
-  for i := 0 to length(FHighlights)-1  do
-    with FHighlights[i] do
-    begin
-      //if Ignore or (ColorIdx < 0) or (X >= X2) or (aStartCol.Logical >= x) or (aStartCol.Logical > X2) then
-        //continue;
-      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then
-      begin
-        ANextLog := FHighlights[i].X;
+  for i := 0 to length(FFoldColorInfos)-1  do
+    with FFoldColorInfos[i] do begin
+      if not Ignore and (ColorIdx >= 0) and (X < X2) and (aStartCol.Logical < x) and (aStartCol.Logical <= X2) then begin
+        ANextLog := FFoldColorInfos[i].X;
         break;
       end;
     end;
 end;
 
+function TSynEditMarkupFoldColors.GetFirstCharacterColumn(index: Integer): Byte;
+var
+  l: String;
+  p: Integer;
+begin
+  l := SynEdit.Lines[index];
+  p := 1;
+  while not (l[p] in [#13, #10, #0])
+  and (l[p] = #32) do inc(p);
+  if p > 255 then p := 255;
+  Result := p;
+end;
+
 procedure TSynEditMarkupFoldColors.DoMarkupParentFoldAtRow(aRow: Integer);
 var
-  i,lvl,z : integer; //iterate parents fold
+  i,lvl,z: integer;
 
   procedure AddVerticalLine( ANode: TSynFoldNodeInfo );
+  var
+    p, s, l: integer;
   begin
-    z := Length(FHighlights);
-    SetLength(FHighlights, z+1);
-    with FHighlights[z] do begin
+    // get column of first character in row
+    s := Length(FFirstCharacterColumn);
+    if (s <= ANode.LineIndex)
+    or (FFirstCharacterColumn[ANode.LineIndex] = 0) then begin
+      p := FirstCharacterColumn[ANode.LineIndex];
+      if s > ANode.LineIndex then begin
+        FFirstCharacterColumn[ANode.LineIndex] := p;
+      end else begin
+        DebugLn('!!! FFirstCharacterColumn-Array too small !!!');
+      end;
+      //DebugLn('  Find FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), p]);
+    end;
+    //DebugLn('  FirstCharacterColumn: %d: %d', [ToPos(ANode.LineIndex), FFirstCharacterColumn[ANode.LineIndex]]);
+    s := Length(FEndLine);
+    if (s <= ANode.LineIndex)
+    or (FEndLine[ANode.LineIndex] = 0) then begin
+      l := ToPos(FHighlighter.FoldEndLine(ANode.LineIndex, 0));
+      if s > ANode.LineIndex then begin
+        FEndLine[ANode.LineIndex] := l;
+      end else begin
+        DebugLn('!!! FEndLine-Array too small !!!');
+      end;
+      //DebugLn('  Find Endline: %d: %d', [ToPos(ANode.LineIndex), l]);
+    end;
+    //DebugLn('  EndLine: %d: %d', [ToPos(ANode.LineIndex), FEndLine[ANode.LineIndex]]);
+    z := Length(FFoldColorInfos);
+    SetLength(FFoldColorInfos, z+1);
+    with FFoldColorInfos[z] do begin
+
       SrcNode:= ANode; //needed by close node
-      Border := ANode.LineIndex + 1 <> aRow;
-      X  := ANode.LogXStart + 1;
-      Y  := aRow;//ANode.LineIndex + 1;
-      X2 := X+1; //ANode.LogXEnd + 1;
+      Border := ToPos(ANode.LineIndex) <> aRow;
+      if s <= ANode.LineIndex then
+        X  := p
+      else
+        X  := FFirstCharacterColumn[ANode.LineIndex];
+      Y  := aRow;
+      X2 := X + 1;
       Ignore := False;
 
       if Border and (sfaOutlineNoLine in ANode.FoldAction) then
@@ -273,7 +318,9 @@
         Ignore := True;
       if not Border and (sfaOutlineNoColor in ANode.FoldAction) then
         Ignore := True;
-        ColorIdx := lvl mod (length(Colors))
+      Level := lvl;
+      ColorIdx := Max(0, lvl) mod (length(Colors));
+      //DebugLn('  LogXStart=%d X=%d B=%s I=%s', [ANode.LogXStart, ANode.ColumnOfFirstCharInRow, IfThen(Border, 'X', '_'), IfThen(Ignore, 'X', '_')]);
     end;
   end;
 
@@ -281,60 +328,62 @@
   y, lvlB,lvlA: Integer;
   TmpNode: TSynFoldNodeInfo;
   NestCount : integer;
-  procedure Later(var J:integer);
-  begin
-    inc(J);
-  end;
-  function Allowed(J: integer):boolean;
-  begin
-    result := J < NestCount;
-  end;
 
 begin
-  y := aRow-1;
+  y := ToIdx(aRow);
   FNestList.Line := y;
   NestCount := FNestList.Count;
+  FHighlighter.CurrentLines := Lines;
 
   lvl := 0;
   i := 0;
-  while Allowed(i) do
-  begin
-
+  while i < NestCount do begin
     TmpNode := FNestList.HLNode[i];
-    //find till valid
-    while (sfaInvalid in TmpNode.FoldAction ) and Allowed(i+1) do //(i < FNestList.Count) do
-    begin
-      Later(i);
-      TmpNode := FNestList.HLNode[i];
-    end;
+    if (sfaOutline in TmpNode.FoldAction)
+    and not (sfaInvalid in TmpNode.FoldAction) then
+      //avoid bug of IncludeOpeningOnLine := False;
+      if (sfaOpen in TmpNode.FoldAction)
+      and (TmpNode.LineIndex + 1 = aRow) then begin
+        {do nothing here}
+      end else begin
+        lvlB := lvl;
 
-    if (sfaOutline in TmpNode.FoldAction ) then
-    //avoid bug of IncludeOpeningOnLine := False;
-    if (sfaOpen in TmpNode.FoldAction)  and (TmpNode.LineIndex + 1 = aRow) then
-    begin {do nothing here} end
-    else
-    begin
-      lvlB := lvl;
+        if ( sfaOutlineForceIndent in TmpNode.FoldAction) then
+          inc(lvl)
+        else if ( sfaOutlineMergeParent in TmpNode.FoldAction) then
+          dec(lvl);
+        //if (FLastNode.LineIndex >= 0)
+        //and (sfaOutlineKeepLevelOnSameLine in FLastNode.FoldAction)
+        //and (FLastNode.LineInde