unit TestMarkupIfDef;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, testregistry, TestBase, TestHighlightFoldBase, LCLProc,
  SynEdit, SynEditMarkupIfDef, SynHighlighterPas, SynEditHighlighterFoldBase,
  SynEditMiscClasses, SynEditFoldedView;

type

  TSynMarkupIfdefNodeTypeTest =
    (idnIfdef, idnElseIf, idnElse, idnEndIf, idnCommentedIfdef,
     idnMustBeNil, idnSkipTest);

  TPeerExpect = record
    PeerType:  TSynMarkupIfdefNodeTypeTest;
    PeerY, PeerX: Integer;
    NoAutoFree: Boolean;
  end;
  PPeerExpect = ^TPeerExpect;

const
  NodeTypeMap: array [TSynMarkupIfdefNodeType] of TSynMarkupIfdefNodeTypeTest =
    (idnIfdef, idnElseIf, idnElse, idnEndIf, idnCommentedIfdef);

type
  TNodeExpect = record
    ExpStart, ExpEnd, ExpEndLineOffs: Integer;
    ExpType:  TSynMarkupIfdefNodeTypeTest;
    ExpState:  TSynMarkupIfdefNodeStateEx;
    TestExpState: Boolean;
    OpenPeer, ClosePeer: TPeerExpect;
  end;

  { TTestMarkupIfDef }

  TTestMarkupIfDef = class(TTestBaseHighlighterFoldBase)
  private
    FTestTree: TSynMarkupHighIfDefLinesTree;
    FNodeStateResponses, FNodeStateRequests: TStringList;
    FOpenings: TLazSynEditNestedFoldsList;
    FUseNestedComments: Boolean;
    function TestTextEmpty: TStringArray;
    function TestTextNoIfDef: TStringArray;
    function TestTextIfDef: TStringArray;
    function TestText1: TStringArray;
    function TestText2: TStringArray;
    function TestText3: TStringArray;
    function TestText3a: TStringArray;
    function TestText4: TStringArray;
    function TestText5: TStringArray;
    function TestText6: TStringArray;
    function TestText7: TStringArray;
    function TestText8: TStringArray;
    function TestText9: TStringArray;
    function TestText10: TStringArray;
    function TestText11: TStringArray;
    function TestText11a: TStringArray;
    function TestText12: TStringArray;

    procedure CheckOpenCloseCount(AName: String; ALine: Integer;
      AExpOpenCnt, AExpCloseCnt: Integer);
    procedure CheckNodes(AName: String; ALine: Integer;
      AExp: array of TNodeExpect);
    procedure CheckNodesXY(AName: String; ALine: Integer;
      AExp: array of Integer; // [Start, end,  start, end, ....]
      AExpEndOffs: Integer);
    //procedure CheckPeer(AName: String; ALine, ACol: Integer;
    //  AType: TSynMarkupIfdefNodeTypeTest; ExpLine, ExpCol: Integer);

    function TesTNodeStateHandler(Sender: TObject; LinePos,
      XStartPos: Integer; CurrentState: TSynMarkupIfdefNodeStateEx): TSynMarkupIfdefNodeState;
  protected
    function CreateTheHighLighter: TSynCustomFoldHighlighter; override;
    //procedure SetUp; override;
    //procedure TearDown; override;
    procedure ReCreateEditForTreeTest(Lines: Array of String); reintroduce;
  published
    procedure TestIfDefTreeMoveOnEdit;
    procedure TestIfDefTreePeerConnect;
    procedure TestIfDefTreeNodeState;
  end;

implementation

function dbgs(AFlag: TSynMarkupIfdefNodeTypeTest): String; overload;
begin
  Result := '';
  WriteStr(Result, AFlag);
end;

function ExpP(PeerType: TSynMarkupIfdefNodeTypeTest; PeerY, PeerX: Integer): PPeerExpect;
begin
  New(Result);
  Result^.PeerType := PeerType;
  Result^.PeerY := PeerY;
  Result^.PeerX := PeerX;
  Result^.NoAutoFree := False;
end;

function EpIf(PeerY, PeerX: Integer): PPeerExpect;
begin
  Result := ExpP(idnIfdef, PeerY, PeerX);
end;
function EpElse(PeerY, PeerX: Integer): PPeerExpect;
begin
  Result := ExpP(idnElse, PeerY, PeerX);
end;
function EpElseIf(PeerY, PeerX: Integer): PPeerExpect;
begin
  Result := ExpP(idnElseIf, PeerY, PeerX);
end;
function EpEnd(PeerY, PeerX: Integer): PPeerExpect;
begin
  Result := ExpP(idnEndIf, PeerY, PeerX);
end;
function EpNil: PPeerExpect;
begin
  Result := ExpP(idnMustBeNil, -1, -1);
end;
function EpSkip: PPeerExpect;
begin
  Result := ExpP(idnSkipTest, -1, -1);
end;

function ExpN(ExpStart: Integer; ExpEnd: Integer;
  ExpEndLineOffs: Integer; // Default to check for same line end
  ExpType: TSynMarkupIfdefNodeTypeTest;
  ExpState: TSynMarkupIfdefNodeStateEx;
  Peer1: PPeerExpect = nil; Peer2: PPeerExpect = nil): TNodeExpect;
begin
  Result.ExpStart       := ExpStart;
  Result.ExpEnd         := ExpEnd;
  Result.ExpEndLineOffs := ExpEndLineOffs;
  Result.ExpType        := ExpType;
  if Peer1 = nil then Peer1 := ExpP(idnSkipTest, -1 , -1);
  if Peer2 = nil then Peer2 := ExpP(idnSkipTest, -1 , -1);
  Result.OpenPeer          := Peer1^;
  Result.ClosePeer          := Peer2^;
  Result.ExpState     := ExpState;
  Result.TestExpState := True;
  if not Peer1^.NoAutoFree then
    Dispose(Peer1);
  if not Peer2^.NoAutoFree then
    Dispose(Peer2);
end;

function ExpN(ExpStart, ExpEnd: Integer; ExpType: TSynMarkupIfdefNodeTypeTest;
  ExpState: TSynMarkupIfdefNodeStateEx;
  Peer1: PPeerExpect = nil; Peer2: PPeerExpect = nil): TNodeExpect;
begin
  Result := ExpN(ExpStart, ExpEnd, 0, ExpType, ExpState, Peer1, Peer2);
end;

function ExpN(ExpStart: Integer; ExpEnd: Integer = -1;
  ExpEndLineOffs: Integer = 0; // Default to check for same line end
  ExpType: TSynMarkupIfdefNodeTypeTest = idnSkipTest;
  Peer1: PPeerExpect = nil; Peer2: PPeerExpect = nil): TNodeExpect;
begin
  Result := ExpN(ExpStart, ExpEnd, ExpEndLineOffs, ExpType, idnUnknown, Peer1, Peer2);
  Result.TestExpState := False;
end;

function ExpN(ExpStart, ExpEnd: Integer; ExpType: TSynMarkupIfdefNodeTypeTest;
  Peer1: PPeerExpect = nil; Peer2: PPeerExpect = nil): TNodeExpect;
begin
  Result := ExpN(ExpStart, ExpEnd, 0, ExpType, Peer1, Peer2);
end;

function ExpN(ExpStart, ExpEnd: Integer; ExpEndLineOffs: Integer;
  Peer1: PPeerExpect; Peer2: PPeerExpect = nil): TNodeExpect;
begin
  Result := ExpN(ExpStart, ExpEnd, ExpEndLineOffs, idnSkipTest, Peer1, Peer2);
end;

function ExpN(ExpStart, ExpEnd: Integer; Peer1: PPeerExpect; Peer2: PPeerExpect = nil): TNodeExpect;
begin
  Result := ExpN(ExpStart, ExpEnd, 0, idnSkipTest, Peer1, Peer2);
end;

function ExpN(ExpStart: Integer; Peer1: PPeerExpect; Peer2: PPeerExpect = nil): TNodeExpect;
begin
  Result := ExpN(ExpStart, -1, 0, idnSkipTest, Peer1, Peer2);
end;


procedure TTestMarkupIfDef.CheckNodes(AName: String; ALine: Integer;
  AExp: array of TNodeExpect);
var
  Node: TSynMarkupHighIfDefEntry;

  procedure CheckPeerNode(ExpPeer: TPeerExpect; APeerType: TSynMarkupIfdefPeerType);
  var
    PName: String;
    TestPeer: TSynMarkupHighIfDefEntry;
  begin
    if ExpPeer.PeerType = idnSkipTest then
      exit;
    PName := '';
    WriteStr(PName, AName, ' ', ExpPeer.PeerType);
    case APeerType of
      idpOpeningPeer: TestPeer := Node.OpeningPeer;
      idpClosingPeer: TestPeer := Node.ClosingPeer;
    end;
    if ExpPeer.PeerType = idnMustBeNil then begin
      AssertTrue(PName + 'NO Peer', TestPeer = nil);
      exit;
    end;
    if ExpPeer.PeerY = -99 then begin // special check for existence only
      AssertTrue(PName + ' Has Peer', TestPeer <> nil);
      AssertTrue(PName+' PeerType', ExpPeer.PeerType = NodeTypeMap[TestPeer.NodeType]);
    end
    else
    if ExpPeer.PeerY < 0 then begin
      AssertTrue(PName + 'NO Peer', TestPeer = nil);
    end
    else begin
      AssertTrue(PName + ' Has Peer', TestPeer <> nil);
      AssertTrue(PName+' PeerType', ExpPeer.PeerType = NodeTypeMap[TestPeer.NodeType]);
      AssertEquals(PName + 'Peer.Y', ExpPeer.PeerY, TestPeer.Line.GetPosition);
      if ExpPeer.PeerX >= 0 then
        AssertEquals(PName + 'Peer.X', ExpPeer.PeerX, TestPeer.StartColumn);
    end;
  end;
var
  i, c: Integer;
  LineNode: TSynMarkupHighIfDefLinesNodeInfo;
  ExpNode: TNodeExpect;
begin
  AName := Format('%s - %s L=%d', [BaseTestName, AName, ALine]);
  LineNode := FTestTree.FindNodeAtPosition(ALine, afmNil);
  c := length(AExp);
  if (c = 0) and (not LineNode.HasNode) then
    exit;
  AssertTrue(AName + 'HasNode', LineNode.HasNode);
  AssertEquals(AName + 'EntryCount', c, LineNode.EntryCount);
  for i := 0 to c - 1 do begin
    ExpNode := AExp[i];
    Node := LineNode.Entry[i];
    AssertTrue('Node.Line = LineNode', Node.Line = LineNode.Node);
    AssertEquals(AName+'StartCol', ExpNode.ExpStart, Node.StartColumn);
// TODO
//    if ExpNode.ExpEnd >= 0 then
//      AssertEquals(AName+'EndCol', ExpNode.ExpEnd, Node.EndColumn);
    if ExpNode.ExpEndLineOffs >= 0 then begin
//      AssertEquals(AName+'EndLineOffs', ExpNode.ExpEndLineOffs, LineNode.LastEntryEndLineOffs);
////        AssertTrue(AName+'EndLineOffs flag', idnMultiLineTag in Node.NodeFlags);
    end;
    if ExpNode.ExpType <> idnSkipTest then
      AssertTrue(AName+'NodeTypeflag', NodeTypeMap[Node.NodeType] = ExpNode.ExpType);
    if ExpNode.TestExpState then
      AssertTrue(AName+'NodeState', Node.NodeState = ExpNode.ExpState);
    if ExpNode.OpenPeer.PeerType <> idnSkipTest then
      CheckPeerNode(ExpNode.OpenPeer, idpOpeningPeer);
    if ExpNode.ClosePeer.PeerType <> idnSkipTest then
      CheckPeerNode(ExpNode.ClosePeer, idpClosingPeer);
  end;

end;

procedure TTestMarkupIfDef.CheckNodesXY(AName: String; ALine: Integer;
  AExp: array of Integer; // [Start, end,  start, end, ....]
  AExpEndOffs: Integer);
var
  i, c: Integer;
  n1: TSynMarkupHighIfDefLinesNodeInfo;
begin
  AName := Format('%s - %s L=%d', [BaseTestName, AName, ALine]);
  n1 := FTestTree.FindNodeAtPosition(ALine, afmNil);
  c := length(AExp);
  if (c = 0) and (not n1.HasNode) then
    exit;
  AssertTrue(AName + 'HasNode', n1.HasNode);
  AssertEquals(AName + 'EntryCount', c div 2, n1.EntryCount);
  for i := 0 to (c div 2) - 1 do begin
    AssertTrue('Node.Line = LineNode', n1.Entry[i].Line = n1.Node);
    AssertEquals(AName+'StartCol', AExp[i*2], n1.Entry[i].StartColumn);
    AssertEquals(AName+'EndCol', AExp[i*2+1], n1.Entry[i].EndColumn);
  end;
  AssertEquals(AName+'EndLine', AExpEndOffs, n1.LastEntryEndLineOffs);
end;

//procedure TTestMarkupIfDef.CheckPeer(AName: String; ALine, ACol: Integer;
//  AType: TSynMarkupIfdefNodeTypeTest; ExpLine, ExpCol: Integer);
//var
//  n1: TSynMarkupHighIfDefLinesNodeInfo;
//  p: TSynMarkupHighIfDefEntry;
//begin
//  AName := Format('%s - %s L=%d Col=%d %s <=> %d, %d', [BaseTestName, AName, ALine, ACol, dbgs(AType), ExpLine, ExpCol]);
//  n1 := FTestTree.FindNodeAtPosition(ALine, afmNil);
//  AssertTrue(AName + 'HasNode', n1.HasNode);
//  AssertTrue(AName + 'HasEntry', n1.EntryCount > ACol);
//  case AType of
//    idnIfdef: p := n1.Entry[ACol].IfDefPeer;
//    idnElse:  p := n1.Entry[ACol].ElsePeer;
//    idnEndIf: p := n1.Entry[ACol].EndIfPeer;
//  end;
//  AssertTrue(AName + 'Peer', p <> nil);
//  AssertEquals(AName + 'Peer.Y', ExpLine, p.Line.GetPosition);
//  AssertTrue(AName + 'Peer.X (1)', p.Line.EntryCount > ExpCol);
//  AssertTrue(AName + 'Peer.X (2)', p.Line.Entry[ExpCol] = p);
//end;


{ TTestMarkupIfDef }

function TTestMarkupIfDef.TestTextEmpty: TStringArray;
begin
  SetLength(Result, 0);
end;

function TTestMarkupIfDef.TestTextNoIfDef: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  AddLine('//'                                                    );
  AddLine('//'                                                    );
  AddLine('//'                                                    );
  AddLine('//'                                                    );

  AddLine('//'                                                    );
  AddLine('//'                                                    );
  AddLine('//'                                                    );
  AddLine('//'                                                    );
  AddLine('//'                                                    );
end;

function TTestMarkupIfDef.TestTextIfDef: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;

begin
  // 1
  AddLine('//'                                                    );
  AddLine('{$IFDEF a}'                                            );
  AddLine(''                                                      );
  AddLine(' {$IFDEF b}'                                           );
  // 5
  AddLine('  {$IFDEF cc}'                                         );
  AddLine(''                                                      );
  AddLine(' {$IFDEF d}{$IFDEF eee}{$IFDEF ffff}'                  );
  AddLine(''                                                      );
  AddLine('{$IFDEF g'                                             );
  // 10
  AddLine('}{$IFDEF h'                                            );
  AddLine(''                                                      );
  AddLine(' }'                                                    );
  AddLine(''                                                      );
  AddLine('{$IFDEF i'                                             );
  // 15
  AddLine('    }{$IFDEF h'                                        );
  AddLine(''                                                      );
  AddLine('           }'                                          );
  AddLine(''                                                      );
  AddLine(''                                                      );

end;

function TTestMarkupIfDef.TestText1: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('//'                                                    );
  AddLine('{$IFDEF a}'                                            ); // A >> else 29 / end 36 (1-11)
  AddLine(''                                                      ); // |
  AddLine('  // $ifdef'                                           ); // |
  // 5
  AddLine('  {$IFDEF b}'                                          ); // | B >> end 17 (3-13)
  AddLine(''                                                      ); // | |
  AddLine('    {$IFDEF c} F1; {$else} F2; {$endif}   {$IFDEF d}'  ); // | | --(5-15/20-27/32-40) // D >> 11 (43-53)
  AddLine('      {$IFDEF e}  {$IFDEF f} F1; {$else} F2; {$endif} '); // | | | e to 10 (7-17) // -- (19-29/34-41/46-54)
  AddLine(''                                                      ); // | | | |
  // 10
  AddLine('      {$Endif}'                                        ); // | | | e << from line 8 (7-15)
  AddLine('    {$Endif}'                                          ); // | | d << from line 7 (5-13)
  AddLine(''                                                      ); // | |
  AddLine('    {$IFDEF c2}'                                       ); // | | c2 >> (5-16)
  AddLine('    {$Endif}'                                          ); // | | c2 << from 14 (5-13)
  // 15
  AddLine('    {$IFDEF x} {$endif} '                              ); // | | -- (5-15/16-24)
  AddLine(''                                                      ); // | |
  AddLine('  {$Endif}'                                            ); // | close b from line 5 (3-11)
  AddLine(''                                                      ); // |
  AddLine('  {$IFDEF b2}'                                         ); // | b2 >> else 21 / end 27 (3-14)
  // 20
  AddLine(''                                                      ); // | |
  AddLine('  {$Else}'                                             ); // | else b2 from 19 to 27 (3-10)
  AddLine(''                                                      ); // | |
  AddLine('    {$IFDEF H}'                                        ); // | | H >> (5-15)
  AddLine(''                                                      ); // | | |
  // 25
  AddLine('    {$Endif}'                                          ); // | | H << (5-13)
  AddLine('    {$IFDEF Y} {$endif} '                              ); // | | -- (5-15/16-24)
  AddLine('  {$Endif}'                                            ); // | << b2 from 19 (else 21) (3-11)
  AddLine(''                                                      ); // |
  AddLine('{$Else}'                                               ); // A else from 2 to 36 (1-8)
  // 30
  AddLine(''                                                      ); // |
  AddLine('  {$IFDEF M}'                                          ); // | M (3-13)
  AddLine('  {$Else}'                                             ); // | else M (3-10)
  AddLine(''                                                      ); // | |
  AddLine('  {$Endif}'                                            ); // | close M (3-11)
  // 35
  AddLine(''                                                      ); // |
  AddLine('{$Endif}'                                              ); // A << from 1 / else 29 (1-9)
  AddLine(''                                                      ); //
end;

function TTestMarkupIfDef.TestText2: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('//'                                                    );
  AddLine('{$IFDEF a}'                                            );
  AddLine(''                                                      );
  AddLine('{$Endif}'                                              );
  // 5
  AddLine(''                                                      );
  AddLine('{$Endif}'                                              ); // extra endif
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  //10
  AddLine('{$IFDEF a}'                                            );
  AddLine(''                                                      );
  AddLine('{$else}'                                               );
  AddLine(''                                                      );
  AddLine('{$else}'                                               );
  // 15
  AddLine(''                                                      );
  AddLine('{$Endif}'                                              );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  // 20
  AddLine('{$IFDEF a}'                                            );
  AddLine(''                                                      );
  //       1 - 9     11 - 21    22 - 29 30 - 37 38 - 46   48 - 56   58 - 68    69 - 77
  AddLine('{$EndIf}  {$IFDEF a} {$ELSE} {$ELSE} {$ENDIF}  {$endif}  {$IFDEF a} {$ENDIF} ');
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
end;

function TTestMarkupIfDef.TestText3: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('//'                                                    );
  AddLine('{$IFDEF a}'                                            );
  AddLine(''                                                      );
  AddLine('{$Endif}'                                              );
  // 5
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
end;

function TTestMarkupIfDef.TestText3a: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('//'                                                    );
  AddLine('{$IFDEF a} {$IFDEF a}'                                 );
  AddLine(''                                                      );
  AddLine('{$Endif} {$Endif}'                                     );
  // 5
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
end;

function TTestMarkupIfDef.TestText4: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('//'                                                    );
  AddLine('{$IFDEF a}  {$Endif}'                                  );
  AddLine(''                                                      );
  AddLine('{$IFDEF a}  {$Endif}'                                  );
  AddLine(''                                                      );
  AddLine(''                                                      );
end;

function TTestMarkupIfDef.TestText5: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('//'                                                    );
  AddLine('{$IFDEF a}'                                            );
  AddLine(''                                                      );
  AddLine('{$elseif b}'                                           );
  // 5
  AddLine(''                                                      );
  AddLine('{$else}'                                            );
  AddLine(''                                                      );
  AddLine('{$Endif}'                                              );
  AddLine(''                                                      );
end;

function TTestMarkupIfDef.TestText6: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('//'                                                    );
  AddLine('{$IFDEF a}'                                            );
  AddLine(''                                                      );
  AddLine(''                                                      );
  // 5
  AddLine(''                                                      );
  AddLine(' {$IFDEF b}'                                           );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  // 10
  AddLine('  {$IFDEF c}'                                          );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine('  {$Endif}'                                            );
  // 15
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(' {$Endif}'                                             );
  AddLine(''                                                      );
  // 20
  AddLine(''                                                      );


end;

function TTestMarkupIfDef.TestText7: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('//'                                                    );
  AddLine('{$IFDEF a}'                                            );
  AddLine(''                                                      );
  AddLine('{$Endif}'                                              );
  // 5
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  // 10
  AddLine(' {$IFDEF a}'                                           );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(' {$Endif}'                                             );
  // 15
  AddLine(''                                                      );
  AddLine(''                                                      );

end;

function TTestMarkupIfDef.TestText8: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('//'                                                    );
  AddLine('{$IFDEF A} {$IFDEF B}    {$ENDIF} {$ENDIF}');
  AddLine(' {$IFDEF XX}'                                           );
  AddLine(''                                                      );
  // 5
  AddLine('{$ENDIF}');
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
end;

function TTestMarkupIfDef.TestText9: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('//'                                                    );
  AddLine('{$IFDEF a}'                                            );
  AddLine(''                                                      );
  AddLine('{$Elseif}'                                              );
  // 5
  AddLine(''                                                      );
  AddLine('{$Elseif}'                                              );
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine('{$Elseif}'                                              );
  // 10
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(' {$IFDEF b}'                                            );
  AddLine(''                                                      );
  AddLine(' {$Endif}'                                              );
  // 15
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine('{$Endif}'                                              );
  AddLine(''                                                      );
  AddLine(''                                                      );


end;

function TTestMarkupIfDef.TestText10: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('//'                                                    );
  AddLine('{$IFDEF a}'                                            );
  AddLine(''                                                      );
  AddLine(''                                                      );
  // 5
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine('{$IFDEF a}'                                            );
  AddLine('//'                                                      );
  AddLine(''                                                      );
end;

function TTestMarkupIfDef.TestText11: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('//'                                                    );
  AddLine('  {$if defined(cpu86)}  ');
  AddLine('  // a');
  AddLine('  {$elseif defined(cpupowerpc)}  ');
  //5
  AddLine('    // disabled  ');
  AddLine('  {$elseif defined(cpupowerpc)}  ');
  AddLine('    // enabled (invalid)  ');
  AddLine('  {$elseif defined(cpuarm)}  ');
  AddLine('    // enabled (invalid)  ');
  // 10
  AddLine('  {$elseif defined(CPUX86_64)}  ');
  AddLine('    // enabled (invalid)  ');
  AddLine('  {$else}  ');
  AddLine('    // enabled (invalid)  ');
  AddLine('  {$ifend}  ');
  //16
  AddLine(''                                                      );
  AddLine(''                                                      );

end;

function TTestMarkupIfDef.TestText11a: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('{$if defined(cpu86)}'      );         // level 0
  AddLine('  {$if defined(cpu86)}  ');           // level 1
  AddLine('  // a');
  AddLine('  {$elseif defined(cpupowerpc)}  ');  // level 1
  //5
  AddLine('    // disabled  ');
  AddLine('  {$elseif defined(cpupowerpc)}  ');  // level 1
  AddLine('    // enabled (invalid)  ');
  AddLine('  {$elseif defined(cpuarm)}  ');      // level 1
  AddLine('    {$if defined(cpu86)}// enabled (invalid)  ');
  // 10
  AddLine('    {$ifend}{$if defined(cpu86)}'      );     // level 2
  AddLine('  {$ifend}{$elseif defined(CPUX86_64)}  ');   // level 1  (close lvl 2)
  AddLine('    // enabled (invalid)  ');
  AddLine('    {$if defined(cpu86)}'      );       // level 2
  AddLine('    {$ifend}{$if defined(cpu86)}'      );  // level 2 <> 2
  // 15
  AddLine('      // nested  ');
  AddLine('    {$elseif defined(cpuarm)}  ');       // level 2
  AddLine('      // nested  ');
  AddLine('    {$else}  ');                          // level 2
  AddLine('      // nested  ');
  // 20
  AddLine('    {$ifend}  ');                        // level 2
  AddLine('  {$else}  ');                         // level 1
  AddLine('    // enabled (invalid)  ');
  AddLine('    {$if defined(cpu86)}'      );
  AddLine('      // nested  ');
  // 25
  AddLine('    {$ifend}{$if defined(cpu86)}'      );
  AddLine('      // nested  ');
  AddLine('    {$elseif defined(cpuarm)}  ');
  AddLine('      // nested  ');
  AddLine('    {$else}  ');
  // 30
  AddLine('      // nested  ');
  AddLine('    {$ifend}  ');
  AddLine('  {$ifend}  ');
  AddLine(''                                                      );
  AddLine('  {$ifend}  ');
  // 35
  AddLine(''                                                      );
  AddLine(''                                                      );
  AddLine(''                                                      );
end;

function TTestMarkupIfDef.TestText12: TStringArray;
  procedure AddLine(s: String);
  begin
    SetLength(Result, Length(Result)+1);
    Result[Length(Result)-1] := s;
  end;
begin
  // 1
  AddLine('program project1;');
  AddLine('');
  AddLine('{$mode objfpc}{$H+}');
  AddLine('');
  // 5
  AddLine('uses {$IFDEF UNIX} {$IFDEF UseCThreads}');
  AddLine('  cthreads, {$ENDIF} {$ENDIF} {$ifdef LCLWinCE}');
  AddLine('  WinCEInt, {$endif}');
  AddLine('  Interfaces, // this includes the LCL widgetset');
  AddLine('  Windows,');
  AddLine('  SysUtils,');
  AddLine('  Forms;');
  AddLine('');
end;

procedure TTestMarkupIfDef.CheckOpenCloseCount(AName: String; ALine: Integer; AExpOpenCnt,
  AExpCloseCnt: Integer);
var
  LineNode: TSynMarkupHighIfDefLinesNodeInfo;
begin
  AName := Format('%s - %s L=%d', [BaseTestName, AName, ALine]);
  LineNode := FTestTree.FindNodeAtPosition(ALine, afmNil);
  AssertTrue(AName + 'HasNode', LineNode.HasNode);
  AssertEquals(AName + ' DisabledEntryOpenCount', AExpOpenCnt, LineNode.Node.DisabledEntryOpenCount);
  AssertEquals(AName + ' DisabledEntryCloseCount', AExpCloseCnt, LineNode.Node.DisabledEntryCloseCount);

end;

function TTestMarkupIfDef.CreateTheHighLighter: TSynCustomFoldHighlighter;
begin
  Result := TSynPasSyn.Create(nil);
end;

procedure TTestMarkupIfDef.ReCreateEditForTreeTest(Lines: array of String);
begin
  FreeAndNil(FTestTree);
  FTestTree.DiscardOpeningList(FOpenings);
  FOpenings := nil;;


  ReCreateEdit;
  TSynPasSyn(SynEdit.Highlighter).NestedComments := FUseNestedComments;
  SetLines(Lines);

  FTestTree := TSynMarkupHighIfDefLinesTree.Create;
  FTestTree.Lines       := SynEdit.ViewedTextBuffer;
  FTestTree.Highlighter := TSynPasSyn(SynEdit.Highlighter);

  FOpenings := FTestTree.CreateOpeningList;
end;

procedure TTestMarkupIfDef.TestIfDefTreeMoveOnEdit;
  procedure DoTest;
  var
    n: String;
  begin
    FTestTree := nil;

    n := 'No modification';
    ReCreateEditForTreeTest(TestTextIfDef);
    FTestTree.ValidateRange(1, 14, FOpenings);
    CheckNodesXY(n+'', 1, [], 0);
    CheckNodesXY(n+'', 2, [1, 11], 0);
    CheckNodesXY(n+'', 3, [], 0);
    CheckNodesXY(n+'', 4, [2, 12], 0);
    CheckNodesXY(n+'', 5, [3, 14], 0);
    CheckNodes(n+'', 5, [ExpN(3,14, idnIfdef)]);

    {%region  one line edit}
    ReCreateEditForTreeTest(TestTextIfDef);
    FTestTree.ValidateRange(1, 10, FOpenings);

    n := 'Remove Space AT line-start, Node after space';
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);
    SynEdit.TextBetweenPoints[point(1, 7),point(2, 7)] := '';
    CheckNodesXY(n+'', 7, [1,11,   11,23,   23,36], 0);

    n := 'Insert Space AT line-start, Node after space';
    SynEdit.TextBetweenPoints[point(1, 7),point(1, 7)] := ' ';
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);

    n := 'Replace Space AT line-start, Node after space';
    SynEdit.TextBetweenPoints[point(1, 7),point(2, 7)] := #9;
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);

    n := 'Insert Space AT line-start, node later on line';
    SynEdit.TextBetweenPoints[point(1, 7),point(1, 7)] := ' ';
    CheckNodesXY(n+'', 7, [3,13,   13,25,   25,38], 0);

    n := 'Insert Space before first node on line';
    SynEdit.TextBetweenPoints[point(3, 7),point(3, 7)] := ' ';
    CheckNodesXY(n+'', 7, [4,14,   14,26,   26,39], 0);

    n := 'Remove Space before first node on line';
    SynEdit.TextBetweenPoints[point(3, 7),point(4, 7)] := '';
    CheckNodesXY(n+'', 7, [3,13,   13,25,   25,38], 0);

    n := 'Insert Space between first and 2nd node on line';
    SynEdit.TextBetweenPoints[point(13, 7),point(13, 7)] := ' ';
    CheckNodesXY(n+'', 7, [3,13,   14,26,   26,39], 0);

    n := 'Remove Space between first and 2nd node on line';
    SynEdit.TextBetweenPoints[point(13, 7),point(14, 7)] := '';
    CheckNodesXY(n+'', 7, [3,13,   13,25,   25,38], 0);

    n := 'Insert Space after last node on line';
    SynEdit.TextBetweenPoints[point(38, 7),point(38, 7)] := ' ';
    CheckNodesXY(n+'', 7, [3,13,   13,25,   25,38], 0);

    n := 'Remove Space after last node on line';
    SynEdit.TextBetweenPoints[point(38, 7),point(39, 7)] := '';
    CheckNodesXY(n+'', 7, [3,13,   13,25,   25,38], 0);

    // Edit in node
    ReCreateEditForTreeTest(TestTextIfDef);
    FTestTree.ValidateRange(1, 19, FOpenings);
    n := 'Insert Space INSIDE node (1st node on line)';
    SynEdit.TextBetweenPoints[point(3, 7),point(3, 7)] := ' ';
    CheckNodesXY(n+'', 7, [2,13,   13,25,   25,38], 0);

    n := 'REMOVE Space INSIDE node (1st node on line)';
    SynEdit.TextBetweenPoints[point(3, 7),point(4, 7)] := '';
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);

    n := 'Insert Space INSIDE node end (1st node on line)';
    SynEdit.TextBetweenPoints[point(11, 7),point(11, 7)] := ' ';
    CheckNodesXY(n+'', 7, [2,13,   13,25,   25,38], 0);

    n := 'REMOVE Space INSIDE node end (1st node on line)';
    SynEdit.TextBetweenPoints[point(11, 7),point(12, 7)] := '';
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);

    n := 'Insert Space INSIDE node (last node on line)';
    SynEdit.TextBetweenPoints[point(26, 7),point(26, 7)] := ' ';
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,38], 0);

    n := 'REMOVE Space INSIDE node (last node on line)';
    SynEdit.TextBetweenPoints[point(26, 7),point(27, 7)] := '';
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);

    n := 'Insert Space INSIDE node (last node on line, multiline)';
    SynEdit.TextBetweenPoints[point(3, 14),point(3, 14)] := ' ';
    CheckNodesXY(n+'', 14, [1,6], 1);

    n := 'REMOVE Space INSIDE node (last node on line, multiline)';
    SynEdit.TextBetweenPoints[point(3, 14),point(4, 14)] := '';
    CheckNodesXY(n+'', 14, [1,6], 1);


    // test 1,12 modify previous node
    n := 'Insert Space INSIDE node (last node on line, multiline, on end line)';
    SynEdit.TextBetweenPoints[point(1, 12),point(1, 12)] := ' ';
    CheckNodesXY(n+'', 10, [2,4], 2);

    n := 'REMOVE Space INSIDE node (last node on line, multiline, on end line)';
    SynEdit.TextBetweenPoints[point(1, 12),point(2, 12)] := '';
    CheckNodesXY(n+'', 10, [2,3], 2);

    // test 1,15 modify previous and current node
    n := 'Insert Space INSIDE node (last node on line, multiline, on end line with NEXT open)';
    SynEdit.TextBetweenPoints[point(2, 15),point(2, 15)] := ' ';
    CheckNodesXY(n+'', 14, [1, 7], 1);
    CheckNodesXY(n+'', 15, [7,13], 2);

      n := 'REMOVE Space INSIDE node (last node on line, multiline, on end line with NEXT open)';
    SynEdit.TextBetweenPoints[point(2, 15),point(3, 15)] := '';
    CheckNodesXY(n+'', 14, [1, 6], 1);
    CheckNodesXY(n+'', 15, [6,13], 2);

    n := 'Insert Space Between node (last node on line, multiline, on end line with NEXT open)';
    SynEdit.TextBetweenPoints[point(6, 15),point(6, 15)] := ' ';
    CheckNodesXY(n+'', 14, [1, 6], 1);
    CheckNodesXY(n+'', 15, [7,13], 2);

    n := 'REMOVE Space Between node (last node on line, multiline, on end line with NEXT open)';
    SynEdit.TextBetweenPoints[point(6, 15),point(7, 15)] := '';
    CheckNodesXY(n+'', 14, [1, 6], 1);
    CheckNodesXY(n+'', 15, [6,13], 2);

    n := 'Insert Space at end of node (IN node) (last node on line, multiline, on end line with NEXT open)';
    SynEdit.TextBetweenPoints[point(5, 15),point(5, 15)] := ' ';
    CheckNodesXY(n+'', 14, [1, 7], 1);
    CheckNodesXY(n+'', 15, [7,13], 2);

  // delete an entire node
    n := 'Delete full node';
    SynEdit.TextBetweenPoints[point(10, 7),point(27, 7)] := '';
  FTestTree.DebugPrint(true);
    CheckNodesXY(n+'', 7, [2,10,   10,20], 0);

    {%endregion one line}

    {%region  Line Breaks }

    n := 'not modified';
    ReCreateEditForTreeTest(TestTextIfDef);
    FTestTree.ValidateRange(1, 19, FOpenings);
    CheckNodesXY(n+'', 6, [], 0);
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);
    CheckNodesXY(n+'', 8, [], 0);
    CheckNodesXY(n+'', 9, [1,2], 1);


    n := 'Insert LineBreak at line start nodes';
    SynEdit.TextBetweenPoints[point(1, 7),point(1, 7)] := LineEnding;
    CheckNodesXY(n+'', 6, [], 0);
    CheckNodesXY(n+'', 7, [], 0);
    CheckNodesXY(n+'', 8, [2,12,   12,24,   24,37], 0);
    CheckNodesXY(n+'', 9, [], 0);
    CheckNodesXY(n+'',10, [1,2], 1);

    n := 'Remove LineBreak at line start nodes';
    SynEdit.TextBetweenPoints[point(1, 7),point(1, 8)] := '';
    CheckNodesXY(n+'', 6, [], 0);
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);
    CheckNodesXY(n+'', 8, [], 0);
    CheckNodesXY(n+'', 9, [1,2], 1);


    n := 'Insert LineBreak before nodes';
    SynEdit.TextBetweenPoints[point(2, 7),point(2, 7)] := LineEnding;
    CheckNodesXY(n+'', 6, [], 0);
    CheckNodesXY(n+'', 7, [], 0);
    CheckNodesXY(n+'', 8, [1,11,   11,23,   23,36], 0);
    CheckNodesXY(n+'', 9, [], 0);
    CheckNodesXY(n+'',10, [1,2], 1);

    n := 'Remove LineBreak before nodes';
    SynEdit.TextBetweenPoints[point(2, 7),point(1, 8)] := '';
    CheckNodesXY(n+'', 6, [], 0);
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);
    CheckNodesXY(n+'', 8, [], 0);
    CheckNodesXY(n+'', 9, [1,2], 1);



    n := 'Insert LineBreak between nodes';
    SynEdit.TextBetweenPoints[point(24, 7),point(24, 7)] := LineEnding;
    CheckNodesXY(n+'', 6, [], 0);
    CheckNodesXY(n+'', 7, [2,12,   12,24], 0);
    CheckNodesXY(n+'', 8, [1,14], 0);
    CheckNodesXY(n+'', 9, [], 0);
    CheckNodesXY(n+'',10, [1,2], 1);

    n := 'Remove LineBreak between nodes';
    SynEdit.TextBetweenPoints[point(24, 7),point(1, 8)] := '';
    CheckNodesXY(n+'', 6, [], 0);
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);
    CheckNodesXY(n+'', 8, [], 0);
    CheckNodesXY(n+'', 9, [1,2], 1);



    n := 'Insert LineBreak after nodes';
    SynEdit.TextBetweenPoints[point(37, 7),point(37, 7)] := LineEnding;
    CheckNodesXY(n+'', 6, [], 0);
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);
    CheckNodesXY(n+'', 8, [], 0);
    CheckNodesXY(n+'', 9, [], 0);
    CheckNodesXY(n+'',10, [1,2], 1);

    n := 'Remove LineBreak after nodes';
    SynEdit.TextBetweenPoints[point(37, 7),point(1, 8)] := '';
    CheckNodesXY(n+'', 6, [], 0);
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);
    CheckNodesXY(n+'', 8, [], 0);
    CheckNodesXY(n+'', 9, [1,2], 1);



    n := 'Insert LineBreak INSIDE nodes';
    SynEdit.TextBetweenPoints[point(13, 7),point(13, 7)] := LineEnding;
    CheckNodesXY(n+'', 6, [], 0);
    CheckNodesXY(n+'', 7, [2,12,   12,12], 1);
    CheckNodesXY(n+'', 8, [12,25], 0);
    CheckNodesXY(n+'', 9, [], 0);
    CheckNodesXY(n+'',10, [1,2], 1);

    n := 'Remove LineBreak INSIDE  nodes';
    SynEdit.TextBetweenPoints[point(13, 7),point(1, 8)] := '';
    CheckNodesXY(n+'', 6, [], 0);
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);
    CheckNodesXY(n+'', 8, [], 0);
    CheckNodesXY(n+'', 9, [1,2], 1);



    n := 'Insert LineBreak INSIDE nodes (last node 1 line)';
    SynEdit.TextBetweenPoints[point(23, 7),point(23, 7)] := LineEnding;
    CheckNodesXY(n+'', 6, [], 0);
    CheckNodesXY(n+'', 7, [2,12,   12,2], 1);
    CheckNodesXY(n+'', 8, [2,15], 0);
    CheckNodesXY(n+'', 9, [], 0);
    CheckNodesXY(n+'',10, [1,2], 1);

    n := 'Remove LineBreak INSIDE  nodes (last node 1 line)';
    SynEdit.TextBetweenPoints[point(23, 7),point(1, 8)] := '';
    CheckNodesXY(n+'', 6, [], 0);
    CheckNodesXY(n+'', 7, [2,12,   12,24,   24,37], 0);
    CheckNodesXY(n+'', 8, [], 0);
    CheckNodesXY(n+'', 9, [1,2], 1);



    n := 'Insert LineBreak INSIDE nodes (last node multi line)';
    SynEdit.TextBetweenPoints[point(9, 10),point(9, 10)] := LineEnding;
    CheckNodesXY(n+'',  9, [1,2], 1);
    CheckNodesXY(n+'', 10, [2,3], 3);
    CheckNodesXY(n+'', 14, [], 0);
    CheckNodesXY(n+'', 15, [1,6], 1);

    n := 'Remove LineBreak INSIDE  nodes (last node multi line)';
    SynEdit.TextBetweenPoints[point(9, 10),point(1, 11)] := '';
    CheckNodesXY(n+'',  9, [1,2], 1);
    CheckNodesXY(n+'', 10, [2,3], 2);
    CheckNodesXY(n+'', 13, [], 0);
    CheckNodesXY(n+'', 14, [1,6], 1);


    n := 'Insert LineBreak INSIDE nodes (last node multi line)';
    SynEdit.TextBetweenPoints[point(2, 14),point(2, 14)] := LineEnding;
    CheckNodesXY(n+'', 14, [1,6], 2);

    n := 'Remove LineBreak INSIDE  nodes (last node multi line)';
    SynEdit.TextBetweenPoints[point(2, 14),point(1, 15)] := '';
    CheckNodesXY(n+'', 14, [1,6], 1);



    n := 'Insert LineBreak INSIDE nodes (previous node multi line)';
    SynEdit.TextBetweenPoints[point(1, 10),point(1, 10)] := LineEnding;
    CheckNodesXY(n+'',  9, [1,2], 2);
    CheckNodesXY(n+'', 10, [], 0);
    CheckNodesXY(n+'', 11, [2,3], 2);

    n := 'Remove LineBreak INSIDE  nodes (previous node multi line)';
    SynEdit.TextBetweenPoints[point(1, 10),point(1, 11)] := '';
    CheckNodesXY(n+'',  9, [1,2], 1);
    CheckNodesXY(n+'', 10, [2,3], 2);



    n := 'Insert LineBreak INSIDE nodes (last node multi line - no next)';
    SynEdit.TextBetweenPoints[point(1, 17),point(1, 17)] := LineEnding;
    CheckNodesXY(n+'', 14, [1, 6], 1);
    CheckNodesXY(n+'', 15, [6,13], 3);
    CheckNodesXY(n+'', 16, [], 0);
    CheckNodesXY(n+'', 17, [], 0);
    CheckNodesXY(n+'', 18, [], 0);

    n := 'Remove LineBreak INSIDE  nodes (last node multi line - no next)';
    SynEdit.TextBetweenPoints[point(1, 17),point(1, 18)] := '';
    CheckNodesXY(n+'', 14, [1, 6], 1);
    CheckNodesXY(n+'', 15, [6,13], 2);
    CheckNodesXY(n+'', 16, [], 0);
    CheckNodesXY(n+'', 17, [], 0);
    CheckNodesXY(n+'', 18, [], 0);


    n := 'Insert LineBreak INSIDE nodes (mid last node multi line - no next)';
    SynEdit.TextBetweenPoints[point(7, 16),point(7, 16)] := LineEnding;
    CheckNodesXY(n+'', 14, [1, 6], 1);
    CheckNodesXY(n+'', 15, [6,13], 3);
    CheckNodesXY(n+'', 16, [], 0);
    CheckNodesXY(n+'', 17, [], 0);
    CheckNodesXY(n+'', 18, [], 0);

    n := 'Remove LineBreak INSIDE  nodes (mid last node multi line - no next)';
    SynEdit.TextBetweenPoints[point(7, 16),point(1, 17)] := '';
    CheckNodesXY(n+'', 14, [1, 6], 1);
    CheckNodesXY(n+'', 15, [6,13], 2);
    CheckNodesXY(n+'', 16, [], 0);
    CheckNodesXY(n+'', 17, [], 0);
    CheckNodesXY(n+'', 18, [], 0);





    n := 'Remove LineBreak INSIDE  nodes EndOffs';
    CheckNodesXY(n+'', 8, [], 0);
    CheckNodesXY(n+'', 9, [1,2], 1);
    CheckNodesXY(n+'',10, [2,3], 2);
    CheckNodesXY(n+'',11, [], 0);
    CheckNodesXY(n+'',12, [], 0);

    SynEdit.TextBetweenPoints[point(10, 9),point(1, 10)] := '';
    CheckNodesXY(n+'', 8, [], 0);
    CheckNodesXY(n+'', 9, [1,11,  11,3], 2);
    CheckNodesXY(n+'',10, [], 0);
    CheckNodesXY(n+'',11, [], 0);
    CheckNodesXY(n+'',12, [], 0);

    n := 'Insert LineBreak INSIDE  nodes EndOffs';
    SynEdit.TextBetweenPoints[point(10, 9),point(10, 9)] := LineEnding;
    CheckNodesXY(n+'', 8, [], 0);
    CheckNodesXY(n+'', 9, [1,2], 1);
    CheckNodesXY(n+'',10, [2,3], 2);
    CheckNodesXY(n+'',11, [], 0);
    CheckNodesXY(n+'',12, [], 0);

    {%endregion  Line Breaks }



    ReCreateEditForTreeTest(TestTextIfDef);
    FTestTree.ValidateRange(1, 14, FOpenings);
    CheckNodesXY('', 1, [], 0);
    CheckNodesXY('', 2, [1, 11], 0);
    CheckNodesXY('', 3, [], 0);
    CheckNodesXY('', 4, [2, 12], 0);
    CheckNodesXY('', 5, [3, 14], 0);

    // no modification
    SynEdit.TextBetweenPoints[point(1,1),point(1,2)] := LineEnding;
    CheckNodesXY('', 1, [], 0);
    CheckNodesXY('', 2, [1, 11], 0);
    CheckNodesXY('', 3, [], 0);
    CheckNodesXY('', 4, [2, 12], 0);
    CheckNodesXY('', 5, [3, 14], 0);

    SynEdit.TextBetweenPoints[point(1,1),point(1,1)] := LineEnding;
    CheckNodesXY('', 1, [], 0);
    CheckNodesXY('', 2, [], 0);
    CheckNodesXY('', 3, [1, 11], 0);
    CheckNodesXY('', 4, [], 0);
    CheckNodesXY('', 5, [2, 12], 0);
    CheckNodesXY('', 6, [3, 14], 0);

    SynEdit.TextBetweenPoints[point(1, 3),point(1, 3)] := ' ';
    CheckNodesXY('', 1, [], 0);
    CheckNodesXY('', 2, [], 0);
    CheckNodesXY('', 3, [2, 12], 0);
    CheckNodesXY('', 4, [], 0);
    CheckNodesXY('', 5, [2, 12], 0);
    CheckNodesXY('', 6, [3, 14], 0);

    SynEdit.TextBetweenPoints[point(1, 3),point(2, 3)] := '';
    CheckNodesXY('', 1, [], 0);
    CheckNodesXY('', 2, [], 0);
    CheckNodesXY('', 3, [1, 11], 0);
    CheckNodesXY('', 4, [], 0);
    CheckNodesXY('', 5, [2, 12], 0);
    CheckNodesXY('', 6, [3, 14], 0);


    {%region  Delete line with node}
      ReCreateEditForTreeTest(TestText7);
      FTestTree.ValidateRange( 1,  5, FOpenings);
      FTestTree.ValidateRange(10, 10, FOpenings);
      FTestTree.ValidateRange(14, 14, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef ) ]);
      CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf ) ]);
      CheckNodes(n,10, [ ExpN( 2,12, idnIfdef ) ]);
      CheckNodes(n,14, [ ExpN( 2,10, idnEndIf ) ]);

      SynEdit.TextBetweenPoints[point(1, 4),point(1, 5)] := '';
      FOpenings.Clear;
      FTestTree.ValidateRange(3, 5, FOpenings); // only validate deleted line
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef) ]);
      CheckNodes(n, 3, [  ]);
      CheckNodes(n, 4, [  ]);
      CheckNodes(n, 5, [  ]);
      CheckNodes(n, 9, [ ExpN( 2,12, idnIfdef) ]);
      CheckNodes(n,13, [ ExpN( 2,10, idnEndIf) ]);

      SynEdit.TextBetweenPoints[point(1, 4),point(1, 4)] := '{$ENDIF}' + LineEnding;
      FTestTree.ValidateRange(3, 5, FOpenings); // only validate deleted line
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef ) ]);
      CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf ) ]);
      CheckNodes(n,10, [ ExpN( 2,12, idnIfdef ) ]);
      CheckNodes(n,14, [ ExpN( 2,10, idnEndIf ) ]);

    {%endregion   }




    // Insert IFDEF into empty text
    ReCreateEditForTreeTest(TestTextNoIfDef);
    FTestTree.ValidateRange(1, 5, FOpenings);
    SynEdit.TextBetweenPoints[point(1, 3),point(1, 3)] := '{$IFDEF a}';
    FTestTree.ValidateRange(1, 5, FOpenings);
    CheckNodesXY('Insert IFDEF into empty text', 3, [1,11], 0);


    FTestTree.DiscardOpeningList(FOpenings);
    FOpenings := nil;;
    FTestTree.Free;
  end;
begin
  FUseNestedComments := False;
  DoTest;
  FUseNestedComments := True;
  DoTest;
end;

procedure TTestMarkupIfDef.TestIfDefTreePeerConnect;
  procedure DoTest;
  var
    n: String;
    i, i2, i3, i4: Integer;
    epMaybeIf: PPeerExpect;
  begin
    FTestTree := nil;


    {%region peers}
      n := 'Peers, TestText1: Validate all';
      ReCreateEditForTreeTest(TestText1);
      FTestTree.ValidateRange(1, 36, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpElse(29, 1)) ]);
      CheckNodes(n, 5, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(17, 3)) ]);
      CheckNodes(n, 7, [ ExpN( 5,15, idnIfdef, EpSkip,      EpElse(7, 20)),
                         ExpN(20,27, idnElse,  EpIf(7, 5),  EpEnd(7, 32)),
                         ExpN(32,40, idnEndIf, EpElse(7, 20)),
                         ExpN(43,53, idnIfdef, EpSkip,      EpEnd(11, 5))  ]);
      CheckNodes(n, 8, [ ExpN( 7,17, idnIfdef, EpSkip,      EpEnd(10, 7)),
                         ExpN(19,29, idnIfdef, EpSkip,      EpElse(8, 34)),
                         ExpN(34,41, idnElse,  EpIf(8, 19), EpEnd(8, 46)),
                         ExpN(46,54, idnEndIf, EpElse(8, 34))  ]);
      CheckNodes(n,10, [ ExpN( 7,15, idnEndIf, EpIf(8, 7)) ]);
      CheckNodes(n,11, [ ExpN( 5,13, idnEndIf, EpIf(7, 43)) ]);
      CheckNodes(n,13, [ ExpN( 5,16, idnIfdef, EpSkip,      EpEnd(14, 5)) ]);
      CheckNodes(n,14, [ ExpN( 5,13, idnEndIf, EpIf(13, 5)) ]);
      CheckNodes(n,15, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(15, 16)),
                         ExpN(16,24, idnEndIf, EpIf(15, 5))  ]);
      CheckNodes(n,17, [ ExpN( 3,11, idnEndIf, EpIf(5, 3)) ]);
      CheckNodes(n,19, [ ExpN( 3,14, idnIfdef, EpSkip,      EpElse(21, 3)) ]);
      CheckNodes(n,21, [ ExpN( 3,10, idnElse,  EpIf(19, 3), EpEnd(27, 3)) ]);
      CheckNodes(n,23, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(25, 5)) ]);
      CheckNodes(n,25, [ ExpN( 5,13, idnEndIf, EpIf(23, 5)) ]);
      CheckNodes(n,26, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(26, 16)),
                         ExpN(16,24, idnEndIf, EpIf(26, 5))  ]);
      CheckNodes(n,27, [ ExpN( 3,11, idnEndIf, EpElse(21, 3)) ]);
      CheckNodes(n,29, [ ExpN( 1, 8, idnElse,  EpIf(2, 1),  EpEnd(36,1)) ]);
      CheckNodes(n,31, [ ExpN( 3,13, idnIfdef, EpSkip,      EpElse(32, 3)) ]);
      CheckNodes(n,32, [ ExpN( 3,10, idnElse,  EpIf(31, 3), EpEnd(34,3)) ]);
      CheckNodes(n,34, [ ExpN( 3,11, idnEndIf, EpElse(32, 3)) ]);
      CheckNodes(n,36, [ ExpN( 1, 9, idnEndIf, EpElse(29, 1)) ]);



      n := 'Peers, TestText1: Validate 8 - 36';
      ReCreateEditForTreeTest(TestText1);
      FTestTree.ValidateRange(8, 36, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpElse(29, 1)) ]);
      CheckNodes(n, 5, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(17, 3)) ]);
      CheckNodes(n, 7, [ ExpN( 5,15, idnIfdef ),
                         ExpN(20,27, idnElse  ),
                         ExpN(32,40, idnEndIf ),
                         ExpN(43,53, idnIfdef, EpSkip,      EpEnd(11, 5))  ]);
      CheckNodes(n, 8, [ ExpN( 7,17, idnIfdef, EpSkip,      EpEnd(10, 7)),
                         ExpN(19,29, idnIfdef, EpSkip,      EpElse(8, 34)),
                         ExpN(34,41, idnElse,  EpIf(8, 19), EpEnd(8, 46)),
                         ExpN(46,54, idnEndIf, EpElse(8, 34))  ]);
      CheckNodes(n,10, [ ExpN( 7,15, idnEndIf, EpIf(8, 7)) ]);
      CheckNodes(n,11, [ ExpN( 5,13, idnEndIf, EpIf(7, 43)) ]);
      CheckNodes(n,13, [ ExpN( 5,16, idnIfdef, EpSkip,      EpEnd(14, 5)) ]);
      CheckNodes(n,14, [ ExpN( 5,13, idnEndIf, EpIf(13, 5)) ]);
      CheckNodes(n,15, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(15, 16)),
                         ExpN(16,24, idnEndIf, EpIf(15, 5))  ]);
      CheckNodes(n,17, [ ExpN( 3,11, idnEndIf, EpIf(5, 3)) ]);
      CheckNodes(n,19, [ ExpN( 3,14, idnIfdef, EpSkip,      EpElse(21, 3)) ]);
      CheckNodes(n,21, [ ExpN( 3,10, idnElse,  EpIf(19, 3), EpEnd(27, 3)) ]);
      CheckNodes(n,23, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(25, 5)) ]);
      CheckNodes(n,25, [ ExpN( 5,13, idnEndIf, EpIf(23, 5)) ]);
      CheckNodes(n,26, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(26, 16)),
                         ExpN(16,24, idnEndIf, EpIf(26, 5))  ]);
      CheckNodes(n,27, [ ExpN( 3,11, idnEndIf, EpElse(21, 3)) ]);
      CheckNodes(n,29, [ ExpN( 1, 8, idnElse,  EpIf(2, 1),  EpEnd(36,1)) ]);
      CheckNodes(n,31, [ ExpN( 3,13, idnIfdef, EpSkip,      EpElse(32, 3)) ]);
      CheckNodes(n,32, [ ExpN( 3,10, idnElse,  EpIf(31, 3), EpEnd(34,3)) ]);
      CheckNodes(n,34, [ ExpN( 3,11, idnEndIf, EpElse(32, 3)) ]);
      CheckNodes(n,36, [ ExpN( 1, 9, idnEndIf, EpElse(29, 1)) ]);




      n := 'Peers, TestText1: Validate 33 - 36';
      ReCreateEditForTreeTest(TestText1);
      FTestTree.ValidateRange(33, 36, FOpenings);
      AssertTrue('Scan start node Node at empty line', FTestTree.FindNodeAtPosition(33, afmNil).HasNode);
      CheckNodes(n,33, [ ]);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpElse(29, 1)) ]);
      CheckNodes(n,29, [ ExpN( 1, 8, idnElse,  EpIf(2, 1), EpEnd(36,1)) ]);
      CheckNodes(n,31, [ ExpN( 3,13, idnIfdef, EpSkip,      EpElse(32, 3)) ]);
      CheckNodes(n,32, [ ExpN( 3,10, idnElse,  EpIf(31, 3), EpEnd(34,3)) ]);
      CheckNodes(n,34, [ ExpN( 3,11, idnEndIf, EpElse(32, 3)) ]);
      CheckNodes(n,36, [ ExpN( 1, 9, idnEndIf, EpElse(29, 1)) ]);
      //
      n := 'Peers, TestText1: Validate all  AFTER 33-36';
      FTestTree.ValidateRange(1, 36, FOpenings);
      AssertFalse('Scan start node Node at empty line gone', FTestTree.FindNodeAtPosition(33, afmNil).HasNode);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpElse(29, 1)) ]);
      CheckNodes(n, 5, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(17, 3)) ]);
      CheckNodes(n, 7, [ ExpN( 5,15, idnIfdef, EpSkip,      EpElse(7, 20)),
                         ExpN(20,27, idnElse,  EpIf(7, 5), EpEnd(7, 32)),
                         ExpN(32,40, idnEndIf, EpElse(7, 20)),
                         ExpN(43,53, idnIfdef, EpSkip,      EpEnd(11, 5))  ]);
      CheckNodes(n, 8, [ ExpN( 7,17, idnIfdef, EpSkip,      EpEnd(10, 7)),
                         ExpN(19,29, idnIfdef, EpSkip,      EpElse(8, 34)),
                         ExpN(34,41, idnElse,  EpIf(8, 19), EpEnd(8, 46)),
                         ExpN(46,54, idnEndIf, EpElse(8, 34))  ]);
      CheckNodes(n,10, [ ExpN( 7,15, idnEndIf, EpIf(8, 7)) ]);
      CheckNodes(n,11, [ ExpN( 5,13, idnEndIf, EpIf(7, 43)) ]);
      CheckNodes(n,13, [ ExpN( 5,16, idnIfdef, EpSkip,      EpEnd(14, 5)) ]);
      CheckNodes(n,14, [ ExpN( 5,13, idnEndIf, EpIf(13, 5)) ]);
      CheckNodes(n,15, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(15, 16)),
                         ExpN(16,24, idnEndIf, EpIf(15, 5))  ]);
      CheckNodes(n,17, [ ExpN( 3,11, idnEndIf, EpIf(5, 3)) ]);
      CheckNodes(n,19, [ ExpN( 3,14, idnIfdef, EpSkip,      EpElse(21, 3)) ]);
      CheckNodes(n,21, [ ExpN( 3,10, idnElse,  EpIf(19, 3), EpEnd(27, 3)) ]);
      CheckNodes(n,23, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(25, 5)) ]);
      CheckNodes(n,25, [ ExpN( 5,13, idnEndIf, EpIf(23, 5)) ]);
      CheckNodes(n,26, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(26, 16)),
                         ExpN(16,24, idnEndIf, EpIf(26, 5))  ]);
      CheckNodes(n,27, [ ExpN( 3,11, idnEndIf, EpElse(21, 3)) ]);
      CheckNodes(n,29, [ ExpN( 1, 8, idnElse,  EpIf(2, 1),  EpEnd(36,1)) ]);
      CheckNodes(n,31, [ ExpN( 3,13, idnIfdef, EpSkip,      EpElse(32, 3)) ]);
      CheckNodes(n,32, [ ExpN( 3,10, idnElse,  EpIf(31, 3), EpEnd(34,3)) ]);
      CheckNodes(n,34, [ ExpN( 3,11, idnEndIf, EpElse(32, 3)) ]);
      CheckNodes(n,36, [ ExpN( 1, 9, idnEndIf, EpElse(29, 1)) ]);




      n := 'Peers, TestText1: 32-36';
      ReCreateEditForTreeTest(TestText1);
      FTestTree.ValidateRange(32, 36, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpElse(29, 1)) ]);
      CheckNodes(n,29, [ ExpN( 1, 8, idnElse,  EpIf(2, 1), EpEnd(36,1)) ]);
      CheckNodes(n,31, [ ExpN( 3,13, idnIfdef, EpSkip,      EpElse(32, 3)) ]);
      CheckNodes(n,32, [ ExpN( 3,10, idnElse,  EpIf(31, 3), EpEnd(34,3)) ]);
      CheckNodes(n,34, [ ExpN( 3,11, idnEndIf, EpElse(32, 3)) ]);
      CheckNodes(n,36, [ ExpN( 1, 9, idnEndIf, EpElse(29, 1)) ]);
      //
      n := 'Peers, TestText1: 16-20 AFTER 32-36';
      FTestTree.ValidateRange(16, 20, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpElse(29, 1)) ]);
      CheckNodes(n, 5, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(17, 3)) ]);
      CheckNodes(n,17, [ ExpN( 3,11, idnEndIf, EpIf(5, 3)) ]);
      CheckNodes(n,19, [ ExpN( 3,14, idnIfdef) ]);
      CheckNodes(n,29, [ ExpN( 1, 8, idnElse,  EpIf(2, 1), EpEnd(36,1)) ]);
      CheckNodes(n,31, [ ExpN( 3,13, idnIfdef, EpSkip,      EpElse(32, 3)) ]);
      CheckNodes(n,32, [ ExpN( 3,10, idnElse,  EpIf(31, 3), EpEnd(34,3)) ]);
      CheckNodes(n,34, [ ExpN( 3,11, idnEndIf, EpElse(32, 3)) ]);
      CheckNodes(n,36, [ ExpN( 1, 9, idnEndIf, EpElse(29, 1)) ]);
      //
      n := 'Peers, TestText1: all AFTER 16-20 AFTER 32-36';
      FTestTree.ValidateRange(1, 36, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpElse(29, 1)) ]);
      CheckNodes(n, 5, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(17, 3)) ]);
      CheckNodes(n, 7, [ ExpN( 5,15, idnIfdef, EpSkip,      EpElse(7, 20)),
                         ExpN(20,27, idnElse,  EpIf(7, 5),  EpEnd(7, 32)),
                         ExpN(32,40, idnEndIf, EpElse(7, 20)),
                         ExpN(43,53, idnIfdef, EpSkip,      EpEnd(11, 5))  ]);
      CheckNodes(n, 8, [ ExpN( 7,17, idnIfdef, EpSkip,      EpEnd(10, 7)),
                         ExpN(19,29, idnIfdef, EpSkip,      EpElse(8, 34)),
                         ExpN(34,41, idnElse,  EpIf(8, 19), EpEnd(8, 46)),
                         ExpN(46,54, idnEndIf, EpElse(8, 34))  ]);
      CheckNodes(n,10, [ ExpN( 7,15, idnEndIf, EpIf(8, 7)) ]);
      CheckNodes(n,11, [ ExpN( 5,13, idnEndIf, EpIf(7, 43)) ]);
      CheckNodes(n,13, [ ExpN( 5,16, idnIfdef, EpSkip,      EpEnd(14, 5)) ]);
      CheckNodes(n,14, [ ExpN( 5,13, idnEndIf, EpIf(13, 5)) ]);
      CheckNodes(n,15, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(15, 16)),
                         ExpN(16,24, idnEndIf, EpIf(15, 5))  ]);
      CheckNodes(n,17, [ ExpN( 3,11, idnEndIf, EpIf(5, 3)) ]);
      CheckNodes(n,19, [ ExpN( 3,14, idnIfdef, EpSkip,      EpElse(21, 3)) ]);
      CheckNodes(n,21, [ ExpN( 3,10, idnElse,  EpIf(19, 3), EpEnd(27, 3)) ]);
      CheckNodes(n,23, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(25, 5)) ]);
      CheckNodes(n,25, [ ExpN( 5,13, idnEndIf, EpIf(23, 5)) ]);
      CheckNodes(n,26, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(26, 16)),
                         ExpN(16,24, idnEndIf, EpIf(26, 5))  ]);
      CheckNodes(n,27, [ ExpN( 3,11, idnEndIf, EpElse(21, 3)) ]);
      CheckNodes(n,29, [ ExpN( 1, 8, idnElse,  EpIf(2, 1),  EpEnd(36,1)) ]);
      CheckNodes(n,31, [ ExpN( 3,13, idnIfdef, EpSkip,      EpElse(32, 3)) ]);
      CheckNodes(n,32, [ ExpN( 3,10, idnElse,  EpIf(31, 3), EpEnd(34,3)) ]);
      CheckNodes(n,34, [ ExpN( 3,11, idnEndIf, EpElse(32, 3)) ]);
      CheckNodes(n,36, [ ExpN( 1, 9, idnEndIf, EpElse(29, 1)) ]);




      n := 'scan plain text, after closed node: step 1: node';
      ReCreateEditForTreeTest(TestText3);
      FTestTree.ValidateRange(3, 5, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(4, 1)) ]);
      CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf, EpIf(2, 1)) ]);

      n := 'scan plain text, after closed node: step 2: empty';
      FTestTree.ValidateRange(8, 9, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(4, 1)) ]);
      CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf, EpIf(2, 1)) ]);




      n := 'scan plain text, after closed node (overlap): step 1: node';
      ReCreateEditForTreeTest(TestText3);
      FTestTree.ValidateRange(3, 8, FOpenings); // ensure node is valid to begin-of-plain-line-scan
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(4, 1)) ]);
      CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf, EpIf(2, 1)) ]);

      n := 'scan plain text, after closed node (overlap): step 2: empty';
      FTestTree.ValidateRange(7, 9, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(4, 1)) ]);
      CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf, EpIf(2, 1)) ]);




      n := 'scan plain text, after closed node (not scanned end): step 1: node';
      ReCreateEditForTreeTest(TestText3);
      FTestTree.ValidateRange(3, 3, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef) ]);

      n := 'scan plain text, after closed node (not scanned end): step 2: empty';
      FTestTree.ValidateRange(8, 9, FOpenings);





      n := 'Peers, TestText5: elseif';
      ReCreateEditForTreeTest(TestText5);
      FTestTree.ValidateRange(1, 9, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpElseIf(4, 1)) ]);
      CheckNodes(n, 4, [ ExpN( 1,12, idnElseIf,EpIf(2, 1),  EpElse(6, 1)) ]);
      CheckNodes(n, 6, [ ExpN( 1, 8, idnElse,  EpElseIf(4, 1), EpEnd(8, 1)) ]);
      CheckNodes(n, 8, [ ExpN( 1, 9, idnEndIf, EpElse(6, 1)) ]);

    {%endregion peers}



    {%region peers + editing}

      n := 'Peers, TestText1: Before Edit';
      ReCreateEditForTreeTest(TestText1);
      FTestTree.ValidateRange(1, 36, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpElse(29, 1)) ]);
      CheckNodes(n, 5, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(17, 3)) ]);
      CheckNodes(n, 7, [ ExpN( 5,15, idnIfdef, EpSkip,      EpElse(7, 20)),
                         ExpN(20,27, idnElse,  EpIf(7, 5), EpEnd(7, 32)),
                         ExpN(32,40, idnEndIf, EpElse(7, 20)),
                         ExpN(43,53, idnIfdef, EpSkip,      EpEnd(11, 5))  ]);
      CheckNodes(n, 8, [ ExpN( 7,17, idnIfdef, EpSkip,      EpEnd(10, 7)),
                         ExpN(19,29, idnIfdef, EpSkip,      EpElse(8, 34)),
                         ExpN(34,41, idnElse,  EpIf(8, 19), EpEnd(8, 46)),
                         ExpN(46,54, idnEndIf, EpElse(8, 34))  ]);
      CheckNodes(n,10, [ ExpN( 7,15, idnEndIf, EpIf(8, 7)) ]);
      CheckNodes(n,11, [ ExpN( 5,13, idnEndIf, EpIf(7, 43)) ]);
      CheckNodes(n,13, [ ExpN( 5,16, idnIfdef, EpSkip,      EpEnd(14, 5)) ]);
      CheckNodes(n,14, [ ExpN( 5,13, idnEndIf, EpIf(13, 5)) ]);
      CheckNodes(n,15, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(15, 16)),
                         ExpN(16,24, idnEndIf, EpIf(15, 5))  ]);
      CheckNodes(n,17, [ ExpN( 3,11, idnEndIf, EpIf(5, 3)) ]);
      CheckNodes(n,19, [ ExpN( 3,14, idnIfdef, EpSkip,      EpElse(21, 3)) ]);
      CheckNodes(n,21, [ ExpN( 3,10, idnElse,  EpIf(19, 3), EpEnd(27, 3)) ]);
      CheckNodes(n,23, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(25, 5)) ]);
      CheckNodes(n,25, [ ExpN( 5,13, idnEndIf, EpIf(23, 5)) ]);
      CheckNodes(n,26, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(26, 16)),
                         ExpN(16,24, idnEndIf, EpIf(26, 5))  ]);
      CheckNodes(n,27, [ ExpN( 3,11, idnEndIf, EpElse(21, 3)) ]);
      CheckNodes(n,29, [ ExpN( 1, 8, idnElse,  EpIf(2, 1), EpEnd(36,1)) ]);
      CheckNodes(n,31, [ ExpN( 3,13, idnIfdef, EpSkip,      EpElse(32, 3)) ]);
      CheckNodes(n,32, [ ExpN( 3,10, idnElse,  EpIf(31, 3), EpEnd(34,3)) ]);
      CheckNodes(n,34, [ ExpN( 3,11, idnEndIf, EpElse(32, 3)) ]);
      CheckNodes(n,36, [ ExpN( 1, 9, idnEndIf, EpElse(29, 1)) ]);

      SynEdit.TextBetweenPoints[point(1, 9),point(1, 9)] := LineEnding;

      n := 'Peers, TestText1: Line inserted at 9';
      FTestTree.ValidateRange(1, 37, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpElse(30, 1)) ]);
      CheckNodes(n, 5, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(18, 3)) ]);
      CheckNodes(n, 7, [ ExpN( 5,15, idnIfdef, EpSkip,      EpElse(7, 20)),
                         ExpN(20,27, idnElse,  EpIf(7, 5), EpEnd(7, 32)),
                         ExpN(32,40, idnEndIf, EpElse(7, 20)),
                         ExpN(43,53, idnIfdef, EpSkip,      EpEnd(12, 5))  ]);
      CheckNodes(n, 8, [ ExpN( 7,17, idnIfdef, EpSkip,      EpEnd(11, 7)),
                         ExpN(19,29, idnIfdef, EpSkip,      EpElse(8, 34)),
                         ExpN(34,41, idnElse,  EpIf(8, 19), EpEnd(8, 46)),
                         ExpN(46,54, idnEndIf, EpElse(8, 34))  ]);
      CheckNodes(n,11, [ ExpN( 7,15, idnEndIf, EpIf(8, 7)) ]);
      CheckNodes(n,12, [ ExpN( 5,13, idnEndIf, EpIf(7, 43)) ]);
      CheckNodes(n,14, [ ExpN( 5,16, idnIfdef, EpSkip,      EpEnd(15, 5)) ]);
      CheckNodes(n,15, [ ExpN( 5,13, idnEndIf, EpIf(14, 5)) ]);
      CheckNodes(n,16, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(16, 16)),
                         ExpN(16,24, idnEndIf, EpIf(16, 5))  ]);
      CheckNodes(n,18, [ ExpN( 3,11, idnEndIf, EpIf(5, 3)) ]);
      CheckNodes(n,20, [ ExpN( 3,14, idnIfdef, EpSkip,      EpElse(22, 3)) ]);
      CheckNodes(n,22, [ ExpN( 3,10, idnElse,  EpIf(20, 3), EpEnd(28, 3)) ]);
      CheckNodes(n,24, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(26, 5)) ]);
      CheckNodes(n,26, [ ExpN( 5,13, idnEndIf, EpIf(24, 5)) ]);
      CheckNodes(n,27, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(27, 16)),
                         ExpN(16,24, idnEndIf, EpIf(27, 5))  ]);
      CheckNodes(n,28, [ ExpN( 3,11, idnEndIf, EpElse(22, 3)) ]);
      CheckNodes(n,30, [ ExpN( 1, 8, idnElse,  EpIf(2, 1), EpEnd(37,1)) ]);
      CheckNodes(n,32, [ ExpN( 3,13, idnIfdef, EpSkip,      EpElse(33, 3)) ]);
      CheckNodes(n,33, [ ExpN( 3,10, idnElse,  EpIf(32, 3), EpEnd(35,3)) ]);
      CheckNodes(n,35, [ ExpN( 3,11, idnEndIf, EpElse(33, 3)) ]);
      CheckNodes(n,37, [ ExpN( 1, 9, idnEndIf, EpElse(30, 1)) ]);

      SynEdit.TextBetweenPoints[point(1, 9),point(1, 9)] := '{$EndIf}';

      n := 'Peers, TestText1: ENDIF inserted at 9';
      FTestTree.ValidateRange(1, 37, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(18, 3)) ]);
      CheckNodes(n, 5, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(12, 5)) ]);
      CheckNodes(n, 7, [ ExpN( 5,15, idnIfdef, EpSkip,      EpElse(7, 20)),
                         ExpN(20,27, idnElse,  EpIf(7, 5), EpEnd(7, 32)),
                         ExpN(32,40, idnEndIf, EpElse(7, 20)),
                         ExpN(43,53, idnIfdef, EpSkip,      EpEnd(11, 7))  ]);
      CheckNodes(n, 8, [ ExpN( 7,17, idnIfdef, EpSkip,      EpEnd(9, 1)),
                         ExpN(19,29, idnIfdef, EpSkip,      EpElse(8, 34)),
                         ExpN(34,41, idnElse,  EpIf(8, 19), EpEnd(8, 46)),
                         ExpN(46,54, idnEndIf, EpElse(8, 34))  ]);
      CheckNodes(n,11, [ ExpN( 7,15, idnEndIf, EpIf(7, 43)) ]);
      CheckNodes(n,12, [ ExpN( 5,13, idnEndIf, EpIf(5, 3)) ]);
      CheckNodes(n,14, [ ExpN( 5,16, idnIfdef, EpSkip,      EpEnd(15, 5)) ]);
      CheckNodes(n,15, [ ExpN( 5,13, idnEndIf, EpIf(14, 5)) ]);
      CheckNodes(n,16, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(16, 16)),
                         ExpN(16,24, idnEndIf, EpIf(16, 5))  ]);
      CheckNodes(n,18, [ ExpN( 3,11, idnEndIf, EpIf(2, 1)) ]);
      CheckNodes(n,20, [ ExpN( 3,14, idnIfdef, EpSkip,      EpElse(22, 3)) ]);
      CheckNodes(n,22, [ ExpN( 3,10, idnElse,  EpIf(20, 3), EpEnd(28, 3)) ]);
      CheckNodes(n,24, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(26, 5)) ]);
      CheckNodes(n,26, [ ExpN( 5,13, idnEndIf, EpIf(24, 5)) ]);
      CheckNodes(n,27, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(27, 16)),
                         ExpN(16,24, idnEndIf, EpIf(27, 5))  ]);
      CheckNodes(n,28, [ ExpN( 3,11, idnEndIf, EpElse(22, 3)) ]);
      CheckNodes(n,30, [ ExpN( 1, 8, idnElse,  EpNil,       EpEnd(37,1)) ]);
      CheckNodes(n,32, [ ExpN( 3,13, idnIfdef, EpSkip,      EpElse(33, 3)) ]);
      CheckNodes(n,33, [ ExpN( 3,10, idnElse,  EpIf(32, 3), EpEnd(35,3)) ]);
      CheckNodes(n,35, [ ExpN( 3,11, idnEndIf, EpElse(33, 3)) ]);
      CheckNodes(n,37, [ ExpN( 1, 9, idnEndIf, EpElse(30, 1)) ]);


      SynEdit.TextBetweenPoints[point(1, 3),point(1, 3)] := '{$Else}';
      FTestTree.ValidateRange(1, 37, FOpenings);

      //SynEdit.TextBetweenPoints[point(1, 3),point(1, 8)] := '';
      SynEdit.TextBetweenPoints[point(1, 3),point(8, 3)] := '';
      FTestTree.ValidateRange(1, 37, FOpenings);
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(18, 3)) ]);
      CheckNodes(n, 5, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(12, 5)) ]);
      CheckNodes(n, 7, [ ExpN( 5,15, idnIfdef, EpSkip,      EpElse(7, 20)),
                         ExpN(20,27, idnElse,  EpIf(7, 5), EpEnd(7, 32)),
                         ExpN(32,40, idnEndIf, EpElse(7, 20)),
                         ExpN(43,53, idnIfdef, EpSkip,      EpEnd(11, 7))  ]);
      CheckNodes(n, 8, [ ExpN( 7,17, idnIfdef, EpSkip,      EpEnd(9, 1)),
                         ExpN(19,29, idnIfdef, EpSkip,      EpElse(8, 34)),
                         ExpN(34,41, idnElse,  EpIf(8, 19), EpEnd(8, 46)),
                         ExpN(46,54, idnEndIf, EpElse(8, 34))  ]);
      CheckNodes(n,11, [ ExpN( 7,15, idnEndIf, EpIf(7, 43)) ]);
      CheckNodes(n,12, [ ExpN( 5,13, idnEndIf, EpIf(5, 3)) ]);
      CheckNodes(n,14, [ ExpN( 5,16, idnIfdef, EpSkip,      EpEnd(15, 5)) ]);
      CheckNodes(n,15, [ ExpN( 5,13, idnEndIf, EpIf(14, 5)) ]);
      CheckNodes(n,16, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(16, 16)),
                         ExpN(16,24, idnEndIf, EpIf(16, 5))  ]);
      CheckNodes(n,18, [ ExpN( 3,11, idnEndIf, EpIf(2, 1)) ]);
      CheckNodes(n,20, [ ExpN( 3,14, idnIfdef, EpSkip,      EpElse(22, 3)) ]);
      CheckNodes(n,22, [ ExpN( 3,10, idnElse,  EpIf(20, 3), EpEnd(28, 3)) ]);
      CheckNodes(n,24, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(26, 5)) ]);
      CheckNodes(n,26, [ ExpN( 5,13, idnEndIf, EpIf(24, 5)) ]);
      CheckNodes(n,27, [ ExpN( 5,15, idnIfdef, EpSkip,      EpEnd(27, 16)),
                         ExpN(16,24, idnEndIf, EpIf(27, 5))  ]);
      CheckNodes(n,28, [ ExpN( 3,11, idnEndIf, EpElse(22, 3)) ]);
      CheckNodes(n,30, [ ExpN( 1, 8, idnElse,  EpNil,       EpEnd(37,1)) ]);
      CheckNodes(n,32, [ ExpN( 3,13, idnIfdef, EpSkip,      EpElse(33, 3)) ]);
      CheckNodes(n,33, [ ExpN( 3,10, idnElse,  EpIf(32, 3), EpEnd(35,3)) ]);
      CheckNodes(n,35, [ ExpN( 3,11, idnEndIf, EpElse(33, 3)) ]);
      CheckNodes(n,37, [ ExpN( 1, 9, idnEndIf, EpElse(30, 1)) ]);



      {%region remove endif by edit // Test nodes delete => resolve peer}
        n := 'remove endif by edit / edit endif (one endif in line)';
        ReCreateEditForTreeTest(TestText3);
        FTestTree.ValidateRange(1, 8, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(4, 1)) ]);
        CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf, EpIf(2, 1)) ]);

        SynEdit.TextBetweenPoints[point(3, 4),point(3, 4)] := ' ';
        FTestTree.ValidateRange(1, 8, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpNil) ]);
        CheckNodes(n, 4, [ ]);


        n := 'remove endif by edit / edit endif (two endif in line, edit first)';
        ReCreateEditForTreeTest(TestText3a);
        FTestTree.ValidateRange(1, 8, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(4,10)),
                           ExpN(12,22, idnIfdef, EpSkip,      EpEnd(4, 1)) ]);
        CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf, EpIf(2,12)),
                           ExpN(10,18, idnEndIf, EpIf(2, 1)) ]);

        SynEdit.TextBetweenPoints[point(3, 4),point(3, 4)] := ' ';
        FTestTree.ValidateRange(1, 8, FOpenings);
        //FTestTree.DebugPrint(true);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpNil),
                           ExpN(12,22, idnIfdef, EpSkip,      EpEnd(4,11)) ]);
        CheckNodes(n, 4, [ ExpN(11,19, idnEndIf, EpIf(2, 12)) ]);


        n := 'remove endif by edit / edit endif (two endif in line, edit 2nd)';
        ReCreateEditForTreeTest(TestText3a);
        FTestTree.ValidateRange(1, 8, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(4,10)),
                           ExpN(12,22, idnIfdef, EpSkip,      EpEnd(4, 1)) ]);
        CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf, EpIf(2,12)),
                           ExpN(10,18, idnEndIf, EpIf(2, 1)) ]);

        SynEdit.TextBetweenPoints[point(13,4),point(13,4)] := ' ';
        FTestTree.ValidateRange(1, 8, FOpenings);
        FTestTree.DebugPrint(true);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpNil),
                           ExpN(12,22, idnIfdef, EpSkip,      EpEnd(4, 1)) ]);
        CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf, EpIf(2,12)) ]);


      {%endregion}

      {%region Add outer lines by removing endif}
        // No outer lines to begin

        n := 'Extend outer lines by removing endif - BUT new peer for new outer ifdef is PAST visible (not scanned)';
        ReCreateEditForTreeTest(TestText7);
        FTestTree.ValidateRange(1, 16, FOpenings); // scan all, so the nodes to exist
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(4, 1)) ]);
        CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf, EpIf(2, 1)) ]);
        CheckNodes(n,10, [ ExpN( 2,12, idnIfdef, EpSkip,      EpEnd(14, 2)) ]);
        CheckNodes(n,14, [ ExpN( 2,10, idnEndIf, EpIf(10, 2)) ]);

        SynEdit.TextBetweenPoints[point(4,4),point(4,4)] := ' '; // remove ifdef (leaving invalid node)
        FOpenings.Clear;
        FTestTree.ValidateRange(6, 8, FOpenings); // scan empty text
        // Expect outer node to be changed
  // This is siped by markup
        //CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpNil) ]);



        n := 'Extend outer lines by removing endif - new endif in visible range';
        ReCreateEditForTreeTest(TestText7);
        FTestTree.ValidateRange(1, 16, FOpenings); // scan all, so the nodes to exist
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(4, 1)) ]);
        CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf, EpIf(2, 1)) ]);
        CheckNodes(n,10, [ ExpN( 2,12, idnIfdef, EpSkip,      EpEnd(14, 2)) ]);
        CheckNodes(n,14, [ ExpN( 2,10, idnEndIf, EpIf(10, 2)) ]);

        SynEdit.TextBetweenPoints[point(4,10),point(4,10)] := ' '; // remove ifdef so we have an extra endif
        FOpenings.Clear;
        FTestTree.ValidateRange(1, 16, FOpenings); // scan all, so the nodes to exist
  FTestTree.DebugPrint(true);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(4, 1)) ]);
        CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf, EpIf(2, 1)) ]);
        CheckNodes(n,10, [ ]);
        CheckNodes(n,14, [ ExpN( 2,10, idnEndIf, EpNil) ]);

        SynEdit.TextBetweenPoints[point(4,4),point(4,4)] := ' '; // remove ifdef (leaving invalid node)
        FOpenings.Clear;
        FTestTree.ValidateRange(6, 16, FOpenings); // scan empty text
  FTestTree.DebugPrint(true);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(14, 2)) ]);
  //      CheckNodes(n, 4, [ ]);
        CheckNodes(n,10, [ ]);
        CheckNodes(n,14, [ ExpN( 2,10, idnEndIf, EpIf(2, 1)) ]);


      {%endregion}


    {%endregion peers + edit}


    {%region UNMATCHED PEERS}

      // NO EDIT
      n := 'Peers, TestText2: Bad nodes';
      ReCreateEditForTreeTest(TestText2);
      FTestTree.ValidateRange(1, 18, FOpenings);
      // ONe and only one of the 2 ends should have a peer
      CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(4, 1)) ]);
      CheckNodes(n, 4, [ ExpN( 1, 9, idnEndIf, EpIf(2, 1)) ]);
      CheckNodes(n, 6, [ ExpN( 1, 9, idnEndIf, EpNil) ]); // must not have a peer
      // One and only one else may be connected to if and one (but maybe the other) to endif
      CheckNodes(n,10, [ ExpN( 1,11, idnIfdef) ]); // EpElse(12, 1) // or 14
      CheckNodes(n,12, [ ExpN( 1, 8, idnElse) ]);
      CheckNodes(n,14, [ ExpN( 1, 8, idnElse) ]);
      CheckNodes(n,16, [ ExpN( 1, 9, idnEndIf, EpElse(14, 1)) ]);


      {%region  Insert If/end to create invalid peering, that must be resolved }
        n := 'Peers, TestText6: Resolve left-over binding: ' +
             'Insert Ifdef in visible part, with node inbetween';
        ReCreateEditForTreeTest(TestText6);
        FTestTree.ValidateRange(1, 20, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpNil ) ]);
        CheckNodes(n, 6, [ ExpN( 2,12, idnIfdef, EpSkip,      EpEnd(18, 2)) ]);
        CheckNodes(n,10, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(14, 3)) ]);
        CheckNodes(n,14, [ ExpN( 3,11, idnEndIf, EpIf(10, 3)) ]);
        CheckNodes(n,18, [ ExpN( 2,10, idnEndIf, EpIf( 6, 2)) ]);

        SynEdit.TextBetweenPoints[point(1, 8),point(1, 8)] := '   {$IfDef X}';
        FTestTree.ValidateRange(1, 20, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpNil ) ]);
        CheckNodes(n, 6, [ ExpN( 2,12, idnIfdef, EpSkip,      EpNil ) ]);
        CheckNodes(n, 8, [ ExpN( 4,14, idnIfdef, EpSkip,      EpEnd(18, 2)) ]);
        CheckNodes(n,10, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(14, 3)) ]);
        CheckNodes(n,14, [ ExpN( 3,11, idnEndIf, EpIf(10, 3)) ]);
        CheckNodes(n,18, [ ExpN( 2,10, idnEndIf, EpIf( 8, 4)) ]);

        n := n + ' Remove again';
        SynEdit.TextBetweenPoints[point(1, 8),point(14, 8)] := '';
        FTestTree.ValidateRange(1, 20, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpNil ) ]);
        CheckNodes(n, 6, [ ExpN( 2,12, idnIfdef, EpSkip,      EpEnd(18, 2)) ]);
        CheckNodes(n,10, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(14, 3)) ]);
        CheckNodes(n,14, [ ExpN( 3,11, idnEndIf, EpIf(10, 3)) ]);
        CheckNodes(n,18, [ ExpN( 2,10, idnEndIf, EpIf( 6, 2)) ]);
      {%endregion  Insert If/end to create invalid peering, that must be resolved }

      {%region  Insert If/end to create invalid peering, that must be resolved }
        n := 'Peers, TestText6: Resolve left-over binding: ' +
             'Insert EndIf in visible part, with node inbetween';
        ReCreateEditForTreeTest(TestText6);
        FTestTree.ValidateRange(1, 20, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpNil ) ]);
        CheckNodes(n, 6, [ ExpN( 2,12, idnIfdef, EpSkip,      EpEnd(18, 2)) ]);
        CheckNodes(n,10, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(14, 3)) ]);
        CheckNodes(n,14, [ ExpN( 3,11, idnEndIf, EpIf(10, 3)) ]);
        CheckNodes(n,18, [ ExpN( 2,10, idnEndIf, EpIf( 6, 2)) ]);

        SynEdit.TextBetweenPoints[point(1, 8),point(1, 8)] := '   {$Endif}';
        FTestTree.ValidateRange(1, 20, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(18, 2)) ]);
        CheckNodes(n, 6, [ ExpN( 2,12, idnIfdef, EpSkip,      EpEnd( 8, 4)) ]);
        CheckNodes(n, 8, [ ExpN( 4,12, idnEndIf, EpIf( 6, 2)) ]);
        CheckNodes(n,10, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(14, 3)) ]);
        CheckNodes(n,14, [ ExpN( 3,11, idnEndIf, EpIf(10, 3)) ]);
        CheckNodes(n,18, [ ExpN( 2,10, idnEndIf, EpIf( 2, 1)) ]);

        n := n + ' Remove again';
        SynEdit.TextBetweenPoints[point(1, 8),point(14, 8)] := '';
        FTestTree.ValidateRange(1, 20, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpNil ) ]);
        CheckNodes(n, 6, [ ExpN( 2,12, idnIfdef, EpSkip,      EpEnd(18, 2)) ]);
        CheckNodes(n,10, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(14, 3)) ]);
        CheckNodes(n,14, [ ExpN( 3,11, idnEndIf, EpIf(10, 3)) ]);
        CheckNodes(n,18, [ ExpN( 2,10, idnEndIf, EpIf( 6, 2)) ]);
      {%endregion  Insert If/end to create invalid peering, that must be resolved }

      {%region  Insert If/end to create invalid peering, that must be resolved }
        n := 'Peers, TestText6: Relosve left-over binding: ' +
             'Insert Ifdef in visible part, with node inbetween AT end of outer ifdef';
        ReCreateEditForTreeTest(TestText6);
        FTestTree.ValidateRange(1, 20, FOpenings);

        SynEdit.TextBetweenPoints[point(12, 6),point(12, 6)] := '   {$IfDef X}';
        FTestTree.ValidateRange(1, 20, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpNil ) ]);
        CheckNodes(n, 6, [ ExpN( 2,12, idnIfdef, EpSkip,      EpNil ),
                           ExpN(15,25, idnIfdef, EpSkip,      EpEnd(18, 2))
                         ]);
        CheckNodes(n,10, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(14, 3)) ]);
        CheckNodes(n,14, [ ExpN( 3,11, idnEndIf, EpIf(10, 3)) ]);
        CheckNodes(n,18, [ ExpN( 2,10, idnEndIf, EpIf( 6, 15)) ]);

        n := n + ' Remove again';
        SynEdit.TextBetweenPoints[point(12, 6),point(25, 6)] := '';
        FTestTree.ValidateRange(1, 20, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpNil ) ]);
        CheckNodes(n, 6, [ ExpN( 2,12, idnIfdef, EpSkip,      EpEnd(18, 2)) ]);
        CheckNodes(n,10, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(14, 3)) ]);
        CheckNodes(n,14, [ ExpN( 3,11, idnEndIf, EpIf(10, 3)) ]);
        CheckNodes(n,18, [ ExpN( 2,10, idnEndIf, EpIf( 6, 2)) ]);
      {%endregion  Insert If/end to create invalid peering, that must be resolved }

      {%region  Insert If/end to create invalid peering, that must be resolved }
        n := 'Peers, TestText6: Relosve left-over binding: ' +
             'Insert EndIf in visible part, with node inbetween AT end of outer ifdef';
        ReCreateEditForTreeTest(TestText6);
        FTestTree.ValidateRange(1, 20, FOpenings);

        SynEdit.TextBetweenPoints[point(12, 6),point(12, 6)] := '   {$EndIf}';
        FTestTree.ValidateRange(1, 20, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpEnd(18, 2) ) ]);
        CheckNodes(n, 6, [ ExpN( 2,12, idnIfdef, EpSkip,      EpEnd( 6,15) ),
                           ExpN(15,23, idnEndIf, EpIf( 6, 2))
                         ]);
        CheckNodes(n,10, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(14, 3)) ]);
        CheckNodes(n,14, [ ExpN( 3,11, idnEndIf, EpIf(10, 3)) ]);
        CheckNodes(n,18, [ ExpN( 2,10, idnEndIf, EpIf( 2, 1)) ]);

        n := n + ' Remove again';
        SynEdit.TextBetweenPoints[point(12, 6),point(25, 6)] := '';
        FTestTree.ValidateRange(1, 20, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, EpSkip,      EpNil ) ]);
        CheckNodes(n, 6, [ ExpN( 2,12, idnIfdef, EpSkip,      EpEnd(18, 2)) ]);
        CheckNodes(n,10, [ ExpN( 3,13, idnIfdef, EpSkip,      EpEnd(14, 3)) ]);
        CheckNodes(n,14, [ ExpN( 3,11, idnEndIf, EpIf(10, 3)) ]);
        CheckNodes(n,18, [ ExpN( 2,10, idnEndIf, EpIf( 6, 2)) ]);
      {%endregion  Insert If/end to create invalid peering, that must be resolved }


      {%region ELSE WITHOUT IFDEF and DOUBLE ELSE }
  // TODO: Else and ifdef on same line
        for i := 0 to 2 do begin
          for i2 := 0 to 7 do
          for i3 := 0 to 2 do // ifdef
          for i4 := 0 to 1 do // endif TODO
          begin
            case i of
              0: begin
                PushBaseName('Scan empty, then add else at line 3 (after scan-start-marker)');
                ReCreateEditForTreeTest(TestTextNoIfDef);
                FTestTree.ValidateRange(1, 9, FOpenings);
              end;
              1: begin
                PushBaseName('Scan empty (form 3), then add else at line 3 (AT scan-start-marker)');
                ReCreateEditForTreeTest(TestTextNoIfDef);
                FTestTree.ValidateRange(3, 9, FOpenings);
              end;
              2:begin
                PushBaseName('Add else (before first scan) at line 3');
                ReCreateEditForTreeTest(TestTextNoIfDef);
              end;
            end;
            //debugln(['# ', i, ' ', i2, ' ', i3, ' ', i4]);

            SynEdit.TextBetweenPoints[point(1, 3),point(1, 3)] := '                    {$ELSE}                      //';
            FTestTree.ValidateRange(1, 9, FOpenings);
            CheckNodes(n, 3, [ ExpN(21,28, idnElse, EpNil,      EpNil ) ]);

            SynEdit.TextBetweenPoints[point(1, 3),point(28, 3)] := '';
            FTestTree.ValidateRange(1, 9, FOpenings);
            CheckNodes(n+'undone', 3, [ ]);

            SynEdit.TextBetweenPoints[point(1, 3),point(1, 3)] := '                    {$ELSE}                      //';
            FTestTree.ValidateRange(1, 9, FOpenings);
            CheckNodes(n+'redone', 3, [ ExpN(21,28, idnElse, EpNil,      EpNil ) ]);

            n := '['+IntToStr(i2)+']';

            // Maybe ifdef
            epMaybeIf := EpNil;
            epMaybeIf^.NoAutoFree := True;
            if i3 >= 1 then begin
              n := n + 'Insert leading IFDEF';
              SynEdit.TextBetweenPoints[point(1, 1),point(1, 1)] := '{$IFDEF a}';
              if i4 = 2 then begin
                n := n + '(scanned)';
                FTestTree.ValidateRange(1, 9, FOpenings);
                CheckNodes(n+'ifdef', 1, [ ExpN( 1,11, idnIfdef, EpNil,     EpElse(3,21) ) ]);
                CheckNodes(n+'ifdef', 3, [ ExpN(21,28, idnElse,  EpIf(1,1), EpNil ) ]);
              end;
              Dispose(epMaybeIf);
              epMaybeIf := EpIf(1,1);
              epMaybeIf^.NoAutoFree := True;
            end;

            if (i2 and 1) = 0 then begin
              // 2nd before first
              SynEdit.TextBetweenPoints[point(1, 2),point(1, 2)] := ' {$ELSE}';
              FTestTree.ValidateRange(1, 9, FOpenings);
              if i3 >= 1 then
                CheckNodes(n+'2nd else before 1st', 1, [ ExpN( 1,11, idnIfdef, EpNil,     EpElse(2,2) ) ]);
              CheckNodes(n+'2nd else before 1st', 2, [ ExpN( 2, 9, idnElse, epMaybeIf,  EpNil ) ]);
              CheckNodes(n+'2nd else before 1st', 3, [ ExpN(21,28, idnElse, EpNil,      EpNil ) ]);

              SynEdit.TextBetweenPoints[point(1, 2),point(9, 2)] := '';
              FTestTree.ValidateRange(1, 9, FOpenings);
              if i3 >= 1 then
                CheckNodes(n+'undone 2nd before 1st', 1, [ ExpN( 1,11, idnIfdef, EpNil,     EpElse(3,21) ) ]);
              CheckNodes(n+'undone 2nd before 1st', 2, [ ]);
              CheckNodes(n+'undone 2nd before 1st', 3, [ ExpN(21,28, idnElse, epMaybeIf, EpNil ) ]);
            end;

            if (i2 and 2) = 0 then begin
              // 2nd after first
              SynEdit.TextBetweenPoints[point(1, 4),point(1, 4)] := ' {$ELSE}';
              FTestTree.ValidateRange(1, 9, FOpenings);
              if i3 >= 1 then
                CheckNodes(n+'2nd after 1st', 1, [ ExpN( 1,11, idnIfdef, EpNil,     EpElse(3,21) ) ]);
              CheckNodes(n+'2nd else after 1st', 3, [ ExpN(21,28, idnElse, epMaybeIf,  EpNil ) ]);
              CheckNodes(n+'2nd else after 1st', 4, [ ExpN( 2, 9, idnElse, EpNil,      EpNil ) ]);

              SynEdit.TextBetweenPoints[point(1, 4),point(9, 4)] := '';
              FTestTree.ValidateRange(1, 9, FOpenings);
              if i3 >= 1 then
                CheckNodes(n+'undone 2nd after 1st', 1, [ ExpN( 1,11, idnIfdef, EpNil,     EpElse(3,21) ) ]);
              CheckNodes(n+'undone 2nd after 1st', 3, [ ExpN(21,28, idnElse, epMaybeIf,  EpNil ) ]);
              CheckNodes(n+'undone 2nd after 1st', 4, [ ]);
            end;

            if (i2 and 3) = 0 then begin
              // 2nd before first (same line)
              SynEdit.TextBetweenPoints[point(11, 3),point(18, 3)] := '{$ELSE}';
              FTestTree.ValidateRange(1, 9, FOpenings);
              if i3 >= 1 then
                CheckNodes(n+'2nd before 1st (same line)', 1, [ ExpN( 1,11, idnIfdef, EpNil,   EpElse(3,11) ) ]);
              CheckNodes(n+'2nd else before 1st (same line)', 3, [
                ExpN(11,18, idnElse, epMaybeIf,  EpNil ),
                ExpN(21,28, idnElse, EpNil,      EpNil )
                ]);

              SynEdit.TextBetweenPoints[point(11, 3),point(18, 3)] := '       ';
              FTestTree.ValidateRange(1, 9, FOpenings);
              if i3 >= 1 then
                CheckNodes(n+'undone 2nd before 1st (same line)', 1, [ ExpN( 1,11, idnIfdef, EpNil,     EpElse(3,21) ) ]);
              CheckNodes(n+'undone 2nd before 1st (same line)', 3, [ ExpN(21,28, idnElse, epMaybeIf, EpNil ) ]);
            end;

            // 2nd after first (same line)
            SynEdit.TextBetweenPoints[point(31, 3),point(38, 3)] := '{$ELSE}';
            FTestTree.ValidateRange(1, 9, FOpenings);
            if i3 >= 1 then
              CheckNodes(n+'2nd after 1st (same line)', 1, [ ExpN( 1,11, idnIfdef, EpNil,     EpElse(3,21) ) ]);
            CheckNodes(n+'2nd else after 1st (same line)', 3, [
              ExpN(21,28, idnElse, epMaybeIf,  EpNil ),
              ExpN(31,38, idnElse, EpNil,      EpNil )
              ]);

            SynEdit.TextBetweenPoints[point(31, 3),point(38, 3)] := '       ';
            FTestTree.ValidateRange(1, 9, FOpenings);
            if i3 >= 1 then
              CheckNodes(n+'undone 2nd after 1st (same line)', 1, [ ExpN( 1,11, idnIfdef, EpNil,     EpElse(3,21) ) ]);
            CheckNodes(n+'undone 2nd after 1st (same line)', 3, [ ExpN(21,28, idnElse, epMaybeIf,  EpNil ) ]);

            Dispose(epMaybeIf);
            PopBaseName;
          end;
        end;
      {%endregion ELSE WITHOUT IFDEF and DOUBLE ELSE }




      {%region  }
        n := 'P';
        ReCreateEditForTreeTest(TestText2);
        FTestTree.ValidateRange(1, 24, FOpenings);
        FTestTree.ValidateRange(1, 24, FOpenings);

      {%endregion   }


    {%endregion UNMATCHED PEERS}


      {%region }
        n := 'scan new unfinished node, next node will be comment';
        ReCreateEditForTreeTest(TestText4);
        SynEdit.TextBetweenPoints[point(1, 3),point(1, 3)] := LineEnding;
        FTestTree.ValidateRange(1, 6, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef), ExpN(13,21, idnEndIf) ]);
        CheckNodes(n, 5, [ ExpN( 1,11, idnIfdef), ExpN(13,21, idnEndIf) ]);

        SynEdit.TextBetweenPoints[point(1, 3),point(1, 3)] := '   {$IFDEF ';
        FTestTree.ValidateRange(3, 4, FOpenings);

        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef), ExpN(13,21, idnEndIf) ]);
        if FUseNestedComments
        then CheckNodes(n, 3, [ ExpN( 4, 0, 4, idnIfdef) ]) // end-column does not exist
        else CheckNodes(n, 3, [ ExpN( 4,11, 2, idnIfdef) ]); // end-column does not exist
      {%endregion  }

      {%region }
        n := 'scan new unfinished node, next node will be comment';
        ReCreateEditForTreeTest(TestText4);
        SynEdit.TextBetweenPoints[point(1, 3),point(1, 3)] := LineEnding;
        FTestTree.ValidateRange(1, 6, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef), ExpN(13,21, idnEndIf) ]);
        CheckNodes(n, 5, [ ExpN( 1,11, idnIfdef), ExpN(13,21, idnEndIf) ]);

        SynEdit.TextBetweenPoints[point(1, 3),point(1, 3)] := '   {$IFDEF ';
        FTestTree.ValidateRange(1, 4, FOpenings);

        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef), ExpN(13,21, idnEndIf) ]);
        if FUseNestedComments
        then CheckNodes(n, 3, [ ExpN( 4, 0, 4, idnIfdef) ]) // end-column does not exist
        else CheckNodes(n, 3, [ ExpN( 4,11, 2, idnIfdef) ]); // end-column does not exist
      {%endregion  }


      {%region }
        n := 'scan new unfinished node, next node will be comment';
        ReCreateEditForTreeTest(TestText4);
        FTestTree.ValidateRange(1, 6, FOpenings);
        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef), ExpN(13,21, idnEndIf) ]);
        CheckNodes(n, 4, [ ExpN( 1,11, idnIfdef), ExpN(13,21, idnEndIf) ]);

        SynEdit.TextBetweenPoints[point(1, 3),point(1, 3)] := '   {$IFDEF ';
        FTestTree.ValidateRange(1, 3, FOpenings);

        CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef), ExpN(13,21, idnEndIf) ]);
        if FUseNestedComments
        then CheckNodes(n, 3, [ ExpN( 4, 0, 3, idnIfdef) ]) // end-column does not exist
        else CheckNodes(n, 3, [ ExpN( 4,11, 1, idnIfdef) ]); // end-column does not exist
      {%endregion  }

      {%region }
        n := 'multi elseif open';
        ReCreateEditForTreeTest(TestText9);
        FTestTree.ValidateRange(1, 18, FOpenings);
        //CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef), ExpN(13,21, idnEndIf) ]);


        //n := '';
        ReCreateEditForTreeTest(TestText9);
        FTestTree.ValidateRange(11, 18, FOpenings);
        //CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef), ExpN(13,21, idnEndIf) ]);


      {%endregion  }



      {%region }
        n := '';
        ReCreateEditForTreeTest(TestText10);
        FTestTree.ValidateRange(1, 9, FOpenings);
        FTestTree.SetNodeState(7, 1, idnDisabled);
        //CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef), ExpN(13,21, idnEndIf) ]);

        SynEdit.TextBetweenPoints[point(1, 4),point(1, 4)] := '{$endi';
        FTestTree.ValidateRange(1, 9, FOpenings);

        SynEdit.TextBetweenPoints[point(7, 4),point(7, 4)] := 'f';
        FTestTree.ValidateRange(1, 9, FOpenings);

        SynEdit.TextBetweenPoints[point(12, 4),point(12, 4)] := '} ';
        FTestTree.ValidateRange(1, 9, FOpenings);


      {%endregion  }

      {%region }
        for i := 1 to 34 do begin
          n := 'TestText11a elseif ' + IntToStr(i);
          ReCreateEditForTreeTest(TestText11a);
          FTestTree.ValidateRange(i, 35, FOpenings);

          n := 'TestText11a elseif ' + IntToStr(i);
          ReCreateEditForTreeTest(TestText11a);
          FTestTree.ValidateRange(i, i+1, FOpenings);

          n := 'TestText11a elseif ' + IntToStr(i);
          ReCreateEditForTreeTest(TestText11a);
          FTestTree.ValidateRange(i, i+2, FOpenings);
        end;

        n := 'TestText11a elseif 12-30';
        ReCreateEditForTreeTest(TestText11a);
        FTestTree.ValidateRange(11, 30, FOpenings);
        CheckNodes(n, 1, [ ExpN( 1,21, idnIfdef)]);
        //CheckNodes(n, 2, [ ExpN( 1,21, idnIfdef)]);
        CheckNodes(n, 11, [ ExpN( 3,11, idnEndIf,  EpIf(10,13), EpNil),
                            ExpN(11,39, idnElseIf, EpElseIf(8, 3), EpElse(21,3))   ]);
      {%endregion  }

      {%region  issue 0025811 }

        n := 'issue 0025811';
        ReCreateEditForTreeTest(TestText12);
        FTestTree.ValidateRange( 7, 10, FOpenings);
        CheckNodes(n, 6, [ ExpN( 13,21, idnSkipTest), ExpN(22,30, idnSkipTest),
                           ExpN(31, 48, idnIfdef, EpNil, EpEnd(7, 13)) ]);
        CheckNodes(n, 7, [ ExpN( 13,21, idnEndIf, EpIf(6, 31), EpNil) ]);

        ReCreateEditForTreeTest(TestText12);
        FTestTree.ValidateRange( 5, 10, FOpenings);
        CheckNodes(n, 5, [ ExpN( 6,19, idnIfdef, EpNil, EpEnd(6,22)),
                           ExpN(20,40, idnIfdef, EpNil, EpEnd(6,13)) ]);
        CheckNodes(n, 6, [ ExpN(13,21, idnEndIf, EpIf(5,20), EpNil),
                           ExpN(22,30, idnEndIf, EpIf(5, 6), EpNil),
                           ExpN(31, 48, idnIfdef, EpNil, EpEnd(7, 13)) ]);
        CheckNodes(n, 7, [ ExpN( 13,21, idnEndIf, EpIf(6, 31), EpNil) ]);

        ReCreateEditForTreeTest(TestText12);
        FTestTree.ValidateRange( 6, 10, FOpenings);
        CheckNodes(n, 5, [ ExpN( 6,19, idnIfdef, EpNil, EpEnd(6,22)),
                           ExpN(20,40, idnIfdef, EpNil, EpEnd(6,13)) ]);
        CheckNodes(n, 6, [ ExpN(13,21, idnEndIf, EpIf(5,20), EpNil),
                           ExpN(22,30, idnEndIf, EpIf(5, 6), EpNil),
                           ExpN(31, 48, idnIfdef, EpNil, EpEnd(7, 13)) ]);
        CheckNodes(n, 7, [ ExpN( 13,21, idnEndIf, EpIf(6, 31), EpNil) ]);

        ReCreateEditForTreeTest(TestText12);
        FTestTree.ValidateRange( 8, 10, FOpenings);

      {%endregion   }



    FTestTree.DiscardOpeningList(FOpenings);
    FOpenings := nil;
    FTestTree.Free;
  end;
begin
  FUseNestedComments := False;
  DoTest;
  FUseNestedComments := True;
  DoTest;
end;

function TTestMarkupIfDef.TesTNodeStateHandler(Sender: TObject; LinePos, XStartPos: Integer;
  CurrentState: TSynMarkupIfdefNodeStateEx): TSynMarkupIfdefNodeState;
var
  n, v: String;
begin
  n := Format('%d/%d', [LinePos, XStartPos]);
  v := FNodeStateRequests.Values[n];
  v := IntToStr(StrToIntDef(v, 0) + 1);
  FNodeStateRequests.Values[n] := v;

  v := FNodeStateResponses.Values[n];
  if v = '' then
    Result := idnInvalid
  else
    Result := TSynMarkupIfdefNodeState(StrToIntDef(v, 0));

  //DebugLn('# TesTNodeStateHandler ', n, ' # ', v, ' ', dbgs(Result));
end;

procedure TTestMarkupIfDef.TestIfDefTreeNodeState;

  procedure ClearData;
  begin
    FNodeStateResponses.Clear;
    FNodeStateRequests.Clear;
  end;

  procedure AddResponses(R: Array of integer);
  var
    i: Integer;
  begin
    for i := 0 to (length(R) div 3) - 1 do
      FNodeStateResponses.Values[Format('%d/%d', [R[i*3], R[I*3+1]])] := IntToStr(R[I*3+2]);
  end;

  procedure CheckReq(n: String; R: Array of integer);
  var
    i: Integer;
    s, s2: String;
  begin
    AssertEquals(n + 'count requests ' , length(R) div 3, FNodeStateRequests.Count);
    for i := 0 to (length(R) div 3) - 1 do begin
      s := Format('%d/%d', [R[i*3], R[I*3+1]]);
      s2 := IntToStr(R[I*3+2]);
      if R[I*3+2] = 0 then s2 := '';
      AssertEquals(n + 'Got reqest for '+s , s2, FNodeStateRequests.Values[s]);
    end;
  end;

  procedure SetupTest(Lines: Array of String);
  begin
    ReCreateEditForTreeTest(Lines);
    FTestTree.OnNodeStateRequest := @TesTNodeStateHandler;
  end;

var
  n: String;
begin
  FTestTree := nil;
  FNodeStateResponses := TStringList.Create;
  FNodeStateRequests := TStringList.Create;

  {%region  Simple Ifdef + edit: get node request }
    SetupTest(TestText3);
    // *** Scan
    n := 'TestText3 scan;';
    ClearData;
    AddResponses([ 2, 1, ord(idnEnabled) ]);

    FTestTree.ValidateRange(1, 10, FOpenings);
    CheckReq(n, [ 2, 1, 1 ]);
    CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, idnEnabled) ]);


    // *** Edit Node, trigger new request
    n := 'TestText3 edit existing ifdef;';
    ClearData;
    AddResponses([ 2, 1, ord(idnDisabled) ]);
    SynEdit.TextBetweenPoints[point(9, 2),point(9, 2)] := 'x';
    FTestTree.ValidateRange(1, 10, FOpenings);

    CheckReq(n, [ 2, 1, 1 ]);
    CheckNodes(n, 2, [ ExpN( 1,12, idnIfdef, idnDisabled) ]);

    // Add ELSE node
    n := 'TestText3 add ELSE;';
    ClearData;
    SynEdit.TextBetweenPoints[point(1, 3),point(1, 3)] := '{$Else}';
    FTestTree.ValidateRange(1, 10, FOpenings);

    CheckReq(n, []);
    CheckNodes(n, 2, [ ExpN( 1,12, idnIfdef, idnDisabled) ]);
    CheckNodes(n, 3, [ ExpN( 1, 8, idnElse, idnEnabled) ]);

    n := 'Comment entire text';
    ClearData;
    SynEdit.TextBetweenPoints[point(1, 1),point(1, 1)] := '(*';
    FTestTree.ValidateRange(1, 10, FOpenings);
    CheckReq(n, []);
    CheckNodes(n, 2, [ ExpN( 1,12, idnCommentedIfdef, idnDisabled) ]);
    CheckNodes(n, 3, []);
    CheckNodes(n, 4, []);

    n := 'Edit Commented-ifdef line (append)';
    ClearData;
    SynEdit.TextBetweenPoints[point(2, 12),point(2, 12)] := '//';
    FTestTree.ValidateRange(1, 10, FOpenings);
    CheckReq(n, []);
    CheckNodes(n, 2, [ ExpN( 1,12, idnCommentedIfdef, idnDisabled) ]);
    CheckNodes(n, 3, []);
    CheckNodes(n, 4, []);

    n := 'UN-Comment entire text';
    ClearData;
    SynEdit.TextBetweenPoints[point(1, 1),point(3, 1)] := '';
    FTestTree.ValidateRange(1, 10, FOpenings);
    CheckReq(n, []);
    CheckNodes(n, 2, [ ExpN( 1,12, idnIfdef, idnDisabled) ]);
    CheckNodes(n, 3, [ ExpN( 1, 8, idnElse, idnEnabled) ]);

  {%endregion  Simple Ifdef + edit: get node request }


  {%region  2 one line Ifdef + edit: get node request, change with SetNodeState }
    n := 'TestText4 scan;';
    SetupTest(TestText4);

    ClearData;
    AddResponses([ 2, 1, ord(idnEnabled),
                   4, 1, ord(idnDisabled)  ]);
    FTestTree.ValidateRange(1, 6, FOpenings);
    CheckReq(n, [ 2, 1, 1,
                  4, 1, 1  ]);
    CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, idnEnabled), ExpN(13,21, idnEndIf, idnEnabled ) ]);
    CheckNodes(n, 4, [ ExpN( 1,11, idnIfdef, idnDisabled), ExpN(13,21, idnEndIf, idnDisabled ) ]);


    ClearData;
    FTestTree.SetNodeState(2,1, idnNotInCode);
    CheckReq(n, []);
    CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, idnNotInCode), ExpN(13,21, idnEndIf ) ]);
    CheckNodes(n, 4, [ ExpN( 1,11, idnIfdef, idnDisabled), ExpN(13,21, idnEndIf, idnDisabled ) ]);
  {%endregion  2 one line Ifdef + edit: get node request, change with SetNodeState }


  {%region  Insert IFDEF into empty text }
    SetupTest(TestTextNoIfDef);
    ClearData;

    FTestTree.ValidateRange(1, 5, FOpenings);
    CheckReq(n, []); // empty text, no request

    SynEdit.TextBetweenPoints[point(1, 3),point(1, 3)] := '{$IFDEF a}';
    FTestTree.ValidateRange(1, 5, FOpenings);
//FTestTree.DebugPrint(true);DebugLn;
    CheckReq(n, [ 3, 1, 1 ]);
    CheckNodesXY('Insert IFDEF into empty text', 3, [1,11], 0);
  {%endregion  Insert IFDEF into empty text }

  {%region  }
    n := '';
    ReCreateEditForTreeTest(TestText8);
    FTestTree.SetNodeState( 2, 1,idnDisabled);
    FTestTree.SetNodeState( 2,12,idnInvalid);
    FTestTree.SetNodeState( 3, 2,idnEnabled);
FTestTree.DebugPrint(true);DebugLn('-----');

    FTestTree.ValidateRange( 1, 11, FOpenings);
FTestTree.DebugPrint(true);DebugLn('-----');
    CheckNodes(n, 2, [ ExpN( 1,11, idnIfdef, idnDisabled, EpNil, EpEnd(2, 35)),
                       ExpN(12,22, idnIfdef, idnInvalid, EpNil, EpEnd(2, 26)),
                       ExpN(26,34, idnEndIf, idnUnknown, EpIf(2,12), EpNil),
                       ExpN(35,43, idnEndIf, idnDisabled, EpIf(2, 1), EpNil)
                     ]);
    CheckOpenCloseCount(n, 2, 1, 1);
    CheckNodes(n, 3, [ ExpN( 2,13, idnIfdef, idnEnabled, EpNil, EpEnd( 5, 1)) ]);
    CheckOpenCloseCount(n, 3, 0, 0);
    CheckNodes(n, 5, [ ExpN( 1, 9, idnEndIf, idnEnabled, EpIf( 3, 2)) ]);
    CheckOpenCloseCount(n, 5, 0, 0);


    SynEdit.TextBetweenPoints[point(10,2),point(10,2)] := LineEnding;
FTestTree.DebugPrint(true);DebugLn('-----');
    CheckNodes(n, 2, [ ExpN( 1, 2, 1, idnIfdef, idnUnknown, EpNil, EpEnd(3, 26)) ]);
    CheckNodes(n, 3, [ ExpN( 3,13, idnIfdef, idnInvalid, EpNil, EpEnd(3, 17)),
                       ExpN(17,25, idnEndIf, idnUnknown, EpIf(3, 3), EpNil),
                       ExpN(26,34, idnEndIf, idnUnknown, EpIf(2, 1), EpNil)
                     ]);
    CheckOpenCloseCount(n, 2, 0, 0);
    CheckOpenCloseCount(n, 3, 0, 0);


    SynEdit.TextBetweenPoints[point(14,2),point(1, 3)] := '';

  {%endregion   }



  {%region  }


    n := '';
    ReCreateEditForTreeTest(TestText11);
    FTestTree.ValidateRange( 1, 16, FOpenings);

    FTestTree.SetNodeState( 2, 3, idnEnabled);
    FTestTree.SetNodeState( 4, 3, idnDisabled);
    FTestTree.SetNodeState( 6, 3, idnInvalid);
    FTestTree.SetNodeState( 8, 3, idnInvalid);

    FTestTree.ValidateRange( 1, 16, FOpenings);
//FTestTree.DebugPrint(true);DebugLn('-----');

    //SynEdit.TextBetweenPoints[point( 7,3),point( 1,4)] := ''; // JOIN LINES
    SynEdit.TestTypeText(1, 4, #8, true);


    FTestTree.ValidateRange( 1, 16, FOpenings);
    FTestTree.SetNodeState( 2, 3, idnEnabled);
    //FTestTree.SetNodeState( 4, 3, idnDisabled);
    FTestTree.SetNodeState( 5, 3, idnInvalid);
    FTestTree.SetNodeState( 7, 3, idnInvalid);
    FTestTree.ValidateRange( 1, 16, FOpenings);

    SynEdit.Undo; // RESTORE LINEBREAK
    SynEdit.SimulatePaintText;
DebugLn('----- undone');FTestTree.DebugPrint(true);DebugLn('-----');

    FTestTree.ValidateRange( 1, 16, FOpenings);
DebugLn('----- valid');FTestTree.DebugPrint(true);DebugLn('-----');
    FTestTree.SetNodeState( 2, 3, idnEnabled);
FTestTree.DebugPrint(true);DebugLn('-----');
    FTestTree.SetNodeState( 4, 3, idnDisabled);
DebugLn('----- disabled');FTestTree.DebugPrint(true);DebugLn('-----');
    FTestTree.SetNodeState( 6, 3, idnInvalid);
    FTestTree.SetNodeState( 8, 3, idnInvalid);
    FTestTree.ValidateRange( 1, 16, FOpenings);

//FTestTree.DebugPrint(true);DebugLn('-----');
    SynEdit.TextBetweenPoints[point( 1,4),point( 1,5)] := '';
    FTestTree.ValidateRange( 1, 16, FOpenings);



  {%endregion   }




  FTestTree.DiscardOpeningList(FOpenings);
  FOpenings := nil;
  FTestTree.Free;
  FreeAndNil(FNodeStateResponses);
  FreeAndNil(FNodeStateRequests);
end;

initialization
  RegisterTest(TTestMarkupIfDef);

end.

