SynEdit: WrappedView, add indent for wrapped lines

This commit is contained in:
Martin 2025-01-24 17:16:24 +01:00
parent b96f00e77f
commit 5a2dab70e4
2 changed files with 302 additions and 84 deletions

View File

@ -62,14 +62,14 @@ type
FCurTxtLineIdx : Integer; FCurTxtLineIdx : Integer;
FCurLineByteLen: Integer; FCurLineByteLen: Integer;
FIsLastViewedSubLine: Boolean; FIsLastViewedSubLine: Boolean;
FCurLinePhysStart: integer; FCurLinePhysStartOffset: integer;
// Fields for GetNextHighlighterTokenFromView // Fields for GetNextHighlighterTokenFromView
// Info about the token (from highlighter) // Info about the token (from highlighter)
FCurViewToken: TLazSynDisplayTokenInfo; FCurViewToken: TLazSynDisplayTokenInfo;
FCurViewCurTokenStartPos: TLazSynDisplayTokenBound; // Start bound of the HL token FCurViewCurTokenStartPos: TLazSynDisplayTokenBound; // Start bound of the HL token
FCurViewAttr: TSynSelectedColorMergeResult; FCurViewAttr: TSynSelectedColorMergeResult;
FWrapEndBound: TLazSynDisplayTokenBound; FWrapStartBound, FWrapEndBound: TLazSynDisplayTokenBound;
// Scanner Pos // Scanner Pos
FCurViewScannerPos: TLazSynDisplayTokenBound; // Start according to Logical flow. Left for LTR, or Right for RTL FCurViewScannerPos: TLazSynDisplayTokenBound; // Start according to Logical flow. Left for LTR, or Right for RTL
FCurViewScannerPhysCharPos: Integer; // 1 based - Full char bound (Before FCurViewScannerPos.Physical (PaintStart)) FCurViewScannerPhysCharPos: Integer; // 1 based - Full char bound (Before FCurViewScannerPos.Physical (PaintStart))
@ -329,7 +329,7 @@ procedure TLazSynPaintTokenBreaker.SetHighlighterTokensLine(ALine: TLineIdx; out
var var
LogLeftPos, ASubLineIdx: Integer; LogLeftPos, ASubLineIdx: Integer;
begin begin
FDisplayView.SetHighlighterTokensLine(ALine, ARealLine, ASubLineIdx, LogLeftPos, FCurLinePhysStart, FCurLineByteLen); FDisplayView.SetHighlighterTokensLine(ALine, ARealLine, ASubLineIdx, LogLeftPos, FCurLinePhysStartOffset, FCurLineByteLen);
if FLinesView.LogPhysConvertor.CurrentLine = ARealLine then begin if FLinesView.LogPhysConvertor.CurrentLine = ARealLine then begin
if not FCharWidthsFromConverter then begin if not FCharWidthsFromConverter then begin
FCharWidthsFromConverter := True; FCharWidthsFromConverter := True;
@ -350,14 +350,14 @@ begin
FCurViewToken.TokenLength := 0; FCurViewToken.TokenLength := 0;
FCurViewScannerPos.Logical := LogLeftPos; FCurViewScannerPos.Logical := LogLeftPos;
FCurViewScannerPos.Physical := FCurLinePhysStart; FCurViewScannerPos.Physical := FCurLinePhysStartOffset; // FCurLinePhysStartOffset still one based, not yet offset
FCurViewScannerPos.Offset := 0; FCurViewScannerPos.Offset := 0;
FCurViewScannerPhysCharPos := FCurLinePhysStart; FCurViewScannerPhysCharPos := FCurLinePhysStartOffset;
FCurViewinRTL := False; FCurViewinRTL := False;
dec(FCurLinePhysStart); dec(FCurLinePhysStartOffset); // 0 based
FVirtualFirstCol := FCurLinePhysStart + FFirstCol; FVirtualFirstCol := FCurLinePhysStartOffset + FFirstCol;
FVirtualLastCol := FCurLinePhysStart + FLastCol; FVirtualLastCol := FCurLinePhysStartOffset + FLastCol;
FNextMarkupPhysPos := low(FNextMarkupPhysPos); // Marker for first call to GetNextHighlighterTokenEx FNextMarkupPhysPos := low(FNextMarkupPhysPos); // Marker for first call to GetNextHighlighterTokenEx
FNextMarkupLogPos := -1; FNextMarkupLogPos := -1;
@ -409,54 +409,66 @@ begin
ATokenInfo.Attr := nil; ATokenInfo.Attr := nil;
Result := GetNextHighlighterTokenFromView(ATokenInfo, FNextMarkupPhysPos, FNextMarkupLogPos); Result := GetNextHighlighterTokenFromView(ATokenInfo, FNextMarkupPhysPos, FNextMarkupLogPos);
if not Result then begin if (not Result) or (FTokenOrigin = dtoBeforeText) then begin
// the first run StartPos is set by GetNextHighlighterTokenFromView if ATokenInfo.Tk.TokenStart = nil then begin
if FTokenOrigin in [dtoAfterText, dtoAfterWrapped] then begin ATokenInfo.Tk.TokenStart := @Space[1];
ATokenInfo.StartPos := FCurMarkupNextStart ATokenInfo.Tk.TokenLength := 1;
end;
if FTokenOrigin = dtoBeforeText then begin
assert((FNextMarkupPhysPos <= 0) or (FNextMarkupPhysPos > ATokenInfo.StartPos.Physical), 'FNextMarkupPhysPos > ATokenInfo.StartPos.Physical');
assert(ATokenInfo.EndPos.Physical > ATokenInfo.StartPos.Physical, 'ATokenInfo.EndPos.Physical > ATokenInfo.StartPos.Physical');
end end
else else begin
if FVirtualFirstCol > ATokenInfo.StartPos.Physical then begin // the first run StartPos is set by GetNextHighlighterTokenFromView
ATokenInfo.StartPos.Logical := ATokenInfo.StartPos.Logical + (FVirtualFirstCol - ATokenInfo.StartPos.Physical); if FTokenOrigin in [dtoAfterText, dtoAfterWrapped] then
ATokenInfo.StartPos.Physical := FVirtualFirstCol; ATokenInfo.StartPos := FCurMarkupNextStart
else
if FVirtualFirstCol > ATokenInfo.StartPos.Physical then begin
ATokenInfo.StartPos.Logical := ATokenInfo.StartPos.Logical + (FVirtualFirstCol - ATokenInfo.StartPos.Physical);
ATokenInfo.StartPos.Physical := FVirtualFirstCol;
end;
Result := (ATokenInfo.StartPos.Physical < FVirtualLastCol);
if not Result then
exit;
assert((FNextMarkupPhysPos <= 0) or (FNextMarkupPhysPos > ATokenInfo.StartPos.Physical), 'FNextMarkupPhysPos > ATokenInfo.StartPos.Physical');
assert(FTokenOrigin in [dtoAfterText, dtoAfterWrapped], 'TLazSynPaintTokenBreaker.GetNextHighlighterTokenEx: FTokenOrigin in [dtoAfterText, dtoAfterWrapped]');
if FNextMarkupPhysPos > 0
then ATokenInfo.EndPos.Physical := Min(FNextMarkupPhysPos, FVirtualLastCol)
else ATokenInfo.EndPos.Physical := FVirtualLastCol;
ATokenInfo.EndPos.Offset := 0;
ATokenInfo.EndPos.Logical := ATokenInfo.StartPos.Logical + (ATokenInfo.EndPos.Physical - ATokenInfo.StartPos.Physical);
if (FNextMarkupLogPos > 0) and (FNextMarkupLogPos < ATokenInfo.EndPos.Logical) then begin
ATokenInfo.EndPos.Physical := ATokenInfo.EndPos.Physical - (ATokenInfo.EndPos.Logical - FNextMarkupLogPos);
ATokenInfo.EndPos.Logical := FNextMarkupLogPos;
end;
assert(ATokenInfo.EndPos.Physical > ATokenInfo.StartPos.Physical, 'ATokenInfo.EndPos.Physical > ATokenInfo.StartPos.Physical');
assert(ATokenInfo.EndPos.Logical > ATokenInfo.StartPos.Logical, 'ATokenInfo.EndPos.Logical > ATokenInfo.StartPos.Logical');
FCurMarkupNextStart := ATokenInfo.EndPos;
if FCurMarkupNextRtlInfo.IsRtl then begin
FNextMarkupPhysPos := -1;
FNextMarkupLogPos := -1;
end;
FCurMarkupNextRtlInfo.IsRtl := False;
ATokenInfo.PhysicalCharStart := ATokenInfo.StartPos.Physical - FCurLinePhysStartOffset;
ATokenInfo.PhysicalClipStart := ATokenInfo.StartPos.Physical - FCurLinePhysStartOffset;
ATokenInfo.PhysicalCharEnd := ATokenInfo.EndPos.Physical - FCurLinePhysStartOffset;
ATokenInfo.PhysicalClipEnd := ATokenInfo.EndPos.Physical - FCurLinePhysStartOffset;
ATokenInfo.RtlInfo.IsRtl := False;
end; end;
Result := (ATokenInfo.StartPos.Physical < FVirtualLastCol);
if not Result then
exit;
assert((FNextMarkupPhysPos <= 0) or (FNextMarkupPhysPos > ATokenInfo.StartPos.Physical), 'FNextMarkupPhysPos > ATokenInfo.StartPos.Physical');
assert(FTokenOrigin in [dtoAfterText, dtoAfterWrapped], 'TLazSynPaintTokenBreaker.GetNextHighlighterTokenEx: FTokenOrigin in [dtoAfterText, dtoAfterWrapped]');
ATokenInfo.Tk.TokenStart := @Space[1];
ATokenInfo.Tk.TokenLength := 1;
if FNextMarkupPhysPos > 0
then ATokenInfo.EndPos.Physical := Min(FNextMarkupPhysPos, FVirtualLastCol)
else ATokenInfo.EndPos.Physical := FVirtualLastCol;
ATokenInfo.EndPos.Offset := 0;
ATokenInfo.EndPos.Logical := ATokenInfo.StartPos.Logical + (ATokenInfo.EndPos.Physical - ATokenInfo.StartPos.Physical);
if (FNextMarkupLogPos > 0) and (FNextMarkupLogPos < ATokenInfo.EndPos.Logical) then begin
ATokenInfo.EndPos.Physical := ATokenInfo.EndPos.Physical - (ATokenInfo.EndPos.Logical - FNextMarkupLogPos);
ATokenInfo.EndPos.Logical := FNextMarkupLogPos;
end;
assert(ATokenInfo.EndPos.Physical > ATokenInfo.StartPos.Physical, 'ATokenInfo.EndPos.Physical > ATokenInfo.StartPos.Physical');
assert(ATokenInfo.EndPos.Logical > ATokenInfo.StartPos.Logical, 'ATokenInfo.EndPos.Logical > ATokenInfo.StartPos.Logical');
FCurMarkupNextStart := ATokenInfo.EndPos;
if FCurMarkupNextRtlInfo.IsRtl then begin
FNextMarkupPhysPos := -1;
FNextMarkupLogPos := -1;
end;
FCurMarkupNextRtlInfo.IsRtl := False;
ATokenInfo.PhysicalCharStart := ATokenInfo.StartPos.Physical - FCurLinePhysStart;
ATokenInfo.PhysicalClipStart := ATokenInfo.StartPos.Physical - FCurLinePhysStart;
ATokenInfo.PhysicalCharEnd := ATokenInfo.EndPos.Physical - FCurLinePhysStart;
ATokenInfo.PhysicalClipEnd := ATokenInfo.EndPos.Physical - FCurLinePhysStart;
ATokenInfo.RtlInfo.IsRtl := False;
FMarkupTokenAttr.Clear; FMarkupTokenAttr.Clear;
if ATokenInfo.Attr <> nil then begin if ATokenInfo.Attr <> nil then begin
FMarkupTokenAttr.Assign(ATokenInfo.Attr); FMarkupTokenAttr.Assign(ATokenInfo.Attr);
if FTokenOrigin = dtoBeforeText then begin
FMarkupTokenAttr.CurrentStartX := FWrapStartBound;
FMarkupTokenAttr.CurrentEndX := FWrapEndBound;
end;
end end
else begin else begin
FMarkupTokenAttr.Foreground := FForegroundColor; FMarkupTokenAttr.Foreground := FForegroundColor;
@ -485,6 +497,7 @@ begin
fMarkupManager.MergeMarkupAttributeAtWrapEnd(FCurTxtLineIdx + 1, fMarkupManager.MergeMarkupAttributeAtWrapEnd(FCurTxtLineIdx + 1,
FWrapEndBound, FMarkupTokenAttr) FWrapEndBound, FMarkupTokenAttr)
else else
if FTokenOrigin <> dtoBeforeText then
fMarkupManager.MergeMarkupAttributeAtRowCol(FCurTxtLineIdx + 1, fMarkupManager.MergeMarkupAttributeAtRowCol(FCurTxtLineIdx + 1,
ATokenInfo.StartPos, ATokenInfo.EndPos, ATokenInfo.RtlInfo, FMarkupTokenAttr); ATokenInfo.StartPos, ATokenInfo.EndPos, ATokenInfo.RtlInfo, FMarkupTokenAttr);
FMarkupTokenAttr.ProcessMergeInfo; FMarkupTokenAttr.ProcessMergeInfo;
@ -757,6 +770,7 @@ var
PhysTokenStop: Integer; PhysTokenStop: Integer;
TabExtra: Integer; TabExtra: Integer;
HasTabs, HasDouble, NeedsEto: Boolean; HasTabs, HasDouble, NeedsEto: Boolean;
FrameStartPos: TLazSynDisplayTokenBound;
begin begin
ATokenInfo.Attr := nil; ATokenInfo.Attr := nil;
while True do begin while True do begin
@ -768,6 +782,78 @@ begin
ALogEnd := MaxInt; ALogEnd := MaxInt;
end; end;
if (FTokenOrigin = dtoBeforeText) then begin
ATokenInfo.RtlInfo.IsRtl := False;
ATokenInfo.NextRtlInfo.IsRtl := False;
if APhysEnd < 0 then begin
ATokenInfo.NextPos := FCurViewScannerPos;
ATokenInfo.NextPos.Offset := 0;
exit;
end;
i := FCurViewToken.TokenLength;
j := FVirtualFirstCol - FCurViewScannerPhysCharPos;
if j > 0 then begin
j := min(j, i);
i := i - j;
FCurViewToken.TokenLength := i;
if FCurViewToken.TokenStart <> nil then
FCurViewToken.TokenStart := FCurViewToken.TokenStart + j;
FVirtualFirstCol := FVirtualFirstCol - j;
FVirtualLastCol := FVirtualLastCol - j;
dec(FCurLinePhysStartOffset, j);
if i = 0 then
continue;
end;
ATokenInfo.Tk := FCurViewToken;
ATokenInfo.StartPos := FCurViewScannerPos;
ATokenInfo.StartPos.Logical := FCharWidthsLen + 1;
ATokenInfo.StartPos.Offset := 0;
ATokenInfo.EndPos.Logical := FCurViewScannerPos.Logical + i;
ATokenInfo.EndPos.Logical := FCharWidthsLen + 1 + i;
ATokenInfo.EndPos.Physical := FCurViewScannerPos.Physical + i; // min right col
ATokenInfo.EndPos.Offset := 0;
ATokenInfo.PhysicalCharStart := FCurViewScannerPhysCharPos - FCurLinePhysStartOffset;
ATokenInfo.PhysicalClipStart := ATokenInfo.StartPos.Physical - FCurLinePhysStartOffset;
ATokenInfo.PhysicalCharEnd := Min(FCurViewScannerPhysCharPos - FCurLinePhysStartOffset+ i, FVirtualFirstCol);
ATokenInfo.PhysicalClipEnd := Min(ATokenInfo.StartPos.Physical - FCurLinePhysStartOffset+ i, FVirtualFirstCol);
FWrapStartBound := FCurViewScannerPos;
FWrapEndBound.Logical := -1;
FWrapEndBound.Offset := 0;
FWrapEndBound.Physical := FCurViewScannerPos.Physical + i + (FCurViewScannerPhysCharPos - (FCurLinePhysStartOffset + 1));
if FCurViewToken.TokenAttr <> nil then begin
FrameStartPos.Logical := 0;
FrameStartPos.Physical := 1 + FCurLinePhysStartOffset;
FrameStartPos.Offset := 0;
InitSynAttr(FCurViewAttr, FCurViewToken.TokenAttr, FrameStartPos);
FCurViewAttr.EndX := FWrapEndBound;
ATokenInfo.Attr := FCurViewAttr;
end
else
ATokenInfo.Attr := nil;
FVirtualFirstCol := FVirtualFirstCol - i;
FVirtualLastCol := FVirtualLastCol - i;
dec(FCurLinePhysStartOffset, i);
ATokenInfo.ExpandedExtraBytes := 0;
ATokenInfo.HasTabs := False; /////////
ATokenInfo.HasDoubleWidth := False;
ATokenInfo.NeedsEto := False;
ATokenInfo.NextPos := ATokenInfo.StartPos;
FCurViewToken.TokenLength := 0;
exit;
end;
if not Result then begin if not Result then begin
ATokenInfo.StartPos := FCurViewScannerPos; ATokenInfo.StartPos := FCurViewScannerPos;
ATokenInfo.RtlInfo.IsRtl := False; ATokenInfo.RtlInfo.IsRtl := False;
@ -864,10 +950,10 @@ begin
ATokenInfo.EndPos.Physical := Min(PhysPos, PhysTokenStop); ATokenInfo.EndPos.Physical := Min(PhysPos, PhysTokenStop);
ATokenInfo.EndPos.Offset := ATokenInfo.EndPos.Physical - PhysPos; // Zero or Negative. Paint ends before Logical ATokenInfo.EndPos.Offset := ATokenInfo.EndPos.Physical - PhysPos; // Zero or Negative. Paint ends before Logical
ATokenInfo.PhysicalCharStart := FCurViewScannerPhysCharPos - FCurLinePhysStart; ATokenInfo.PhysicalCharStart := FCurViewScannerPhysCharPos - FCurLinePhysStartOffset;
ATokenInfo.PhysicalClipStart := ATokenInfo.StartPos.Physical - FCurLinePhysStart; ATokenInfo.PhysicalClipStart := ATokenInfo.StartPos.Physical - FCurLinePhysStartOffset;
ATokenInfo.PhysicalCharEnd := PhysPos - FCurLinePhysStart; ATokenInfo.PhysicalCharEnd := PhysPos - FCurLinePhysStartOffset;
ATokenInfo.PhysicalClipEnd := ATokenInfo.EndPos.Physical - FCurLinePhysStart; ATokenInfo.PhysicalClipEnd := ATokenInfo.EndPos.Physical - FCurLinePhysStartOffset;
ATokenInfo.RtlInfo.IsRtl := False; ATokenInfo.RtlInfo.IsRtl := False;
//ATokenInfo.RtlInfo.PhysLeft := FCurViewRtlPhysStart; //ATokenInfo.RtlInfo.PhysLeft := FCurViewRtlPhysStart;
//ATokenInfo.RtlInfo.PhysRight := FCurViewRtlPhysEnd; //ATokenInfo.RtlInfo.PhysRight := FCurViewRtlPhysEnd;
@ -997,10 +1083,10 @@ begin
ATokenInfo.EndPos.Physical := Max(PhysPos, PhysTokenStop); ATokenInfo.EndPos.Physical := Max(PhysPos, PhysTokenStop);
ATokenInfo.EndPos.Offset := PhysPos - ATokenInfo.EndPos.Physical; // <= 0 ATokenInfo.EndPos.Offset := PhysPos - ATokenInfo.EndPos.Physical; // <= 0
ATokenInfo.PhysicalCharStart := PhysPos - FCurLinePhysStart; ATokenInfo.PhysicalCharStart := PhysPos - FCurLinePhysStartOffset;
ATokenInfo.PhysicalClipStart := ATokenInfo.EndPos.Physical - FCurLinePhysStart; ATokenInfo.PhysicalClipStart := ATokenInfo.EndPos.Physical - FCurLinePhysStartOffset;
ATokenInfo.PhysicalCharEnd := FCurViewScannerPhysCharPos - FCurLinePhysStart; ATokenInfo.PhysicalCharEnd := FCurViewScannerPhysCharPos - FCurLinePhysStartOffset;
ATokenInfo.PhysicalClipEnd := ATokenInfo.StartPos.Physical - FCurLinePhysStart; ATokenInfo.PhysicalClipEnd := ATokenInfo.StartPos.Physical - FCurLinePhysStartOffset;
ATokenInfo.RtlInfo.IsRtl := True; ATokenInfo.RtlInfo.IsRtl := True;
ATokenInfo.RtlInfo.PhysLeft := FCurViewRtlPhysStart; ATokenInfo.RtlInfo.PhysLeft := FCurViewRtlPhysStart;
ATokenInfo.RtlInfo.PhysRight := FCurViewRtlPhysEnd; ATokenInfo.RtlInfo.PhysRight := FCurViewRtlPhysEnd;

View File

@ -184,6 +184,7 @@ type
FCurSubLinePhysStartIdx, FPrevSubLinePhysWidth: Integer; FCurSubLinePhysStartIdx, FPrevSubLinePhysWidth: Integer;
FCurToken: TLazSynDisplayTokenInfo; FCurToken: TLazSynDisplayTokenInfo;
FCurLineLogIdx: Integer; FCurLineLogIdx: Integer;
FCurLineWrapIndentString: String;
public public
constructor Create(AWrappedView: TSynEditLineMappingView; AWrapPlugin: TLazSynEditLineWrapPlugin); constructor Create(AWrappedView: TSynEditLineMappingView; AWrapPlugin: TLazSynEditLineWrapPlugin);
//destructor Destroy; override; //destructor Destroy; override;
@ -199,11 +200,21 @@ type
FCurrentWrapColumn: Integer; FCurrentWrapColumn: Integer;
FCaretWrapPos: TLazSynEditWrapCaretPos; FCaretWrapPos: TLazSynEditWrapCaretPos;
FMinWrapWidth: Integer; FMinWrapWidth: Integer;
FWrapIndentMaxAbs: Integer;
FWrapIndentMaxRel: Integer;
FWrapIndentMinAbs: Integer;
FWrapIndentIsOffset: Boolean;
FWrapIndentWidth: Integer;
procedure DoLinesChanged(Sender: TObject); procedure DoLinesChanged(Sender: TObject);
procedure DoWidthChanged(Sender: TObject; Changes: TSynStatusChanges); procedure DoWidthChanged(Sender: TObject; Changes: TSynStatusChanges);
function GetWrapColumn: Integer; function GetWrapColumn: Integer;
procedure SetWrapIndentMaxAbs(AValue: Integer);
procedure SetWrapIndentMaxRel(AValue: Integer);
procedure SetWrapIndentMinAbs(AValue: Integer);
procedure SetMinWrapWidth(AValue: Integer); procedure SetMinWrapWidth(AValue: Integer);
procedure SetWrapIndentIsOffset(AValue: Boolean);
procedure SetWrapIndentWidth(AValue: Integer);
public public
FLineMapView: TSynEditLineMappingView; FLineMapView: TSynEditLineMappingView;
function CreatePageMapNode(AMapTree: TSynLineMapAVLTree function CreatePageMapNode(AMapTree: TSynLineMapAVLTree
@ -211,13 +222,14 @@ public
protected protected
procedure SetEditor(const AValue: TCustomSynEdit); override; procedure SetEditor(const AValue: TCustomSynEdit); override;
function CalculateIndentFor(ALine: PChar; AMaxWidth: Integer; const PhysCharWidths: TPhysicalCharWidths): Integer;
function CalculateNextBreak(ALine: PChar; ALogStartFrom: IntIdx; AMaxWidth: Integer; function CalculateNextBreak(ALine: PChar; ALogStartFrom: IntIdx; AMaxWidth: Integer;
const PhysCharWidths: TPhysicalCharWidths; out APhysWidth: Integer): IntIdx; const PhysCharWidths: TPhysicalCharWidths; out APhysWidth: Integer): IntIdx;
function GetSublineCount (ALine: String; AMaxWidth: Integer; const APhysCharWidths: TPhysicalCharWidths): Integer; inline; function GetSublineCount (ALine: String; AMaxWidth: Integer; const APhysCharWidths: TPhysicalCharWidths): Integer; inline;
procedure GetSublineBounds(ALine: String; AMaxWidth: Integer; procedure GetSublineBounds(ALine: String; AMaxWidth, AWrapIndent: Integer;
const APhysCharWidths: TPhysicalCharWidths; ASubLine: Integer; out ALogStartX, const APhysCharWidths: TPhysicalCharWidths; ASubLine: Integer; out ALogStartX,
ANextLogStartX, APhysStart: IntIdx; out APhysWidth: integer); ANextLogStartX, APhysStart: IntIdx; out APhysWidth: integer);
function GetSubLineFromX (ALine: String; AMaxWidth: Integer; const APhysCharWidths: TPhysicalCharWidths; var APhysToViewedXPos: Integer): integer; function GetSubLineFromX (ALine: String; AMaxWidth, AWrapIndent: Integer; const APhysCharWidths: TPhysicalCharWidths; var APhysToViewedXPos: Integer): integer;
procedure GetWrapInfoForViewedXY(var AViewedXY: TViewedPoint; AFlags: TViewedXYInfoFlags; out AFirstViewedX: IntPos; ALogPhysConvertor: TSynLogicalPhysicalConvertor); procedure GetWrapInfoForViewedXY(var AViewedXY: TViewedPoint; AFlags: TViewedXYInfoFlags; out AFirstViewedX: IntPos; ALogPhysConvertor: TSynLogicalPhysicalConvertor);
@ -235,7 +247,13 @@ public
published published
property CaretWrapPos: TLazSynEditWrapCaretPos read FCaretWrapPos write FCaretWrapPos; property CaretWrapPos: TLazSynEditWrapCaretPos read FCaretWrapPos write FCaretWrapPos;
property MinWrapWidth: Integer read FMinWrapWidth write SetMinWrapWidth; property MinWrapWidth: Integer read FMinWrapWidth write SetMinWrapWidth;
property WrapIndentWidth: Integer read FWrapIndentWidth write SetWrapIndentWidth;
property WrapIndentIsOffset: Boolean read FWrapIndentIsOffset write SetWrapIndentIsOffset;
property WrapIndentMinAbs: Integer read FWrapIndentMinAbs write SetWrapIndentMinAbs;
property WrapIndentMaxAbs: Integer read FWrapIndentMaxAbs write SetWrapIndentMaxAbs;
property WrapIndentMaxRel: Integer read FWrapIndentMaxRel write SetWrapIndentMaxRel;
end; end;
implementation implementation
@ -1491,7 +1509,7 @@ var
PrevSub: IntIdx; PrevSub: IntIdx;
LineTxt: String; LineTxt: String;
PWidth: TPhysicalCharWidths; PWidth: TPhysicalCharWidths;
PhysWidth, MaxW: Integer; PhysWidth, MaxW, CurLineWrapInd: Integer;
begin begin
IsNext := (AWrappedLine = FCurWrappedLine + 1) and (FCurWrappedLine >= 0); IsNext := (AWrappedLine = FCurWrappedLine + 1) and (FCurWrappedLine >= 0);
PrevSub := FCurrentWrapSubline; PrevSub := FCurrentWrapSubline;
@ -1503,22 +1521,32 @@ begin
PWidth := FLineMappingView.LogPhysConvertor.CurrentWidthsDirect; PWidth := FLineMappingView.LogPhysConvertor.CurrentWidthsDirect;
//PWidth := FLineMappingView.GetPhysicalCharWidths(ARealLine); //PWidth := FLineMappingView.GetPhysicalCharWidths(ARealLine);
MaxW := FWrapPlugin.WrapColumn; MaxW := FWrapPlugin.WrapColumn;
CurLineWrapInd := 0;
if (FCurrentWrapSubline > 0) then begin
CurLineWrapInd := FWrapPlugin.CalculateIndentFor(PChar(LineTxt), MaxW, PWidth);
FCurLineWrapIndentString := StringOfChar(' ', CurLineWrapInd);
assert(CurLineWrapInd>=0, 'TLazSynEditLineWrapPlugin.GetSublineCount: CurLineWrapInd>=0');
end;
if IsNext and (FCurrentWrapSubline = PrevSub + 1) then begin if IsNext and (FCurrentWrapSubline = PrevSub + 1) then begin
FCurSubLineLogStartIdx := FCurSubLineNextLogStartIdx; FCurSubLineLogStartIdx := FCurSubLineNextLogStartIdx;
// 2nd or lower line / deduct CurLineWrapInd
FCurSubLineNextLogStartIdx := FWrapPlugin.CalculateNextBreak(PChar(LineTxt), FCurSubLineNextLogStartIdx, FCurSubLineNextLogStartIdx := FWrapPlugin.CalculateNextBreak(PChar(LineTxt), FCurSubLineNextLogStartIdx,
MaxW, PWidth, PhysWidth); MaxW-CurLineWrapInd, PWidth, PhysWidth);
FCurSubLinePhysStartIdx := FCurSubLinePhysStartIdx + FPrevSubLinePhysWidth; FCurSubLinePhysStartIdx := FCurSubLinePhysStartIdx + FPrevSubLinePhysWidth;
FPrevSubLinePhysWidth := PhysWidth; FPrevSubLinePhysWidth := PhysWidth;
end end
else begin else begin
FWrapPlugin.GetSublineBounds(LineTxt, MaxW, PWidth, FCurrentWrapSubline, // CurLineWrapInd may be incorrectly 0 / but only if retriving bounds for a "first line"
FWrapPlugin.GetSublineBounds(LineTxt, MaxW, CurLineWrapInd, PWidth, FCurrentWrapSubline,
FCurSubLineLogStartIdx, FCurSubLineNextLogStartIdx, FCurSubLinePhysStartIdx, FPrevSubLinePhysWidth); FCurSubLineLogStartIdx, FCurSubLineNextLogStartIdx, FCurSubLinePhysStartIdx, FPrevSubLinePhysWidth);
end; end;
AStartBytePos := AStartBytePos + FCurSubLineLogStartIdx; AStartBytePos := AStartBytePos + FCurSubLineLogStartIdx;
AStartPhysPos := ToPos(FCurSubLinePhysStartIdx); AStartPhysPos := ToPos(FCurSubLinePhysStartIdx);
ALineByteLen := FCurSubLineNextLogStartIdx - FCurSubLineLogStartIdx; ALineByteLen := FCurSubLineNextLogStartIdx - FCurSubLineLogStartIdx;
FCurLineLogIdx := 0; FCurLineLogIdx := -CurLineWrapInd;
end; end;
function TLazSynDisplayWordWrap.GetNextHighlighterToken(out function TLazSynDisplayWordWrap.GetNextHighlighterToken(out
@ -1527,6 +1555,16 @@ var
PreStart: Integer; PreStart: Integer;
begin begin
ATokenInfo := Default(TLazSynDisplayTokenInfo); ATokenInfo := Default(TLazSynDisplayTokenInfo);
if FCurLineLogIdx < 0 then begin
Result := True;
ATokenInfo.TokenStart := PChar(FCurLineWrapIndentString);
ATokenInfo.TokenLength := -FCurLineLogIdx;
ATokenInfo.TokenAttr := nil;
ATokenInfo.TokenOrigin := dtoBeforeText;
FCurLineLogIdx := 0;
exit;
end;
If (FCurLineLogIdx >= FCurSubLineNextLogStartIdx) and (FCurSubLineNextLogStartIdx < FCurRealLineByteLen) then begin If (FCurLineLogIdx >= FCurSubLineNextLogStartIdx) and (FCurSubLineNextLogStartIdx < FCurRealLineByteLen) then begin
ATokenInfo.TokenOrigin := dtoAfterWrapped; ATokenInfo.TokenOrigin := dtoAfterWrapped;
Result := True; // TokenStart = nil => no text Result := True; // TokenStart = nil => no text
@ -1594,6 +1632,33 @@ begin
Result := FMinWrapWidth; Result := FMinWrapWidth;
end; end;
procedure TLazSynEditLineWrapPlugin.SetWrapIndentMaxAbs(AValue: Integer);
begin
if AValue < 0 then
AValue := 0;
if FWrapIndentMaxAbs = AValue then Exit;
FWrapIndentMaxAbs := AValue;
WrapAll;
end;
procedure TLazSynEditLineWrapPlugin.SetWrapIndentMaxRel(AValue: Integer);
begin
if AValue > 100 then
AValue := 100;
if FWrapIndentMaxRel = AValue then Exit;
FWrapIndentMaxRel := AValue;
WrapAll;
end;
procedure TLazSynEditLineWrapPlugin.SetWrapIndentMinAbs(AValue: Integer);
begin
if AValue < 0 then
AValue := 0;
if FWrapIndentMinAbs = AValue then Exit;
FWrapIndentMinAbs := AValue;
WrapAll;
end;
procedure TLazSynEditLineWrapPlugin.SetMinWrapWidth(AValue: Integer); procedure TLazSynEditLineWrapPlugin.SetMinWrapWidth(AValue: Integer);
begin begin
if AValue < 1 then if AValue < 1 then
@ -1603,6 +1668,21 @@ begin
DoWidthChanged(nil, [scCharsInWindow]); DoWidthChanged(nil, [scCharsInWindow]);
end; end;
procedure TLazSynEditLineWrapPlugin.SetWrapIndentIsOffset(AValue: Boolean);
begin
if FWrapIndentIsOffset = AValue then Exit;
FWrapIndentIsOffset := AValue;
WrapAll;
end;
procedure TLazSynEditLineWrapPlugin.SetWrapIndentWidth(AValue: Integer);
begin
// can be negative
if FWrapIndentWidth = AValue then Exit;
FWrapIndentWidth := AValue;
WrapAll;
end;
function TLazSynEditLineWrapPlugin.CreatePageMapNode(AMapTree: TSynLineMapAVLTree): TSynEditLineMapPage; function TLazSynEditLineWrapPlugin.CreatePageMapNode(AMapTree: TSynLineMapAVLTree): TSynEditLineMapPage;
begin begin
Result := TSynWordWrapIndexPage.Create(AMapTree); Result := TSynWordWrapIndexPage.Create(AMapTree);
@ -1616,6 +1696,39 @@ begin
inherited SetEditor(AValue); inherited SetEditor(AValue);
end; end;
function TLazSynEditLineWrapPlugin.CalculateIndentFor(ALine: PChar; AMaxWidth: Integer;
const PhysCharWidths: TPhysicalCharWidths): Integer;
var
i: Integer;
begin
if FWrapIndentIsOffset then begin
Result := 0;
i := CountLeadWhiteSpace(ALine);
while i > 0 do begin
dec(i);
Result := Result + (PhysCharWidths[i] and PCWMask);
end;
Result := Result + FWrapIndentWidth;
if Result < FWrapIndentMinAbs then
Result := FWrapIndentMinAbs;
end
else
Result := FWrapIndentWidth;
if FWrapIndentMaxAbs > 0 then
Result := Min(FWrapIndentMaxAbs, Result);
if FWrapIndentMaxRel > 0 then
Result := Min(MulDiv(AMaxWidth, FWrapIndentMaxRel, 100), Result)
else
if FWrapIndentMaxRel < 0 then
Result := Min(Max(0, AMaxWidth + FWrapIndentMaxRel), Result); // keep at least n columns visible
Result := Max(0, Result);
if Result >= AMaxWidth then
Result := Max(0, AMaxWidth-1);
end;
function TLazSynEditLineWrapPlugin.CalculateNextBreak(ALine: PChar; function TLazSynEditLineWrapPlugin.CalculateNextBreak(ALine: PChar;
ALogStartFrom: IntIdx; AMaxWidth: Integer; ALogStartFrom: IntIdx; AMaxWidth: Integer;
const PhysCharWidths: TPhysicalCharWidths; out APhysWidth: Integer): IntIdx; const PhysCharWidths: TPhysicalCharWidths; out APhysWidth: Integer): IntIdx;
@ -1699,15 +1812,17 @@ begin
if Length(ALine) = 0 then if Length(ALine) = 0 then
exit; exit;
x := CalculateNextBreak(PChar(ALine), 0, AMaxWidth, APhysCharWidths, dummy); x := CalculateNextBreak(PChar(ALine), 0, AMaxWidth, APhysCharWidths, dummy);
AMaxWidth := AMaxWidth - CalculateIndentFor(PChar(ALine), AMaxWidth, APhysCharWidths);
assert(AMaxWidth>0, 'TLazSynEditLineWrapPlugin.GetSublineCount: AMaxWidth>0');
while (x < Length(ALine)) do begin while (x < Length(ALine)) do begin
inc(Result); inc(Result);
x := CalculateNextBreak(PChar(ALine), x, AMaxWidth, APhysCharWidths, dummy); x := CalculateNextBreak(PChar(ALine), x, AMaxWidth, APhysCharWidths, dummy);
end; end;
end; end;
procedure TLazSynEditLineWrapPlugin.GetSublineBounds(ALine: String; procedure TLazSynEditLineWrapPlugin.GetSublineBounds(ALine: String; AMaxWidth,
AMaxWidth: Integer; const APhysCharWidths: TPhysicalCharWidths; ASubLine: Integer; AWrapIndent: Integer; const APhysCharWidths: TPhysicalCharWidths; ASubLine: Integer; out
out ALogStartX, ANextLogStartX, APhysStart: IntIdx; out APhysWidth: integer); ALogStartX, ANextLogStartX, APhysStart: IntIdx; out APhysWidth: integer);
begin begin
ALogStartX := 0; ALogStartX := 0;
ANextLogStartX := 0; ANextLogStartX := 0;
@ -1715,6 +1830,9 @@ begin
if Length(ALine) = 0 then if Length(ALine) = 0 then
exit; exit;
ANextLogStartX := CalculateNextBreak(PChar(ALine), ALogStartX, AMaxWidth, APhysCharWidths, APhysWidth); ANextLogStartX := CalculateNextBreak(PChar(ALine), ALogStartX, AMaxWidth, APhysCharWidths, APhysWidth);
AMaxWidth := AMaxWidth - AWrapIndent;
assert(AMaxWidth>0, 'TLazSynEditLineWrapPlugin.GetSublineBounds: AMaxWidth>0');
while ASubLine > 0 do begin while ASubLine > 0 do begin
ALogStartX := ANextLogStartX; ALogStartX := ANextLogStartX;
APhysStart := APhysStart + APhysWidth; APhysStart := APhysStart + APhysWidth;
@ -1723,21 +1841,25 @@ begin
end; end;
end; end;
function TLazSynEditLineWrapPlugin.GetSubLineFromX(ALine: String; function TLazSynEditLineWrapPlugin.GetSubLineFromX(ALine: String; AMaxWidth, AWrapIndent: Integer;
AMaxWidth: Integer; const APhysCharWidths: TPhysicalCharWidths; const APhysCharWidths: TPhysicalCharWidths; var APhysToViewedXPos: Integer): integer;
var APhysToViewedXPos: Integer): integer;
var var
x, PhysWidth: Integer; x, PhysWidth, AMaxWidth2: Integer;
begin begin
Result := 0; Result := 0;
if Length(ALine) = 0 then if Length(ALine) = 0 then
exit; exit;
Result := -1; Result := -1;
x := 0; x := 0;
AMaxWidth2 := AMaxWidth - AWrapIndent;
assert(AMaxWidth2>0, 'TLazSynEditLineWrapPlugin.GetSublineCount: AMaxWidth>0');
APhysToViewedXPos := ToIdx(APhysToViewedXPos); APhysToViewedXPos := ToIdx(APhysToViewedXPos);
while (x < Length(ALine)) do begin while (x < Length(ALine)) do begin
inc(Result); inc(Result);
x := CalculateNextBreak(PChar(ALine), x, AMaxWidth, APhysCharWidths, PhysWidth); x := CalculateNextBreak(PChar(ALine), x, AMaxWidth, APhysCharWidths, PhysWidth);
AMaxWidth := AMaxWidth2;
if x >= Length(ALine) then if x >= Length(ALine) then
break; break;
if (FCaretWrapPos = wcpBOL) and (PhysWidth = APhysToViewedXPos) and (x < Length(ALine)) if (FCaretWrapPos = wcpBOL) and (PhysWidth = APhysToViewedXPos) and (x < Length(ALine))
@ -1761,7 +1883,7 @@ var
LineTxt: String; LineTxt: String;
PWidth: TPhysicalCharWidths; PWidth: TPhysicalCharWidths;
LogX, NextLogX, PhysX: IntIdx; LogX, NextLogX, PhysX: IntIdx;
PhysWidth: Integer; PhysWidth, WrapInd, AMaxWidth: Integer;
begin begin
YIdx := FLineMapView.Tree.GetLineForForWrap(ToIdx(AViewedXY.y), SubLineOffset); YIdx := FLineMapView.Tree.GetLineForForWrap(ToIdx(AViewedXY.y), SubLineOffset);
@ -1770,8 +1892,10 @@ begin
LineTxt := FLineMapView.Strings[YIdx]; LineTxt := FLineMapView.Strings[YIdx];
ALogPhysConvertor.CurrentLine := YIdx; ALogPhysConvertor.CurrentLine := YIdx;
PWidth := ALogPhysConvertor.CurrentWidthsDirect; PWidth := ALogPhysConvertor.CurrentWidthsDirect;
AMaxWidth := WrapColumn;
WrapInd := CalculateIndentFor(PChar(LineTxt), AMaxWidth, PWidth);
GetSublineBounds(LineTxt, WrapColumn, PWidth, SubLineOffset, LogX, NextLogX, PhysX, PhysWidth); GetSublineBounds(LineTxt, AMaxWidth, WrapInd, PWidth, SubLineOffset, LogX, NextLogX, PhysX, PhysWidth);
case CaretWrapPos of case CaretWrapPos of
wcpEOL: begin wcpEOL: begin
@ -1795,33 +1919,41 @@ end;
function TLazSynEditLineWrapPlugin.TextXYToLineXY(ATextXY: TPhysPoint function TLazSynEditLineWrapPlugin.TextXYToLineXY(ATextXY: TPhysPoint
): TViewedSubPoint_0; ): TViewedSubPoint_0;
var
AMaxWidth, WrapInd: Integer;
ALine: String;
APhysCharWidths: TPhysicalCharWidths;
begin begin
FLineMapView.LogPhysConvertor.CurrentLine := ATextXY.y; FLineMapView.LogPhysConvertor.CurrentLine := ATextXY.y;
ALine := FLineMapView.NextLines.Strings[ATextXY.y];
APhysCharWidths := FLineMapView.LogPhysConvertor.CurrentWidthsDirect;
AMaxWidth := WrapColumn;
WrapInd := CalculateIndentFor(PChar(ALine), AMaxWidth, APhysCharWidths);
Result.x := ATextXY.x; Result.x := ATextXY.x;
Result.y := Result.y :=
GetSubLineFromX(FLineMapView.NextLines.Strings[ATextXY.y], GetSubLineFromX(ALine, AMaxWidth, WrapInd, APhysCharWidths, Result.x);
WrapColumn, if Result.x <> ATextXY.x then
FLineMapView.LogPhysConvertor.CurrentWidthsDirect, Result.x := Result.x + WrapInd; // Result is on sub-line
Result.x
);
end; end;
function TLazSynEditLineWrapPlugin.LineXYToTextX(ARealLine: IntPos; function TLazSynEditLineWrapPlugin.LineXYToTextX(ARealLine: IntPos;
ALineXY: TViewedSubPoint_0): Integer; ALineXY: TViewedSubPoint_0): Integer;
var var
ANextLogX, APhysWidth: Integer; AMaxWidth, WrapInd, ANextLogX, APhysWidth: Integer;
ALine: String; ALine: String;
APhysCharWidths: TPhysicalCharWidths; APhysCharWidths: TPhysicalCharWidths;
dummy: integer; dummy: integer;
begin begin
FLineMapView.LogPhysConvertor.CurrentLine := ARealLine; FLineMapView.LogPhysConvertor.CurrentLine := ARealLine;
ALine := FLineMapView.NextLines.Strings[ARealLine]; ALine := FLineMapView.NextLines.Strings[ARealLine];
GetSublineBounds(ALine, APhysCharWidths := FLineMapView.LogPhysConvertor.CurrentWidthsDirect;
WrapColumn, AMaxWidth := WrapColumn;
FLineMapView.LogPhysConvertor.CurrentWidthsDirect, WrapInd := CalculateIndentFor(PChar(ALine), AMaxWidth, APhysCharWidths);
ALineXY.y, dummy, ANextLogX, Result, APhysWidth
);
GetSublineBounds(ALine, AMaxWidth, WrapInd, APhysCharWidths, ALineXY.y, dummy, ANextLogX, Result, APhysWidth);
if (ALineXY.y > 0) then
ALineXY.x := max(1, ALineXY.x -WrapInd);
if (ALineXY.y > 0) and (ALineXY.X <= 1) and (FCaretWrapPos = wcpEOL) then begin if (ALineXY.y > 0) and (ALineXY.X <= 1) and (FCaretWrapPos = wcpEOL) then begin
ALineXY.x := 2; ALineXY.x := 2;
end end