diff --git a/components/synedit/syneditmarkupifdef.pp b/components/synedit/syneditmarkupifdef.pp index 6c98547d8e..d71a556d16 100644 --- a/components/synedit/syneditmarkupifdef.pp +++ b/components/synedit/syneditmarkupifdef.pp @@ -1854,6 +1854,7 @@ var i, j, OtherDepth: Integer; OtherLine: TSynMarkupHighIfDefLinesNodeInfo; PeerChanged: Boolean; + CurEntry: TSynMarkupHighIfDefEntry; function OpenIdx(AIdx: Integer): Integer; // correct negative idx begin @@ -1876,10 +1877,11 @@ begin SetLength(PeerList, MaxListIdx); for i := 0 to ANode.EntryCount - 1 do begin - case ANode.Entry[i].NodeType of + CurEntry := ANode.Entry[i]; + case CurEntry.NodeType of idnIfdef: begin inc(CurDepth); - OpenList[OpenIdx(CurDepth)] := ANode.Entry[i]; // Store IfDef, with Index at end of IfDef (inside block) + OpenList[OpenIdx(CurDepth)] := CurEntry; // Store IfDef, with Index at end of IfDef (inside block) if CurDepth < MinOpenDepth then MinOpenDepth := CurDepth; if CurDepth > MaxOpenDepth then MaxOpenDepth := CurDepth; end; @@ -1893,9 +1895,9 @@ begin assert(CurDepth = MaxOpenDepth, 'ConnectPeers: Same line peer skips opening node(s)'); case OpenList[OpenIdx(CurDepth)].NodeType of idnIfdef, idnElseIf: - if OpenList[OpenIdx(CurDepth)].ClosingPeer <> ANode.Entry[i] then begin + if OpenList[OpenIdx(CurDepth)].ClosingPeer <> CurEntry then begin //Debugln(['New Peer for ',dbgs(OpenList[OpenIdx(CurDepth)].NodeType), ' to else same line']); - OpenList[OpenIdx(CurDepth)].ClosingPeer := ANode.Entry[i]; + OpenList[OpenIdx(CurDepth)].ClosingPeer := CurEntry; PeerChanged := True; //dec(MaxOpenDepth); // Will be set with the current entry end; @@ -1905,13 +1907,13 @@ begin else If CurDepth >= 0 then begin // Opening Node in previous line - PeerList[CurDepth] := ANode.Entry[i]; + PeerList[CurDepth] := CurEntry; assert((MaxPeerDepth=-1) or ((MinPeerDepth <= MaxPeerDepth) and (CurDepth = MinPeerDepth-1)), 'ConnectPeers: skipped noeds during line scan'); if CurDepth < MinPeerDepth then MinPeerDepth := CurDepth; if CurDepth > MaxPeerDepth then MaxPeerDepth := CurDepth; end; - OpenList[OpenIdx(CurDepth)] := ANode.Entry[i]; // Store IfDef, with Index at end of IfDef (inside block) + OpenList[OpenIdx(CurDepth)] := CurEntry; // Store IfDef, with Index at end of IfDef (inside block) if CurDepth < MinOpenDepth then MinOpenDepth := CurDepth; if CurDepth > MaxOpenDepth then MaxOpenDepth := CurDepth; end; @@ -1925,16 +1927,16 @@ begin if (CurDepth >= MinOpenDepth) and (CurDepth <= MaxOpenDepth) then begin // Opening Node on this line assert(CurDepth = MaxOpenDepth, 'ConnectPeers: Same line peer skips opening node(s)'); - if OpenList[OpenIdx(CurDepth)].ClosingPeer <> ANode.Entry[i] then begin + if OpenList[OpenIdx(CurDepth)].ClosingPeer <> CurEntry then begin //Debugln(['New Peer for ',dbgs(OpenList[OpenIdx(CurDepth)].NodeType), ' to endif same line']); - OpenList[OpenIdx(CurDepth)].ClosingPeer := ANode.Entry[i]; + OpenList[OpenIdx(CurDepth)].ClosingPeer := CurEntry; PeerChanged := True; end; dec(MaxOpenDepth); end else begin // Opening Node in previous line - PeerList[CurDepth] := ANode.Entry[i]; + PeerList[CurDepth] := CurEntry; assert((MaxPeerDepth=-1) or ((MinPeerDepth <= MaxPeerDepth) and (CurDepth = MinPeerDepth-1)), 'ConnectPeers: skipped noeds during line scan'); if CurDepth < MinPeerDepth then MinPeerDepth := CurDepth; if CurDepth > MaxPeerDepth then MaxPeerDepth := CurDepth; @@ -1957,18 +1959,22 @@ begin //end; assert(not (PeerList[i].NodeType in [idnIfdef, idnCommentedNode]), 'multi-line peer valid'); - //if (PeerList[i].NodeType = idnElse) and (AOuterLines <> nil) then begin + + // AOuterLines is only set while scanning opening lines in front of the 1st visible screenline + // (at the start of ValidateRange) if (AOuterLines <> nil) then begin - // todo: find multiply elseif - assert((PeerList[i].NodeType in [idnElse, idnElseIf]), 'multi-line (opening) peer valid'); - // scanning outer lines - j := ToPos(AOuterLines.NodeLineEx[i-1, 1]); - if j < 0 then begin - //debugln(['Skipping peer for ELSE with NO IFDEF at depth ', i-1, ' before line ', ANode.StartLine]); - continue; - end; - OtherLine := GetOrInsertNodeAtLine(j); - MaybeValidateNode(OtherLine); + if PeerList[i].NodeType in [idnElse, idnElseIf] then begin + // todo: find multiply elseif + j := ToPos(AOuterLines.NodeLineEx[i-1, 1]); + if j < 0 then begin + //debugln(['Skipping peer for ELSE with NO IFDEF at depth ', i-1, ' before line ', ANode.StartLine]); + continue; + end; + OtherLine := GetOrInsertNodeAtLine(j); + MaybeValidateNode(OtherLine); + end + else + continue; // while scanning outerlines, any EndIf can be ignored end else OtherLine := ANestList.Node[i]; // Todo: keep if same al last loop, and continue at OtherDepth / j diff --git a/components/synedit/test/testmarkupifdef.pas b/components/synedit/test/testmarkupifdef.pas index 570a7197e6..d4993cc29b 100644 --- a/components/synedit/test/testmarkupifdef.pas +++ b/components/synedit/test/testmarkupifdef.pas @@ -58,6 +58,8 @@ type function TestText9: TStringArray; function TestText10: TStringArray; function TestText11: TStringArray; + function TestText11a: TStringArray; + function TestText12: TStringArray; procedure CheckOpenCloseCount(AName: String; ALine: Integer; AExpOpenCnt, AExpCloseCnt: Integer); @@ -705,6 +707,83 @@ begin 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 @@ -2030,6 +2109,62 @@ procedure TTestMarkupIfDef.TestIfDefTreePeerConnect; {%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); @@ -2059,7 +2194,7 @@ begin else Result := TSynMarkupIfdefNodeState(StrToIntDef(v, 0)); - DebugLn('# TesTNodeStateHandler ', n, ' # ', v, ' ', dbgs(Result)); + //DebugLn('# TesTNodeStateHandler ', n, ' # ', v, ' ', dbgs(Result)); end; procedure TTestMarkupIfDef.TestIfDefTreeNodeState; @@ -2258,8 +2393,8 @@ 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); @@ -2292,7 +2427,6 @@ DebugLn('----- disabled');FTestTree.DebugPrint(true);DebugLn('-----'); - FTestTree.DiscardOpeningList(FOpenings); FOpenings := nil; FTestTree.Free;