diff --git a/components/synedit/lazsyntextarea.pp b/components/synedit/lazsyntextarea.pp index af69086816..25e35faf1b 100644 --- a/components/synedit/lazsyntextarea.pp +++ b/components/synedit/lazsyntextarea.pp @@ -35,6 +35,7 @@ type ExpandedExtraBytes: Integer; // tab and space expansion HasTabs: Boolean; // ExtraWidth may still be 0 HasDoubleWidth: Boolean; + NeedsEto: Boolean; NextPos: TLazSynDisplayTokenBound; // Next toxen, may be BIDI NextRtlInfo: TLazSynDisplayRtlInfo; @@ -463,6 +464,7 @@ begin ATokenInfo.ExpandedExtraBytes := 0; ATokenInfo.HasTabs := False; ATokenInfo.HasDoubleWidth := False; // TODO: True, but needs charwidth for painter + ATokenInfo.NeedsEto := False; end else begin if ATokenInfo.NextRtlInfo.IsRtl <> FCurMarkupNextRtlInfo.IsRtl then begin @@ -740,7 +742,7 @@ var PrevLogicIdx, PrevPhysPos: Integer; PhysTokenStop: Integer; TabExtra: Integer; - HasTabs, HasDouble: Boolean; + HasTabs, HasDouble, NeedsEto: Boolean; begin ATokenInfo.Attr := nil; while True do begin @@ -762,6 +764,7 @@ begin LogicEnd := LogicIdx + FCurViewToken.TokenLength; //assert(GetCharWidthData(LogicIdx)<>0, 'GetNextHighlighterTokenFromView: Token starts with char'); + NeedsEto := False; case FCurViewinRTL of False: // Left To Right begin @@ -812,8 +815,13 @@ begin else if j > 1 then HasDouble := True; - if c = ' ' then + if c = ' ' then begin inc(TabExtra, FSpaceExtraByteCount); + {$IfDef WINDOWS} + if not NeedsEto then + NeedsEto := IsCombiningCodePoint(FCurViewToken.TokenStart + i + 1); + {$ENDIF} + end; end; repeat @@ -851,6 +859,7 @@ begin ATokenInfo.ExpandedExtraBytes := TabExtra; ATokenInfo.HasTabs := HasTabs; ATokenInfo.HasDoubleWidth := HasDouble; + ATokenInfo.NeedsEto := NeedsEto; assert(ATokenInfo.StartPos.Offset >= 0, 'FCurViewScannerPos.Offset >= 0'); assert(ATokenInfo.EndPos.Offset <= 0, 'FCurViewToken.EndPos.Offset <= 0'); @@ -937,8 +946,13 @@ begin else if j > 1 then HasDouble := True; - if c = ' ' then + if c = ' ' then begin inc(TabExtra, FSpaceExtraByteCount); + {$IfDef WINDOWS} + if not NeedsEto then + NeedsEto := IsCombiningCodePoint(FCurViewToken.TokenStart + i + 1); + {$ENDIF} + end; end; repeat @@ -979,6 +993,7 @@ begin ATokenInfo.ExpandedExtraBytes := TabExtra; ATokenInfo.HasTabs := HasTabs; ATokenInfo.HasDoubleWidth := HasDouble; + ATokenInfo.NeedsEto := NeedsEto; assert(ATokenInfo.StartPos.Offset >= 0, 'FCurViewScannerPos.Offset >= 0'); assert(ATokenInfo.EndPos.Offset <= 0, 'FCurViewToken.EndPos.Offset <= 0'); @@ -1634,7 +1649,7 @@ var end; NeedExpansion := (ATokenInfo.ExpandedExtraBytes > 0) or (ATokenInfo.HasTabs); - NeedTransform := FTextDrawer.NeedsEto or ATokenInfo.HasDoubleWidth or NeedExpansion + NeedTransform := FTextDrawer.NeedsEto or ATokenInfo.HasDoubleWidth or ATokenInfo.NeedsEto or NeedExpansion {$IFDEF Windows} or ATokenInfo.RtlInfo.IsRtl {$ENDIF} ; Len := ATokenInfo.Tk.TokenLength; @@ -1663,7 +1678,7 @@ var end; // Prepare FETOBuf - if FTextDrawer.NeedsEto or ATokenInfo.HasDoubleWidth + if FTextDrawer.NeedsEto or ATokenInfo.HasDoubleWidth or ATokenInfo.NeedsEto {$IFDEF Windows} or ATokenInfo.RtlInfo.IsRtl {$ENDIF} // RTL may have script with ligature then begin FEtoBuf := FTextDrawer.Eto; @@ -1705,7 +1720,9 @@ var if FetoBuf <> nil then FEtoBuf.EtoData[e] := c; inc(e); end; - if (vscTabAtLast in FVisibleSpecialChars) and ((pl-1)^=' ') and (j < CWLen) then begin + if (vscTabAtLast in FVisibleSpecialChars) and ((pl-1)^=' ') and (j < CWLen) and + (not IsCombiningCodePoint(pt+1)) + then begin (pl-1)^ := #194; pl^ := #187; inc(pl); if FetoBuf <> nil then FEtoBuf.EtoData[e] := c; @@ -1713,7 +1730,9 @@ var end; end; ' ': begin - if (vscSpace in FVisibleSpecialChars) and (j < CWLen) then begin + if (vscSpace in FVisibleSpecialChars) and (j < CWLen) and + (not IsCombiningCodePoint(pt+1)) + then begin pl^ := #194; inc(pl); pl^ := #183; inc(pl); end diff --git a/components/synedit/synbeautifier.pas b/components/synedit/synbeautifier.pas index 74b9de42b4..18fe84fcbf 100644 --- a/components/synedit/synbeautifier.pas +++ b/components/synedit/synbeautifier.pas @@ -43,7 +43,7 @@ uses Classes, SysUtils, LazStringUtils, SynEditMiscClasses, LazSynEditText, SynEditPointClasses, - SynEditKeyCmds, SynEditTypes; + SynEditKeyCmds, SynEditTypes, SynEditMiscProcs; type @@ -672,11 +672,7 @@ var begin p := PChar(Line); if Assigned(p) then begin - Result := 0; - while p^ in [#1..#32] do begin - Inc(p); - Inc(Result); - end; + Result := CountLeadWhiteSpace(p); if Physical and (Result>0) then Result := FCurrentLines.LogicalToPhysicalCol(Line, -1, Result+1)-1; // TODO, Need the real index of the line end else diff --git a/components/synedit/synedit.pp b/components/synedit/synedit.pp index ff3c950de0..b348070576 100644 --- a/components/synedit/synedit.pp +++ b/components/synedit/synedit.pp @@ -5805,9 +5805,7 @@ begin ALine:=FTheLinesView[FBlockSelection.StartLinePos - 1]; x2:=length(ALine)+1; if not WithLeadSpaces then begin - x := FBlockSelection.StartBytePos; - while (x x) and (ALine[X2-1] in [' ',#9]) do dec(x2); @@ -7476,7 +7474,9 @@ begin Temp := Clipboard.AsText; Helper := SelText; if (Temp <> '') and (not (Temp[Length(Temp)] in [#10,#13, #9, #32])) and - (not (Helper[1] in [#10,#13, #9, #32])) + not( (Helper[1] in [#10,#13, #9, #32]) and + ( (Length(Helper) = 1) or (not IsCombiningCodePoint(PChar(Helper)+1))) + ) then Temp := Temp + ' '; Clipboard.AsText := Temp + Helper; @@ -9010,6 +9010,8 @@ begin // scan over whitespaces while (p^ in [#9, #32]) do inc(p); + if IsCombiningCodePoint(p) then + dec(p); i := LogicalToPhysicalCol(PrevLine, iLine, p-@PrevLine[1]+1) - CaretX; end; end; @@ -9321,7 +9323,7 @@ begin j := 0; for i := 1 to FBlockTabIndent do begin i2 := fTabWidth; - while (i2 > 0) and (Line[j] = #32) do begin + while (i2 > 0) and IsSpaceChar(Line+j) do begin dec(i2); inc(j); end; @@ -9537,9 +9539,7 @@ begin s := FTheLinesView[CaretXY.Y - 1]; // search first non blank char pos - FirstNonBlank := 1; - while (FirstNonBlank <= length(s)) and (s[FirstNonBlank] in [#32, #9]) do - inc(FirstNonBlank); + FirstNonBlank := CountLeadWhiteSpace(PChar(s)) + 1; if FirstNonBlank > length(s) then FirstNonBlank := -1; end else diff --git a/components/synedit/syneditmarkupfoldcoloring.pas b/components/synedit/syneditmarkupfoldcoloring.pas index 08441fa9c7..b34312849a 100644 --- a/components/synedit/syneditmarkupfoldcoloring.pas +++ b/components/synedit/syneditmarkupfoldcoloring.pas @@ -543,15 +543,11 @@ function TSynEditMarkupFoldColors.GetFirstCharacterColumn(pIndex: Integer ): TColumnCacheEntry; var l: String; - s, p: Integer; + p: Integer; begin l := SynEdit.Lines[pIndex]; - s := length(l); - p := 1; - while (p <= s) - //and (l[p] in [#9, #32, '/']) do inc(p); - and (l[p] in [#9, #32]) do inc(p); - if p > s then + p := CountLeadWhiteSpace(PChar(l)) + 1; + if p > length(l) then exit(high(Result)); Result := SynEdit.LogicalToPhysicalPos(Point(p, toPos(pIndex))).x; end; diff --git a/components/synedit/syneditmarkupspecialchar.pp b/components/synedit/syneditmarkupspecialchar.pp index e3bfb26996..453c9c0bf6 100644 --- a/components/synedit/syneditmarkupspecialchar.pp +++ b/components/synedit/syneditmarkupspecialchar.pp @@ -27,7 +27,7 @@ interface uses Classes, SysUtils, Graphics, Controls, - SynEditMarkup, SynEditTypes, SynEditMiscClasses; + SynEditMarkup, SynEditTypes, SynEditMiscClasses, SynEditMiscProcs; type @@ -87,9 +87,10 @@ end; function TSynEditMarkupSpecialChar.IsSpecial(pos: Integer): Boolean; begin if (pos < 1) or (pos > Length(FCurLine)) then exit(False); - Result := ( (vscSpace in FVisibleSpecialChars) and (FCurLine[pos] in [' ']) ) or - ( (FVisibleSpecialChars*[vscTabAtFirst, vscTabAtLast] <> []) and (FCurLine[pos] in [#9]) ) - ; + Result := ( ( (vscSpace in FVisibleSpecialChars) and (FCurLine[pos] in [' ']) ) or + ( (FVisibleSpecialChars*[vscTabAtFirst, vscTabAtLast] <> []) and (FCurLine[pos] in [#9]) ) + ) and + (not IsCombiningCodePoint(PChar(FCurLine)+pos)); end; constructor TSynEditMarkupSpecialChar.Create(ASynEdit : TSynEditBase); diff --git a/components/synedit/syneditmiscprocs.pp b/components/synedit/syneditmiscprocs.pp index 3ac81a6fad..43646fd53a 100644 --- a/components/synedit/syneditmiscprocs.pp +++ b/components/synedit/syneditmiscprocs.pp @@ -82,6 +82,9 @@ function ToPos(AIdx: Integer): Integer; inline; function YToIdx(APointWithYPos: TPoint): TPoint; inline; function YToPos(APointWithYIdx: TPoint): TPoint; inline; +function IsCombiningCodePoint(const AChar: PChar): Boolean; +function IsSpaceChar(AText: PChar): Boolean; inline; +function CountLeadSpace(AText: PChar): integer; inline; function CountLeadWhiteSpace(AText: PChar): integer; inline; function CountBackwardWhiteSpace(AText: PChar; AStart: Integer): integer; inline; function CountLeadWhiteSpace(AText: PChar; out AnHasTab: boolean): integer; inline; @@ -111,6 +114,38 @@ begin inc(Result.Y); end; +function IsCombiningCodePoint(const AChar: PChar): Boolean; +begin + Result := ( + ( (AChar[0] = #$CC) ) or // Combining Diacritical Marks (belongs to previos char) 0300-036F + ( (AChar[0] = #$CD) and (AChar[1] in [#$80..#$AF]) ) or // Combining Diacritical Marks + ( (AChar[0] = #$D8) and (AChar[1] in [#$90..#$9A]) ) or // Arabic 0610 (d890)..061A (d89a) + ( (AChar[0] = #$D9) and (AChar[1] in [#$8b..#$9f, #$B0]) ) or // Arabic 064B (d98b)..065F (d99f) // 0670 (d9b0) + ( (AChar[0] = #$DB) and (AChar[1] in [#$96..#$9C, #$9F..#$A4, #$A7..#$A8, #$AA..#$AD]) ) or // Arabic 06D6 (db96).. .. ..06EA (dbaa) + ( (AChar[0] = #$E0) and (AChar[1] = #$A3) and (AChar[2] in [#$A4..#$BE]) ) or // Arabic 08E4 (e0a3a4) ..08FE (e0a3be) + ( (AChar[0] = #$E1) and (AChar[1] = #$B7) ) or // Combining Diacritical Marks Supplement 1DC0-1DFF + ( (AChar[0] = #$E2) and (AChar[1] = #$83) and (AChar[2] in [#$90..#$FF]) ) or // Combining Diacritical Marks for Symbols 20D0-20FF + ( (AChar[0] = #$EF) and (AChar[1] = #$B8) and (AChar[2] in [#$A0..#$AF]) ) // Combining half Marks FE20-FE2F + ); +end; + +function IsSpaceChar(AText: PChar): Boolean; +begin + Result := (AText^ = ' ') and not IsCombiningCodePoint(AText); +end; + +function CountLeadSpace(AText: PChar): integer; +var + Run : PChar; +begin + Run := AText; + while (Run^ in [' ']) do + Inc(Run); + Result := Run - AText; + if (Result > 0) and IsCombiningCodePoint(Run) then + dec(Result); +end; + function CountLeadWhiteSpace(AText: PChar): integer; var Run : PChar; @@ -119,6 +154,8 @@ begin while (Run^ in [' ', #9]) do Inc(Run); Result := Run - AText; + if (Result > 0) and IsCombiningCodePoint(Run) then + dec(Result); end; function CountBackwardWhiteSpace(AText: PChar; AStart: Integer): integer; @@ -142,6 +179,8 @@ begin while (Run^ in [' ', #9]) do Inc(Run); Result := Run - AText; + if (Result > 0) and IsCombiningCodePoint(Run) then + dec(Result); end; {* fontstyle utilities *} diff --git a/components/synedit/synedittextbuffer.pp b/components/synedit/synedittextbuffer.pp index 1a827045f0..d4e6046cb5 100644 --- a/components/synedit/synedittextbuffer.pp +++ b/components/synedit/synedittextbuffer.pp @@ -206,7 +206,6 @@ type procedure UndoEditLinesDelete(LogY, ACount: Integer); procedure IncreaseTextChangeStamp; procedure DoGetPhysicalCharWidths(Line: PChar; LineLen, Index: Integer; PWidths: PPhysicalCharWidth); override; - function LogicPosIsCombining(const AChar: PChar): Boolean; inline; function GetDisplayView: TLazSynDisplayView; override; @@ -979,7 +978,7 @@ begin #$80..#$BF: PWidths^ := 0; else - if LogicPosIsCombining(Line) then + if IsCombiningCodePoint(Line) then PWidths^ := 0 else PWidths^ := 1; @@ -1012,21 +1011,6 @@ begin end; -function TSynEditStringList.LogicPosIsCombining(const AChar: PChar): Boolean; -begin - Result := ( - ( (AChar[0] = #$CC) ) or // Combining Diacritical Marks (belongs to previos char) 0300-036F - ( (AChar[0] = #$CD) and (AChar[1] in [#$80..#$AF]) ) or // Combining Diacritical Marks - ( (AChar[0] = #$D8) and (AChar[1] in [#$90..#$9A]) ) or // Arabic 0610 (d890)..061A (d89a) - ( (AChar[0] = #$D9) and (AChar[1] in [#$8b..#$9f, #$B0]) ) or // Arabic 064B (d98b)..065F (d99f) // 0670 (d9b0) - ( (AChar[0] = #$DB) and (AChar[1] in [#$96..#$9C, #$9F..#$A4, #$A7..#$A8, #$AA..#$AD]) ) or // Arabic 06D6 (db96).. .. ..06EA (dbaa) - ( (AChar[0] = #$E0) and (AChar[1] = #$A3) and (AChar[2] in [#$A4..#$BE]) ) or // Arabic 08E4 (e0a3a4) ..08FE (e0a3be) - ( (AChar[0] = #$E1) and (AChar[1] = #$B7) ) or // Combining Diacritical Marks Supplement 1DC0-1DFF - ( (AChar[0] = #$E2) and (AChar[1] = #$83) and (AChar[2] in [#$90..#$FF]) ) or // Combining Diacritical Marks for Symbols 20D0-20FF - ( (AChar[0] = #$EF) and (AChar[1] = #$B8) and (AChar[2] in [#$A0..#$AF]) ) // Combining half Marks FE20-FE2F - ); -end; - function TSynEditStringList.GetDisplayView: TLazSynDisplayView; begin Result := FDisplayView; @@ -1240,7 +1224,7 @@ begin while (Result < l) and (ACount > 0) do begin inc(Result); if (ALine[Result] in [#0..#127, #192..#255]) and - ( (lpStopAtCodePoint in AFlags) or (not LogicPosIsCombining(@ALine[Result])) ) + ( (lpStopAtCodePoint in AFlags) or (not IsCombiningCodePoint(@ALine[Result])) ) then dec(ACount); end; @@ -1250,7 +1234,7 @@ begin if (Result <= l) then while (Result > 1) and ( (not(ALine[Result] in [#0..#127, #192..#255])) or - ( (not(lpStopAtCodePoint in AFlags)) and LogicPosIsCombining(@ALine[Result]) ) + ( (not(lpStopAtCodePoint in AFlags)) and IsCombiningCodePoint(@ALine[Result]) ) ) do dec(Result); @@ -1259,7 +1243,7 @@ begin dec(Result); if (Result > l) or (Result = 1) or ( (ALine[Result] in [#0..#127, #192..#255]) and - ( (lpStopAtCodePoint in AFlags) or (not LogicPosIsCombining(@ALine[Result])) ) + ( (lpStopAtCodePoint in AFlags) or (not IsCombiningCodePoint(@ALine[Result])) ) ) then inc(ACount); @@ -1278,7 +1262,7 @@ begin if Result then Result := (ALogicalPos = 1) or (lpStopAtCodePoint in AFlags) or - (not LogicPosIsCombining(@ALine[ALogicalPos])); + (not IsCombiningCodePoint(@ALine[ALogicalPos])); end; function TSynEditStringList.LogicPosAdjustToChar(const ALine: String; ALogicalPos: integer; @@ -1292,7 +1276,7 @@ begin while (Result <= length(ALine)) and ( (not(ALine[Result] in [#0..#127, #192..#255])) or ((Result <> 1) and - (not(lpStopAtCodePoint in AFlags)) and LogicPosIsCombining(@ALine[Result]) + (not(lpStopAtCodePoint in AFlags)) and IsCombiningCodePoint(@ALine[Result]) ) ) do @@ -1305,7 +1289,7 @@ begin while (Result > 1) and ( (not(ALine[Result] in [#0..#127, #192..#255])) or - ( (not(lpStopAtCodePoint in AFlags)) and LogicPosIsCombining(@ALine[Result]) ) + ( (not(lpStopAtCodePoint in AFlags)) and IsCombiningCodePoint(@ALine[Result]) ) ) do dec(Result);