diff --git a/components/synedit/synedithighlighter.pp b/components/synedit/synedithighlighter.pp index 408536d248..5e1d159c69 100644 --- a/components/synedit/synedithighlighter.pp +++ b/components/synedit/synedithighlighter.pp @@ -209,6 +209,7 @@ type fUpdateCount: integer; //mh 2001-09-13 fEnabled: Boolean; fWordBreakChars: TSynIdentChars; + FIsScanning: Boolean; procedure SetCurrentLines(const AValue: TSynEditStrings); procedure SetDrawDividerLevel(const AValue: Integer); procedure SetEnabled(const Value: boolean); //DDH 2001-10-23 @@ -239,6 +240,7 @@ type function GetDrawDivider(Index: integer): TSynDividerDrawConfigSetting; virtual; function GetDividerDrawConfig(Index: Integer): TSynDividerDrawConfig; virtual; function GetDividerDrawConfigCount: Integer; virtual; + property IsScanning: Boolean read FIsScanning; public procedure DefHighlightChange(Sender: TObject); property AttributeChangeNeedScan: Boolean read FAttributeChangeNeedScan; @@ -324,7 +326,7 @@ type property DividerDrawConfigCount: Integer read GetDividerDrawConfigCount; published property DefaultFilter: string read GetDefaultFilter write SetDefaultFilter - stored IsFilterStored; + stored IsFilterStored; deprecated; property Enabled: boolean read fEnabled write SetEnabled default TRUE; //DDH 2001-10-23 end; @@ -945,7 +947,7 @@ begin if Src is ClassType then SampleSource := Src.SampleSource; fWordBreakChars := Src.WordBreakChars; - DefaultFilter := Src.DefaultFilter; + //DefaultFilter := Src.DefaultFilter; Enabled := Src.Enabled; end else inherited Assign(Source); @@ -1221,18 +1223,23 @@ function TSynCustomHighlighter.ScanFrom(Index: integer; AtLeastTilIndex: integer var c: LongInt; begin - Result := Index; - c := CurrentLines.Count; - StartAtLineIndex(Result); - NextToEol; - while UpdateRangeInfoAtLine(Result) or - (Result <= AtLeastTilIndex+1) - do begin - inc(Result); - if Result = c then - break; - ContinueNextLine; + FIsScanning := True; + try + Result := Index; + c := CurrentLines.Count; + StartAtLineIndex(Result); NextToEol; + while UpdateRangeInfoAtLine(Result) or + (Result <= AtLeastTilIndex+1) + do begin + inc(Result); + if Result = c then + break; + ContinueNextLine; + NextToEol; + end; + finally + FIsScanning := False; end; end; diff --git a/components/synedit/synhighlighterlfm.pas b/components/synedit/synhighlighterlfm.pas index c10cd97b48..fddfc1e793 100644 --- a/components/synedit/synhighlighterlfm.pas +++ b/components/synedit/synhighlighterlfm.pas @@ -599,6 +599,7 @@ end; procedure TSynLFMSyn.ResetRange; begin + inherited; fRange := rsUnknown; end; diff --git a/components/synedit/synhighlighterxml.pas b/components/synedit/synhighlighterxml.pas index a89756d93a..f8a9a09141 100644 --- a/components/synedit/synhighlighterxml.pas +++ b/components/synedit/synhighlighterxml.pas @@ -56,7 +56,7 @@ interface {$I SynEdit.inc} uses - SysUtils, Classes, + SysUtils, Classes, math, LCLProc, {$IFDEF SYN_CLX} Qt, QControls, QGraphics, {$ELSE} @@ -67,7 +67,7 @@ uses {$ENDIF} Controls, Graphics, {$ENDIF} - SynEditTypes, SynEditHighlighter; + SynEditTypes, SynEditTextBuffer, SynEditHighlighter, SynEditHighlighterFoldBase; type TtkTokenKind = (tkAposAttrValue, tkAposEntityRef, tkAttribute, tkCDATA, @@ -84,7 +84,7 @@ type ); TRangeState = (rsAposAttrValue, rsAPosEntityRef, rsAttribute, rsCDATA, - rsComment, rsElement, rsEntityRef, rsEqual, rsProcessingInstruction, + rsComment, rsElement, rsCloseElement, rsOpenElement, rsEntityRef, rsEqual, rsProcessingInstruction, rsQuoteAttrValue, rsQuoteEntityRef, rsText, // rsnsAposAttrValue, rsnsAPosEntityRef, rsnsEqual, rsnsQuoteAttrValue, @@ -96,11 +96,46 @@ type rsDocTypeQuoteEntityRef} ); + TXmlCodeFoldBlockType = ( + cfbtXmlNone, + cfbtXmlElement, // + cfbtXmlComment, // + cfbtXmlCData, // + cfbtXmlDocType, // 0) and (fLine[Run - 1] = '/') then + if TopXmlCodeFoldBlockType = cfbtXmlElement then + EndXmlElemCodeFoldBlock; + fTokenId := tkSymbol; fRange:= rsText; Inc(Run); @@ -400,11 +475,14 @@ end; procedure TSynXMLSyn.CommentProc; begin - if (fLine[Run] = '-') and (fLine[Run + 1] = '-') and (fLine[Run + 2] = '>') + if (fLine[Run] = '-') and (fLine[Run + 1] = '-') and + (fLine[Run + 2] = '>') then begin fTokenID := tkSymbol; fRange:= rsText; Inc(Run, 3); + if TopXmlCodeFoldBlockType = cfbtXmlComment then + EndXmlCodeFoldBlock; Exit; end; @@ -438,6 +516,8 @@ begin then begin fRange := rsText; Inc(Run); + if TopXmlCodeFoldBlockType = cfbtXmlProcess then + EndXmlCodeFoldBlock; break; end; Inc(Run); @@ -479,6 +559,8 @@ begin end; '>': begin fRange := rsAttribute; + if TopXmlCodeFoldBlockType = cfbtXmlDocType then + EndXmlCodeFoldBlock; Inc(Run); Break; end; @@ -513,10 +595,13 @@ begin while not (fLine[Run] in [#0, #10, #13]) do begin - if (fLine[Run] = '>') and (fLine[Run - 1] = ']') + if (Run >= 2) and (fLine[Run] = '>') and (fLine[Run - 1] = ']') and + (fLine[Run - 2] = ']') then begin fRange := rsText; Inc(Run); + if TopXmlCodeFoldBlockType = cfbtXmlCData then + EndXmlCodeFoldBlock; break; end; Inc(Run); @@ -524,9 +609,20 @@ begin end; procedure TSynXMLSyn.ElementProc; +var + NameStart: LongInt; begin - if fLine[Run] = '/' then Inc(Run); + if fLine[Run] = '/' then + Inc(Run); + NameStart := Run; while (fLine[Run] in NameChars) do Inc(Run); + + if fRange = rsOpenElement then + StartXmlElemCodeFoldBlock(cfbtXmlElement, NameStart, Copy(fLine, NameStart + 1, Run - NameStart)); + + if fRange = rsCloseElement then + EndXmlElemCodeFoldBlock(NameStart, Copy(fLine, NameStart + 1, Run - NameStart)); // TODO: defer until ">" reached + fRange := rsAttribute; fTokenID := tkElement; end; @@ -695,10 +791,28 @@ begin fRange := rsAPosAttrValue; end; +function TSynXMLSyn.UpdateRangeInfoAtLine(Index: Integer): Boolean; +var + InfoOpenLenChanged, InfoCloseLenChanged: Boolean; +begin + Result := inherited UpdateRangeInfoAtLine(Index); + InfoOpenLenChanged := Length(FXmlRangeInfo.ElementOpenList) <> FXmlRangeInfoOpenPos; + InfoCloseLenChanged := Length(FXmlRangeInfo.ElementCloseList) <> FXmlRangeInfoClosePos; + if FXmlRangeInfoChanged or InfoOpenLenChanged or InfoCloseLenChanged then begin + Result := True; + if InfoOpenLenChanged then + SetLength(FXmlRangeInfo.ElementOpenList, FXmlRangeInfoOpenPos); + if InfoCloseLenChanged then + SetLength(FXmlRangeInfo.ElementCloseList, FXmlRangeInfoClosePos); + TSynHighlighterXmlRangeList(CurrentRanges).XmlRangeInfo[LineIndex] := FXmlRangeInfo; // Store on this line + FXmlRangeInfoChanged := False; + end; +end; + procedure TSynXMLSyn.IdentProc; begin case fRange of - rsElement: + rsElement, rsOpenElement, rsCloseElement: begin ElementProc{$IFDEF FPC}(){$ENDIF}; end; @@ -859,19 +973,83 @@ end; function TSynXMLSyn.GetRange: Pointer; begin - Result := Pointer(PtrInt(fRange)); + CodeFoldRange.RangeType:=Pointer(PtrUInt(Integer(fRange))); + Result := inherited; end; procedure TSynXMLSyn.SetRange(Value: Pointer); begin - fRange := TRangeState(PtrUInt(Value)); + inherited; + fRange := TRangeState(Integer(PtrUInt(CodeFoldRange.RangeType))); end; procedure TSynXMLSyn.ReSetRange; begin + inherited; fRange:= rsText; end; +function TSynXMLSyn.FoldOpenCount(ALineIndex: Integer; AType: Integer): integer; +begin + Result := EndFoldLevel(ALineIndex) - MinimumFoldLevel(ALineIndex); +end; + +function TSynXMLSyn.FoldCloseCount(ALineIndex: Integer; AType: Integer): integer; +begin + Result := EndFoldLevel(ALineIndex - 1) - MinimumFoldLevel(ALineIndex); +end; + +function TSynXMLSyn.FoldNestCount(ALineIndex: Integer; AType: Integer): integer; +begin + Result := EndFoldLevel(ALineIndex); +end; + +function TSynXMLSyn.FoldLineLength(ALineIndex, FoldIndex: Integer): integer; +var + i, lvl, cnt: Integer; + e, m: Integer; +begin + //atype := FoldTypeAtNodeIndex(ALineIndex, FoldIndex); + cnt := CurrentLines.Count; + e := EndFoldLevel(ALineIndex); + m := MinimumFoldLevel(ALineIndex); + lvl := Min(m+1+FoldIndex, e); + i := ALineIndex + 1; + while (i < cnt) and (MinimumFoldLevel(i) >= lvl) do inc(i); + // check if fold last line of block (not mixed "end begin") + // and not lastlinefix + if (i = cnt) or (EndFoldLevel(i) > MinimumFoldLevel(i)) then + dec(i); + // Amount of lines, that will become invisible (excludes the cfCollapsed line) + Result := i - ALineIndex; +end; + +function TSynXMLSyn.MinimumFoldLevel(ALineIndex: Integer): integer; +var + r: TSynCustomHighlighterRange; +begin + if (ALineIndex < 0) or (ALineIndex >= CurrentLines.Count) then + exit(0); + r := TSynCustomHighlighterRange(CurrentRanges[ALineIndex]); + if (r <> nil) and (Pointer(r) <> NullRange) then + Result := r.MinimumCodeFoldBlockLevel + else + Result := 0; +end; + +function TSynXMLSyn.EndFoldLevel(ALineIndex: Integer): integer; +var + r: TSynCustomHighlighterRange; +begin + if (ALineIndex < 0) or (ALineIndex >= CurrentLines.Count) then + exit(0); + r := TSynCustomHighlighterRange(CurrentRanges[ALineIndex]); + if (r <> nil) and (Pointer(r) <> NullRange) then + Result := r.CodeFoldStackSize + else + Result := 0; +end; + function TSynXMLSyn.GetIdentChars: TSynIdentChars; begin Result := ['0'..'9', 'a'..'z', 'A'..'Z', '_', '.', '-'] + TSynSpecialChars; @@ -894,6 +1072,181 @@ begin ''; end; +procedure TSynXMLSyn.CreateRootCodeFoldBlock; +begin + inherited CreateRootCodeFoldBlock; + RootCodeFoldBlock.InitRootBlockType(Pointer(PtrInt(cfbtXmlNone))); +end; + +function TSynXMLSyn.CreateRangeList: TSynHighlighterRangeList; +begin + Result := TSynHighlighterXmlRangeList.Create; +end; + +function TSynXMLSyn.StartXmlCodeFoldBlock(ABlockType: TXmlCodeFoldBlockType): TSynCustomCodeFoldBlock; +begin + if CodeFoldRange.CodeFoldStackSize >= MaxFoldNestDeep then exit; + StartCodeFoldBlock(Pointer(PtrInt(ABlockType))); +end; + +function TSynXMLSyn.StartXmlElemCodeFoldBlock(ABlockType: TXmlCodeFoldBlockType; + OpenPos: Integer; AName: String): TSynCustomCodeFoldBlock; +var + i: Integer; +begin + If IsScanning then begin + AName := LowerCase(AName); + i := Length(FXmlRangeInfo.ElementOpenList); + if (FXmlRangeInfoOpenPos < i) then begin + if (FXmlRangeInfo.ElementOpenList[FXmlRangeInfoOpenPos] <> AName) then begin + FXmlRangeInfo.ElementOpenList[FXmlRangeInfoOpenPos] := AName; + FXmlRangeInfoChanged := true; // TODO:if this node closes on the same line, it may not be amodified .... + end; + end else begin // append - modified will be deteced by the new length + SetLength(FXmlRangeInfo.ElementOpenList, FXmlRangeInfoOpenPos + 10); + FXmlRangeInfo.ElementOpenList[FXmlRangeInfoOpenPos] := AName; + end; + end; + inc(FXmlRangeInfoOpenPos); + StartXmlCodeFoldBlock(ABlockType); +end; + +procedure TSynXMLSyn.EndXmlCodeFoldBlock; +begin + EndCodeFoldBlock(); +end; + +procedure TSynXMLSyn.EndXmlElemCodeFoldBlock(ClosePos: Integer = -1; AName: String = ''); +var + cnt, i, k, lvl: Integer; + LInfo: Array of String; +begin + if not (TopXmlCodeFoldBlockType = cfbtXmlElement) then debugln('---- XXXXX TSynXMLSyn.EndXmlElemCodeFoldBlock XXXXX'); + AName := LowerCase(AName); + + cnt := 0; + If IsScanning then begin + if (AName = '') and (CodeFoldRange.CodeFoldStackSize > 0) then begin + cnt := 1; + end + else begin + cnt := 1; + i := FXmlRangeInfoOpenPos; + while i > 0 do begin + if TopXmlCodeFoldBlockType(FXmlRangeInfoOpenPos - i) <> cfbtXmlElement then debugln('---- XXXXX TSynXMLSyn.EndXmlElemCodeFoldBlock XXXXX'); + if (FXmlRangeInfo.ElementOpenList[i-1] = AName) then + break; + dec(i); + inc(cnt); + end; + + if i = 0 then begin + i := LineIndex - 1; + lvl := EndFoldLevel(i); + while i >= 0 do begin + if MinimumFoldLevel(i) < lvl then begin + LInfo := TSynHighlighterXmlRangeList(CurrentRanges).XmlRangeInfo[i].ElementOpenList; + k := length(LInfo) - Max(EndFoldLevel(i) - lvl, 0) - 1; + while (k >= 0) do begin + if (LInfo[k] = AName) then + break; + inc(cnt); + dec(k); + dec(lvl); + end; + if k >= 0 then break; + end; + dec(i); + end; + + if (i < 0) or (cnt > CodeFoldRange.CodeFoldStackSize ) then cnt := 0; // never opened, do not close + end; + end; + + i := Length(FXmlRangeInfo.ElementCloseList); + if (FXmlRangeInfoClosePos < i) then begin + if (FXmlRangeInfo.ElementCloseList[FXmlRangeInfoClosePos] <> cnt) then begin + FXmlRangeInfo.ElementCloseList[FXmlRangeInfoClosePos] := cnt; + FXmlRangeInfoChanged := true; + end; + end else begin // append - modified will be deteced by the new length + SetLength(FXmlRangeInfo.ElementCloseList, FXmlRangeInfoClosePos + 10); + FXmlRangeInfo.ElementCloseList[FXmlRangeInfoClosePos] := cnt; + end; + end + else begin + if FXmlRangeInfoClosePos < length(FXmlRangeInfo.ElementCloseList) then + cnt := FXmlRangeInfo.ElementCloseList[FXmlRangeInfoClosePos] + else + cnt := 0; + end; + inc(FXmlRangeInfoClosePos); + + for i := 1 to cnt do begin + if FXmlRangeInfoOpenPos > 0 then + dec(FXmlRangeInfoOpenPos); + EndXmlCodeFoldBlock; + end; +end; + +function TSynXMLSyn.TopXmlCodeFoldBlockType(DownIndex: Integer): TXmlCodeFoldBlockType; +begin + Result := TXmlCodeFoldBlockType(PtrUInt(TopCodeFoldBlockType(DownIndex))); +end; + +{ TSynHighlighterXmlRangeList } + +function TSynHighlighterXmlRangeList.GetXmlRangeInfo(Index: Integer): TSynXmlRangeInfo; +begin + if (Index < 0) or (Index >= Count) then begin + Result.ElementOpenList := nil; + exit; + end; + Result := TSynXmlRangeInfo((ItemPointer[Index] + inherited ItemSize)^); +end; + +procedure TSynHighlighterXmlRangeList.SetXmlRangeInfo(Index: Integer; + const AValue: TSynXmlRangeInfo); +begin + TSynXmlRangeInfo((ItemPointer[Index] + inherited ItemSize)^) := AValue; +end; + +procedure TSynHighlighterXmlRangeList.SetCapacity(const AValue: Integer); +var + i: LongInt; +begin + for i := AValue to Capacity-1 do + with TSynXmlRangeInfo((ItemPointer[i] + inherited ItemSize)^) do begin + ElementOpenList := nil; + ElementCloseList := nil; + end; + inherited SetCapacity(AValue); +end; + +function TSynHighlighterXmlRangeList.ItemSize: Integer; +begin + Result := inherited ItemSize + SizeOf(TSynXmlRangeInfo); +end; + +procedure TSynHighlighterXmlRangeList.Move(AFrom, ATo, ALen: Integer); +var + i: LongInt; +begin + if ATo > AFrom then + for i:= Max(AFrom + ALen, ATo) to ATo + ALen - 1 do // move forward + with TSynXmlRangeInfo((ItemPointer[i] + inherited ItemSize)^) do begin + ElementOpenList := nil; + ElementCloseList := nil; + end + else + for i:= ATo to Min(ATo + ALen , AFrom) - 1 do // move backward + with TSynXmlRangeInfo((ItemPointer[i] + inherited ItemSize)^) do begin + ElementOpenList := nil; + ElementCloseList := nil; + end; + inherited Move(AFrom, ATo, ALen); +end; + initialization {$IFNDEF SYN_CPPB_1}