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

View File

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