SynEdit, Refactor: Changed Phys/Logical Conversation (tabs, utf8)

git-svn-id: trunk@18603 -
This commit is contained in:
martin 2009-02-08 17:01:36 +00:00
parent dd08096ff5
commit 01e79b4d74
8 changed files with 176 additions and 658 deletions

View File

@ -68,7 +68,7 @@ begin
Inc(Result);
end;
if Physical and (Result>0) then
Result := TSynEdit(Editor).LogicalToPhysicalCol(Line, Result+1)-1;
Result := TSynEdit(Editor).LogicalToPhysicalCol(Line, -1, Result+1)-1; // TODO, Need the real index of the line
end else
Result := 0;
end;
@ -98,7 +98,7 @@ begin
if SpaceCount2 = SpaceCount1 then
SpaceCount2 := 0;
// remove visible spaces
LogSpacePos := TSynEdit(Editor).PhysicalToLogicalCol(Line, SpaceCount2 + 1);
LogSpacePos := TSynEdit(Editor).PhysicalToLogicalCol(Line, PhysCaret.y-1, SpaceCount2 + 1);
CaretNewX := SpaceCount2 + 1;
DelChars := copy(Line, LogSpacePos, PhysCaret.X - LogSpacePos);
InsChars := ''; // TODO: if tabs were removed, maybe fill-up with spaces
@ -132,7 +132,8 @@ begin
sbitSpace:
InsChars := StringOfChar(' ', SpaceCount2);
sbitCopySpaceTab:
InsChars := copy(Temp, 1, TSynEdit(Editor).PhysicalToLogicalCol(Temp, SpaceCount2+1)-1);
InsChars := copy(Temp, 1, TSynEdit(Editor).PhysicalToLogicalCol(Temp,
BackCounter, SpaceCount2+1)-1);
sbitPositionCaret:
if Line <> '' then
InsChars := StringOfChar(' ', SpaceCount2)
@ -140,7 +141,7 @@ begin
InsChars := '';
end;
Result := InsChars + Line;
CaretNewX := TSynEdit(Editor).LogicalToPhysicalCol(Result, SpaceCount2+1);
CaretNewX := TSynEdit(Editor).LogicalToPhysicalCol(Result, PhysCaret.y - 1, SpaceCount2+1);
end;
function TSynBeautifier.GetIndentForLine(Editor: TSynEditBase; const Line: string; const PhysCaret: TPoint): Integer;

View File

@ -471,7 +471,6 @@ type
function GetLineText: string;
{$IFDEF SYN_LAZARUS}
function GetCharLen(const Line: string; CharStartPos: integer): integer;
function AdjustPhysPosToCharacterStart(Line: integer; PhysPos: integer): integer;
function GetLogicalCaretXY: TPoint;
procedure SetCFDividerDrawLevel(const AValue: Integer);
function GetCFDividerDrawLevel : Integer;
@ -756,18 +755,13 @@ type
{$IFDEF SYN_LAZARUS}
// Byte to Char
function LogicalToPhysicalPos(const p: TPoint): TPoint;
function LogicalToPhysicalCol(const Line: string;
LogicalPos: integer): integer;
function LogicalToPhysicalCol(Line: PChar; LineLen: integer;
LogicalPos, StartBytePos, StartPhysicalPos: integer): integer;
function LogicalToPhysicalCol(const Line: String; Index, LogicalPos
: integer): integer;
// Char to Byte
function PhysicalToLogicalPos(const p: TPoint): TPoint;
function PhysicalToLogicalCol(const Line: string;
PhysicalPos: integer): integer;
function PhysicalToLogicalCol(const Line: string;
PhysicalPos, StartBytePos, StartPhysicalPos: integer): integer;
function PhysicalLineLength(Line: PChar; LineLen: integer;
WithTabs: boolean): integer;
Index, PhysicalPos: integer): integer;
function PhysicalLineLength(Line: String; Index: integer): integer;
function ScreenColumnToXValue(Col: integer): integer; // map screen column to screen pixel
procedure MoveCaretToVisibleArea;
procedure MoveCaretIgnoreEOL(const NewCaret: TPoint);
@ -1817,22 +1811,6 @@ begin
Result:=1;
end;
function TCustomSynEdit.AdjustPhysPosToCharacterStart(Line: integer;
PhysPos: integer): integer;
var
s: string;
BytePos: LongInt;
begin
Result:=PhysPos;
if Result<1 then
Result:=1
else if (Line>=1) and (Line<=FTheLinesView.Count) then begin
s:=FTheLinesView[Line-1];
BytePos:=PhysicalToLogicalCol(s,Result);
Result:=LogicalToPhysicalCol(s,BytePos);
end;
end;
function TCustomSynEdit.GetLogicalCaretXY: TPoint;
begin
Result:=PhysicalToLogicalPos(CaretXY);
@ -3685,7 +3663,7 @@ begin
if not (eoScrollPastEol in fOptions) then begin
{$IFDEF SYN_LAZARUS}
Line:=FTheLinesView[Value.Y-1];
nMaxX := PhysicalLineLength(PChar(Line),length(Line),true)+1;
nMaxX := PhysicalLineLength(Line, Value.Y-1) + 1;
{$ELSE}
nMaxX := Length(FTheLinesView[Value.Y - 1]) + 1; //abc 2000-09-30
{$ENDIF}
@ -6158,12 +6136,8 @@ begin
// join this line with the last line if possible
if CaretY > 1 then begin
CaretY := CaretY - 1;
{$IFDEF SYN_LAZARUS}
CaretX := LogicalToPhysicalCol(FTheLinesView[CaretY - 1],
Length(FTheLinesView[CaretY - 1]) + 1);
{$ELSE}
CaretX := Length(Lines[CaretY - 1]) + 1;
{$ENDIF}
CaretX := PhysicalLineLength(FTheLinesView[CaretY - 1],
CaretY - 1) + 1;
FTheLinesView.Delete(CaretY);
DoLinesDeleted(CaretY, 1);
{$IFNDEF SYN_LAZARUS}
@ -6194,9 +6168,9 @@ begin
Helper := Copy(Temp, CaretX, counter);
VDelete(Temp, CaretX, counter, drLTR);
{$ELSE USE_UTF8BIDI_LCL}
LogCaretXY.X:=PhysicalToLogicalCol(Temp,CaretX-counter);
LogCaretXY.X:=PhysicalToLogicalCol(Temp, CaretY-1, CaretX-counter);
LogCounter:=GetCharLen(Temp,LogCaretXY.X);
CaretX := LogicalToPhysicalCol(Temp,LogCaretXY.X);
CaretX := LogicalToPhysicalCol(Temp, CaretY-1, LogCaretXY.X);
Helper := Copy(Temp, LogCaretXY.X, LogCounter);
System.Delete(Temp, LogCaretXY.X, LogCounter);
//debugln('ecDeleteLastChar delete char CaretX=',dbgs(CaretX),
@ -6263,7 +6237,7 @@ begin
{$IFDEF SYN_LAZARUS}
Counter:=GetCharLen(Temp,LogCaretXY.X);
Helper := Copy(Temp, LogCaretXY.X, Counter);
Caret.X := LogicalToPhysicalCol(Temp,LogCaretXY.X+Counter);
Caret.X := LogicalToPhysicalCol(Temp, CaretY-1, LogCaretXY.X+Counter);
Caret.Y := CaretY;
{$IFDEF USE_UTF8BIDI_LCL}
VDelete(Temp, LogCaretXY.X, Counter, drLTR);
@ -6307,7 +6281,7 @@ begin
end;
ecDeleteWord, ecDeleteEOL:
if not ReadOnly then begin
Len := LogicalToPhysicalCol(LineText,Length(LineText)+1)-1;
Len := LogicalToPhysicalCol(LineText, CaretY-1,Length(LineText)+1)-1;
Helper := '';
Caret := CaretXY;
if Command = ecDeleteWord then begin
@ -8114,17 +8088,17 @@ begin
Dec(iLine);
if iLine < 0 then break;
PrevLine := FTheLinesView[iLine];
until PhysicalLineLength(PChar(PrevLine),length(PrevLine),true) > OldCaretX - 1;
until PhysicalLineLength(PrevLine, iLine) > OldCaretX - 1;
if iLine >= 0 then begin
p := @PrevLine[PhysicalToLogicalCol(PrevLine,OldCaretX)];
p := @PrevLine[PhysicalToLogicalCol(PrevLine, iLine, OldCaretX)];
// scan over non-whitespaces
while not (p^ in [#0, #9, #32]) do
inc(p);
// scan over whitespaces
while (p^ in [#9, #32]) do
inc(p);
i := LogicalToPhysicalCol(PrevLine, p-@PrevLine[1]+1) - CaretX;
i := LogicalToPhysicalCol(PrevLine, iLine, p-@PrevLine[1]+1) - CaretX;
end;
end;
end;
@ -9304,28 +9278,18 @@ end;
{$ENDIF}
{$IFDEF SYN_LAZARUS}
function TCustomSynEdit.LogicalToPhysicalCol(const Line: string;
LogicalPos: integer): integer;
function TCustomSynEdit.LogicalToPhysicalCol(const Line: String;
Index, LogicalPos: integer): integer;
// LogicalPos is 1-based
// Index 0-based LineNumber
begin
Result := TSynEditStrings(FTheLinesView).LogicalToPhysicalCol(PChar(Pointer(Line)),
length(Line),LogicalPos,1,1);
Result := TSynEditStrings(FTheLinesView).LogicalToPhysicalCol(Line, Index,
LogicalPos);
end;
function TCustomSynEdit.LogicalToPhysicalCol(Line: PChar; LineLen: integer;
LogicalPos, StartBytePos, StartPhysicalPos: integer): integer;
// Note: LogicalPos, StartBytePos, StartPhysicalPos start at 1
function TCustomSynEdit.PhysicalLineLength(Line: String; Index: integer): integer;
begin
Result := TSynEditStrings(FTheLinesView).LogicalToPhysicalCol(Line, LineLen, LogicalPos,
StartBytePos, StartPhysicalPos);
end;
function TCustomSynEdit.PhysicalLineLength(Line: PChar; LineLen: integer;
WithTabs: boolean): integer;
begin
if WithTabs then
Result:=LogicalToPhysicalCol(Line,LineLen,LineLen+1,1,1)-1
else
Result:=UTF8Length(Line,LineLen);
Result:=LogicalToPhysicalCol(Line, Index, length(Line)+1) - 1
end;
function TCustomSynEdit.PhysicalToLogicalPos(const p: TPoint): TPoint;
@ -9334,16 +9298,10 @@ begin
end;
function TCustomSynEdit.PhysicalToLogicalCol(const Line: string;
PhysicalPos: integer): integer;
Index, PhysicalPos: integer): integer;
begin
Result := TSynEditStrings(FTheLinesView).PhysicalToLogicalCol(Line,PhysicalPos,1,1);
end;
function TCustomSynEdit.PhysicalToLogicalCol(const Line: string;
PhysicalPos, StartBytePos, StartPhysicalPos: integer): integer;
begin
Result := TSynEditStrings(FTheLinesView).PhysicalToLogicalCol(Line, PhysicalPos,
StartBytePos, StartPhysicalPos);
Result := TSynEditStrings(FTheLinesView).PhysicalToLogicalCol(Line, Index,
PhysicalPos);
end;
function TCustomSynEdit.ScreenColumnToXValue(Col : integer) : integer;

View File

@ -71,41 +71,6 @@ function GetIntArray(Count: Cardinal; InitialValue: integer): PIntArray;
procedure InternalFillRect(dc: HDC; const rcPaint: TRect);
// Converting tabs to spaces: To use the function several times it's better
// to use a function pointer that is set to the fastest conversion function.
type
TConvertTabsProc = function(const Line: AnsiString;
TabWidth: integer): AnsiString;
function GetBestConvertTabsProc(TabWidth: integer): TConvertTabsProc;
// This is the slowest conversion function which can handle TabWidth <> 2^n.
function ConvertTabs(const Line: AnsiString; TabWidth: integer): AnsiString;
{begin} //mh 2000-10-19
type
TConvertTabsProcEx = function(const Line: AnsiString; TabWidth: integer;
out HasTabs: boolean): AnsiString;
{$IFDEF SYN_LAZARUS}
TSimulateConvertTabsProcEx = function(const Line: AnsiString;
TabWidth: integer; out HasTabs: boolean): integer;
// returns length of converted string
{$ENDIF}
function GetBestConvertTabsProcEx(TabWidth: integer): TConvertTabsProcEx;
{$IFDEF SYN_LAZARUS}
function GetBestSimulateConvertTabsProcEx(
TabWidth:integer): TSimulateConvertTabsProcEx;
{$ENDIF}
// This is the slowest conversion function which can handle TabWidth <> 2^n.
function ConvertTabsEx(const Line: AnsiString; TabWidth: integer;
out HasTabs: boolean): AnsiString;
{end} //mh 2000-10-19
function CharIndex2CaretPos(Index, TabWidth: integer;
const Line: string): integer;
function CaretPos2CharIndex(Position, TabWidth: integer; const Line: string;
var InsideTabChar: boolean): integer;
// search for the first char of set AChars in Line, starting at index Start
function StrScanForCharInSet(const Line: string; Start: integer;
AChars: TSynIdentChars): integer;
@ -238,391 +203,6 @@ end;
{***}
// mh: Please don't change; no stack frame and efficient register use.
function GetHasTabs(pLine: PChar; out CharsBefore: integer): boolean;
begin
CharsBefore := 0;
if Assigned(pLine) then begin
while (pLine^ <> #0) do begin
if (pLine^ = #9) then break;
Inc(CharsBefore);
Inc(pLine);
end;
Result := (pLine^ = #9);
end else
Result := FALSE;
end;
{$IFDEF SYN_LAZARUS}
function StringHasTabs(const Line: string; out CharsBefore: integer): boolean;
var LineLen: integer;
begin
LineLen:=length(Line);
CharsBefore := 1;
while (CharsBefore<=LineLen) and (Line[CharsBefore]<>#9) do
inc(CharsBefore);
Result:=CharsBefore<=LineLen;
dec(CharsBefore);
end;
{$ENDIF}
{begin} //mh 2000-10-19
function ConvertTabs1Ex(const Line: AnsiString; TabWidth: integer;
out HasTabs: boolean): AnsiString;
var
pDest: PChar;
nBeforeTab: integer;
begin
Result := Line; // increment reference count only
if GetHasTabs(pointer(Line), nBeforeTab) then begin
HasTabs := TRUE; //mh 2000-11-08
pDest := @Result[nBeforeTab + 1]; // this will make a copy of Line
// We have at least one tab in the string, and the tab width is 1.
// pDest points to the first tab char. We overwrite all tabs with spaces.
repeat
if (pDest^ = #9) then pDest^ := ' ';
Inc(pDest);
until (pDest^ = #0);
end else
HasTabs := FALSE;
end;
{$IFDEF SYN_LAZARUS}
function SimulateConvertTabs1Ex(const Line: AnsiString; TabWidth: integer;
out HasTabs: boolean): integer;
// TabWidth=1
var
i: integer;
begin
Result:=length(Line);
i:=1;
while (i<=Result) and (Line[i]<>#9) do inc(i);
HasTabs:=(i<=Result);
end;
{$ENDIF}
function ConvertTabs1(const Line: AnsiString; TabWidth: integer): AnsiString;
var
HasTabs: boolean;
begin
Result := ConvertTabs1Ex(Line, TabWidth, HasTabs);
end;
function ConvertTabs2nEx(const Line: AnsiString; TabWidth: integer;
out HasTabs: boolean): AnsiString;
var
i, DestLen, TabCount, TabMask: integer;
pSrc, pDest: PChar;
begin
Result := Line; // increment reference count only
if GetHasTabs(pointer(Line), DestLen) then begin
HasTabs := TRUE; //mh 2000-11-08
pSrc := @Line[1 + DestLen];
// We have at least one tab in the string, and the tab width equals 2^n.
// pSrc points to the first tab char in Line. We get the number of tabs
// and the length of the expanded string now.
TabCount := 0;
TabMask := (TabWidth - 1) xor $7FFFFFFF;
repeat
if (pSrc^ = #9) then begin
DestLen := (DestLen + TabWidth) and TabMask;
Inc(TabCount);
end else
Inc(DestLen);
Inc(pSrc);
until (pSrc^ = #0);
// Set the length of the expanded string.
SetLength(Result, DestLen);
DestLen := 0;
pSrc := PChar(Line);
pDest := PChar(Result);
// We use another TabMask here to get the difference to 2^n.
TabMask := TabWidth - 1;
repeat
if (pSrc^ = #9) then begin
i := TabWidth - (DestLen and TabMask);
Inc(DestLen, i);
repeat
pDest^ := ' ';
Inc(pDest);
Dec(i);
until (i = 0);
Dec(TabCount);
if (TabCount = 0) then begin
repeat
Inc(pSrc);
pDest^ := pSrc^;
Inc(pDest);
until (pSrc^ = #0);
exit;
end;
end else begin
pDest^ := pSrc^;
Inc(pDest);
Inc(DestLen);
end;
Inc(pSrc);
until (pSrc^ = #0);
end else
HasTabs := FALSE;
end;
{$IFDEF SYN_LAZARUS}
function SimulateConvertTabs2nEx(const Line: AnsiString; TabWidth: integer;
out HasTabs: boolean): integer;
var
LineLen, DestLen, SrcPos, TabMask: integer;
begin
LineLen:=length(Line);
if StringHasTabs(Line, DestLen) then begin
HasTabs := TRUE;
SrcPos:=DestLen+1;
// We have at least one tab in the string, and the tab width equals 2^n.
// pSrc points to the first tab char in Line. We get the number of tabs
// and the length of the expanded string now.
TabMask := (TabWidth - 1) xor $7FFFFFFF;
repeat
if (Line[SrcPos] = #9) then
DestLen := (DestLen + TabWidth) and TabMask
else
Inc(DestLen);
Inc(SrcPos);
until (SrcPos>LineLen);
Result:=DestLen;
end else begin
Result := LineLen;
HasTabs := FALSE;
end;
end;
{$ENDIF}
function ConvertTabs2n(const Line: AnsiString; TabWidth: integer): AnsiString;
var
HasTabs: boolean;
begin
Result := ConvertTabs2nEx(Line, TabWidth, HasTabs);
end;
function ConvertTabsEx(const Line: AnsiString; TabWidth: integer;
out HasTabs: boolean): AnsiString;
var
i, DestLen, TabCount: integer;
pSrc, pDest: PChar;
begin
Result := Line; // increment reference count only
if GetHasTabs(pointer(Line), DestLen) then begin
HasTabs := TRUE; //mh 2000-11-08
pSrc := @Line[1 + DestLen];
// We have at least one tab in the string, and the tab width is greater
// than 1. pSrc points to the first tab char in Line. We get the number
// of tabs and the length of the expanded string now.
TabCount := 0;
repeat
if (pSrc^ = #9) then begin
DestLen := DestLen + TabWidth - DestLen mod TabWidth;
Inc(TabCount);
end else
Inc(DestLen);
Inc(pSrc);
until (pSrc^ = #0);
// Set the length of the expanded string.
SetLength(Result, DestLen);
DestLen := 0;
pSrc := PChar(Line);
pDest := PChar(Result);
repeat
if (pSrc^ = #9) then begin
i := TabWidth - (DestLen mod TabWidth);
Inc(DestLen, i);
repeat
pDest^ := ' ';
Inc(pDest);
Dec(i);
until (i = 0);
Dec(TabCount);
if (TabCount = 0) then begin
repeat
Inc(pSrc);
pDest^ := pSrc^;
Inc(pDest);
until (pSrc^ = #0);
exit;
end;
end else begin
pDest^ := pSrc^;
Inc(pDest);
Inc(DestLen);
end;
Inc(pSrc);
until (pSrc^ = #0);
end else
HasTabs := FALSE;
end;
{$IFDEF SYN_LAZARUS}
function SimulateConvertTabsEx(const Line: AnsiString; TabWidth: integer;
out HasTabs: boolean): integer;
var
LineLen, DestLen, SrcPos: integer;
begin
LineLen:=length(Line);
if StringHasTabs(Line, DestLen) then begin
HasTabs := TRUE;
SrcPos := DestLen+1;
// We have at least one tab in the string, and the tab width is greater
// than 1. pSrc points to the first tab char in Line. We get the number
// of tabs and the length of the expanded string now.
repeat
if (Line[SrcPos] = #9) then
DestLen := DestLen + TabWidth - DestLen mod TabWidth
else
Inc(DestLen);
Inc(SrcPos);
until (SrcPos > LineLen);
Result:=DestLen;
end else begin
Result:=LineLen;
HasTabs := FALSE;
end;
end;
{$ENDIF}
function ConvertTabs(const Line: AnsiString; TabWidth: integer): AnsiString;
var
HasTabs: boolean;
begin
Result := ConvertTabsEx(Line, TabWidth, HasTabs);
end;
function IsPowerOfTwo(TabWidth: integer): boolean;
var
nW: integer;
begin
nW := 2;
repeat
if (nW >= TabWidth) then break;
Inc(nW, nW);
until (nW >= $10000); // we don't want 64 kByte spaces...
Result := (nW = TabWidth);
end;
function GetBestConvertTabsProc(TabWidth: integer): TConvertTabsProc;
begin
if (TabWidth < 2) then Result := TConvertTabsProc(@ConvertTabs1)
else if IsPowerOfTwo(TabWidth) then
Result := TConvertTabsProc(@ConvertTabs2n)
else
Result := TConvertTabsProc(@ConvertTabs);
end;
function GetBestConvertTabsProcEx(TabWidth: integer): TConvertTabsProcEx;
begin
if (TabWidth < 2) then
Result := TConvertTabsProcEx(@ConvertTabs1Ex)
else if IsPowerOfTwo(TabWidth) then
Result := TConvertTabsProcEx(@ConvertTabs2nEx)
else
Result := TConvertTabsProcEx(@ConvertTabsEx);
end;
{end} //mh 2000-10-19
{$IFDEF SYN_LAZARUS}
function GetBestSimulateConvertTabsProcEx(
TabWidth:integer): TSimulateConvertTabsProcEx;
begin
if (TabWidth < 2) then Result :=
TSimulateConvertTabsProcEx(@SimulateConvertTabs1Ex)
else if IsPowerOfTwo(TabWidth) then
Result := TSimulateConvertTabsProcEx(@SimulateConvertTabs2nEx)
else
Result := TSimulateConvertTabsProcEx(@SimulateConvertTabsEx);
end;
{$ENDIF}
{***}
function CharIndex2CaretPos(Index, TabWidth: integer;
const Line: string): integer;
var
iChar: integer;
pNext: PChar;
begin
// possible sanity check here: Index := Max(Index, Length(Line));
if Index > 1 then begin
if (TabWidth <= 1) or not GetHasTabs(pointer(Line), iChar) then
Result := Index
else begin
if iChar + 1 >= Index then
Result := Index
else begin
// iChar is number of chars before first #9
Result := iChar;
// Index is *not* zero-based
Inc(iChar);
Dec(Index, iChar);
pNext := @Line[iChar];
while Index > 0 do begin
case pNext^ of
#0: break;
#9: begin
// Result is still zero-based
Inc(Result, TabWidth);
Dec(Result, Result mod TabWidth);
end;
else Inc(Result);
end;
Dec(Index);
Inc(pNext);
end;
// done with zero-based computation
Inc(Result);
end;
end;
end else
Result := 1;
end;
function CaretPos2CharIndex(Position, TabWidth: integer; const Line: string;
var InsideTabChar: boolean): integer;
var
iPos: integer;
pNext: PChar;
begin
InsideTabChar := FALSE;
if Position > 1 then begin
if (TabWidth <= 1) or not GetHasTabs(pointer(Line), iPos) then
Result := Position
else begin
if iPos + 1 >= Position then
Result := Position
else begin
// iPos is number of chars before first #9
Result := iPos + 1;
pNext := @Line[Result];
// for easier computation go zero-based (mod-operation)
Dec(Position);
while iPos < Position do begin
case pNext^ of
#0: break;
#9: begin
Inc(iPos, TabWidth);
Dec(iPos, iPos mod TabWidth);
if iPos > Position then begin
InsideTabChar := TRUE;
break;
end;
end;
else Inc(iPos);
end;
Inc(Result);
Inc(pNext);
end;
end;
end;
end else
Result := Position;
end;
function StrScanForCharInSet(const Line: string; Start: integer;
AChars: TSynIdentChars): integer;
var

View File

@ -644,7 +644,8 @@ var
else begin
TempString := FLines[FCaret.LinePos - 1];
Len := Length(TempString);
LogicalInsertPos := FLines.PhysicalToLogicalCol(TempString,InsertPos);
LogicalInsertPos := FLines.PhysicalToLogicalCol(TempString,
FCaret.LinePos - 1, InsertPos);
if Len < LogicalInsertPos
then begin
TempString :=

View File

@ -35,6 +35,8 @@ type
Index, Count: Integer) of object;
TSynEditNotifyReason = (senrLineCount, senrLineChange);
TPhysicalCharWidths = Array of Shortint;
{ TSynEditStrings }
TSynEditStrings = class(TStrings)
@ -50,7 +52,6 @@ type
function GetExpandedString(Index: integer): string; virtual; abstract;
function GetLengthOfLongestLine: integer; virtual; abstract;
procedure SetTextStr(const Value: string); override;
public
constructor Create;
procedure DeleteLines(Index, NumLines: integer); virtual; abstract;
@ -66,20 +67,16 @@ type
procedure RemoveChangeHandler(AReason: TSynEditNotifyReason;
AHandler: TStringListLineCountEvent); virtual; abstract;
public
function GetPhysicalCharWidths(Index: Integer): TPhysicalCharWidths;
function GetPhysicalCharWidths(const Line: String; Index: Integer): TPhysicalCharWidths; virtual; abstract;
// Byte to Char
function LogicalToPhysicalPos(const p: TPoint): TPoint;
function LogicalToPhysicalCol(const Line: string;
LogicalPos: integer): integer;
function LogicalToPhysicalCol(Line: PChar; LineLen: integer;
LogicalPos, StartBytePos,
StartPhysicalPos: integer): integer; virtual;
function LogicalToPhysicalCol(const Line: String;
Index, LogicalPos: integer): integer; virtual;
// Char to Byte
function PhysicalToLogicalPos(const p: TPoint): TPoint;
function PhysicalToLogicalCol(const Line: string;
PhysicalPos: integer): integer;
function PhysicalToLogicalCol(const Line: string;
PhysicalPos, StartBytePos,
StartPhysicalPos: integer): integer; virtual;
Index, PhysicalPos: integer): integer;
public
property ExpandedStrings[Index: integer]: string read GetExpandedString;
property LengthOfLongestLine: integer read GetLengthOfLongestLine;
@ -134,6 +131,9 @@ type
AHandler: TStringListLineCountEvent); override;
procedure RemoveChangeHandler(AReason: TSynEditNotifyReason;
AHandler: TStringListLineCountEvent); override;
function GetPhysicalCharWidths(const Line: String; Index: Integer): TPhysicalCharWidths; override;
end;
@ -149,6 +149,11 @@ begin
IsUtf8 := True;
end;
function TSynEditStrings.GetPhysicalCharWidths(Index: Integer): TPhysicalCharWidths;
begin
Result := GetPhysicalCharWidths(Strings[Index], Index);
end;
function TSynEditStrings.GetIsUtf8 : Boolean;
begin
Result := FIsUtf8;
@ -197,81 +202,57 @@ function TSynEditStrings.LogicalToPhysicalPos(const p : TPoint) : TPoint;
begin
Result := p;
if Result.Y - 1 < Count then
Result.X:=LogicalToPhysicalCol(self[Result.Y - 1],Result.X);
Result.X:=LogicalToPhysicalCol(self[Result.Y - 1], Result.Y, Result.X);
end;
function TSynEditStrings.LogicalToPhysicalCol(const Line : string; LogicalPos : integer) : integer;
begin
Result := LogicalToPhysicalCol(PChar(Pointer(Line)),length(Line),LogicalPos,1,1);
end;
function TSynEditStrings.LogicalToPhysicalCol(Line : PChar; LineLen : integer; LogicalPos, StartBytePos, StartPhysicalPos : integer) : integer;
function TSynEditStrings.LogicalToPhysicalCol(const Line : String;
Index, LogicalPos: integer) : integer;
var
BytePos, ByteLen: integer;
ScreenPos: integer;
i, ByteLen: integer;
CharWidths: TPhysicalCharWidths;
begin
ByteLen := LineLen;
// map UTF8
ScreenPos := StartPhysicalPos;
BytePos:= StartBytePos;
while BytePos<LogicalPos do begin
if (BytePos <= ByteLen) then begin
inc(ScreenPos);
if IsUTF8 then
inc(BytePos,UTF8CharacterLength(@Line[BytePos-1]))
else
inc(BytePos);
end else begin
// beyond end of line
inc(ScreenPos,LogicalPos-BytePos);
break;
end;
end;
if (BytePos>LogicalPos) and (ScreenPos>StartPhysicalPos) then
dec(ScreenPos);
Result := ScreenPos;
CharWidths := GetPhysicalCharWidths(Line, Index);
ByteLen := length(Line);
dec(LogicalPos);
if LogicalPos > ByteLen then begin
Result := 1 + LogicalPos - ByteLen;
LogicalPos := ByteLen;
end
else
Result := 1;
for i := 0 to LogicalPos - 1 do
Result := Result + CharWidths[i];
end;
function TSynEditStrings.PhysicalToLogicalPos(const p : TPoint) : TPoint;
begin
Result := p;
if (Result.Y>=1) and (Result.Y <= Count) then
Result.X:=PhysicalToLogicalCol(self[Result.Y - 1],Result.X,1,1);
Result.X:=PhysicalToLogicalCol(self[Result.Y - 1], Result.Y - 1, Result.X);
end;
function TSynEditStrings.PhysicalToLogicalCol(const Line : string; PhysicalPos : integer) : integer;
begin
Result:=PhysicalToLogicalCol(Line,PhysicalPos,1,1);
end;
function TSynEditStrings.PhysicalToLogicalCol(const Line : string; PhysicalPos, StartBytePos, StartPhysicalPos : integer) : integer;
function TSynEditStrings.PhysicalToLogicalCol(const Line : string;
Index, PhysicalPos : integer) : integer;
var
BytePos, ByteLen: integer;
ScreenPos: integer;
PLine: PChar;
CharWidths: TPhysicalCharWidths;
begin
CharWidths := GetPhysicalCharWidths(Line, Index);
ByteLen := Length(Line);
ScreenPos := StartPhysicalPos;
BytePos := StartBytePos;
PLine := PChar(Line);
// map utf
while ScreenPos < PhysicalPos do begin
if (BytePos <= ByteLen) then begin
inc(ScreenPos);
if IsUTF8 then
inc(BytePos,UTF8CharacterLength(@PLine[BytePos-1]))
else
inc(BytePos);
end else begin
// beyond end of line
inc(BytePos,PhysicalPos-ScreenPos);
break;
end;
ScreenPos := 1;
BytePos := 0;
while BytePos < ByteLen do begin
if ScreenPos + CharWidths[BytePos] > PhysicalPos then
exit(BytePos+1);
ScreenPos := ScreenPos + CharWidths[BytePos];
inc(BytePos);
end;
if (ScreenPos>PhysicalPos) and (BytePos>1) and (BytePos-2<ByteLen)
and (PLine[BytePos-2]=#9) then
dec(BytePos);
Result := BytePos;
Result := BytePos + 1 + PhysicalPos - ScreenPos;
end;
{ TSynEditStringsLinked }
@ -415,6 +396,11 @@ begin
fSynStrings.PutObject(Index, AObject);
end;
function TSynEditStringsLinked.GetPhysicalCharWidths(const Line: String; Index: Integer): TPhysicalCharWidths;
begin
Result := fSynStrings.GetPhysicalCharWidths(Line, Index);
end;
procedure TSynEditStringsLinked.SetUpdateState(Updating: Boolean);
begin
if Updating then

View File

@ -200,6 +200,7 @@ type
AHandler: TStringListLineCountEvent); override;
procedure RemoveChangeHandler(AReason: TSynEditNotifyReason;
AHandler: TStringListLineCountEvent); override;
function GetPhysicalCharWidths(const Line: String; Index: Integer): TPhysicalCharWidths; override;
public
property DosFileFormat: boolean read fDosFileFormat write fDosFileFormat;
property LengthOfLongestLine: integer read GetLengthOfLongestLine;
@ -707,6 +708,29 @@ begin
else
Result := 0;
end;
// Maps the Physical Width (ScreenCells) to each character
// Multibyte Chars have thw width on the first byte, and a 0 Width for all other bytes
function TSynEditStringList.GetPhysicalCharWidths(const Line: String; Index: Integer): TPhysicalCharWidths;
var
i, j: Integer;
begin
SetLength(Result, Length(Line));
i := 0;
j := 0;
while i < length(Line) do begin
if j > 0 then begin
Result[i] := 0;
dec(j);
end else begin
Result[i] := 1;
if IsUtf8 then
j := UTF8CharacterLength(@Line[i+1]) - 1;
end;
inc(i);
end;
end;
{end} //mh 2000-10-19
function TSynEditStringList.GetObject(Index: integer): TObject;

View File

@ -41,8 +41,6 @@ type
TSynEditStringTabExpander = class(TSynEditStringsLinked)
private
FTabWidth: integer;
FConvertTabsProc: TConvertTabsProcEx;
FSimulateConvertTabsProc: TSimulateConvertTabsProcEx;
FIndexOfLongestLine: Integer;
function GetLengthOfLine(Index: Integer): integer;
procedure SetLengthOfLine(Index: Integer; const AValue: integer);
@ -53,6 +51,7 @@ TSynEditStringTabExpander = class(TSynEditStringsLinked)
function GetTabWidth : integer;
procedure SetTabWidth(const AValue : integer);
function GetExpandedString(Index: integer): string; override;
function GetPhysicalCharWidths(const Line: String; Index: Integer): TPhysicalCharWidths; override;
function GetLengthOfLongestLine: integer; override;
property LengthOfLine[Index: Integer]: integer
read GetLengthOfLine write SetLengthOfLine;
@ -61,14 +60,6 @@ TSynEditStringTabExpander = class(TSynEditStringsLinked)
destructor Destroy; override;
property LengthOfLongestLine: integer read GetLengthOfLongestLine;
// TODO: maybe use inherited for utf8?
function LogicalToPhysicalCol(Line: PChar; LineLen: integer;
LogicalPos, StartBytePos,
StartPhysicalPos: integer): integer; override;
function PhysicalToLogicalCol(const Line: string;
PhysicalPos, StartBytePos,
StartPhysicalPos: integer): integer; override;
public
property TabWidth: integer read GetTabWidth write SetTabWidth;
end;
@ -76,6 +67,18 @@ TSynEditStringTabExpander = class(TSynEditStringsLinked)
implementation
function GetHasTabs(pLine: PChar): boolean;
begin
if Assigned(pLine) then begin
while (pLine^ <> #0) do begin
if (pLine^ = #9) then break;
Inc(pLine);
end;
Result := (pLine^ = #9);
end else
Result := FALSE;
end;
{ TSynEditStringTabExpander }
constructor TSynEditStringTabExpander.Create(ASynStringSource: TSynEditStrings);
@ -117,8 +120,6 @@ begin
if FTabWidth = AValue then exit;
FTabWidth := AValue;
FConvertTabsProc := GetBestConvertTabsProcEx(fTabWidth);
FSimulateConvertTabsProc := GetBestSimulateConvertTabsProcEx(fTabWidth);
FIndexOfLongestLine := -1;
for i := 0 to Count - 1 do
if not(LengthOfLine[i] >= NoTabLengthOffset) then
@ -136,33 +137,54 @@ end;
function TSynEditStringTabExpander.ExpandedString(Index: integer): string;
var
HasTabs: boolean;
Line: String;
CharWidths: TPhysicalCharWidths;
i, j, l: Integer;
begin
if fSynStrings[Index] = '' then begin
Result := '';
LengthOfLine[Index] := 0 + NoTabLengthOffset;
Line := fSynStrings[Index];
if (Line = '') or (not GetHasTabs(PChar(Line))) then begin
Result := Line;
LengthOfLine[Index] := length(Result) + NoTabLengthOffset;
end else begin
Result := fConvertTabsProc(fSynStrings[Index], fTabWidth, HasTabs);
if HasTabs then
LengthOfLine[Index] := length(Result)
else
LengthOfLine[Index] := length(Result) + NoTabLengthOffset;
CharWidths := GetPhysicalCharWidths(Line, Index);
l := 0;
for i := 0 to length(CharWidths)-1 do
l := l + CharWidths[i];
SetLength(Result, l);
l := 1;
for i := 1 to length(CharWidths) do begin
if Line[i] <> #9 then begin
Result[l] := Line[i];
inc(l);
end else begin
for j := 1 to CharWidths[i-1] do begin
Result[l] := ' ';
inc(l);
end;
end;
end;
LengthOfLine[Index] := length(Result);
end;
end;
function TSynEditStringTabExpander.ExpandedStringLength(Index: integer): Integer;
var
HasTabs: boolean;
Line: String;
CharWidths: TPhysicalCharWidths;
i, j, l: Integer;
begin
if fSynStrings[Index] = '' then begin
Line := fSynStrings[Index];
if (Line = '') or (not GetHasTabs(PChar(Line))) then begin
Result := 0;
LengthOfLine[Index] := 0 + NoTabLengthOffset;
LengthOfLine[Index] := Length(Line) + NoTabLengthOffset;
end else begin
Result := fSimulateConvertTabsProc(fSynStrings[Index], fTabWidth, HasTabs);
if HasTabs then
LengthOfLine[Index] := Result
else
LengthOfLine[Index] := Result + NoTabLengthOffset;
CharWidths := GetPhysicalCharWidths(Line, Index);
Result := 0;
for i := 0 to length(CharWidths)-1 do
Result := Result + CharWidths[i];
LengthOfLine[Index] := Result;
end;
end;
@ -177,6 +199,21 @@ begin
Result := '';
end;
function TSynEditStringTabExpander.GetPhysicalCharWidths(const Line: String;
Index: Integer): TPhysicalCharWidths;
var
i, p: Integer;
begin
Result := inherited GetPhysicalCharWidths(Line, Index);
p := 0;
for i := 0 to length(Line) -1 do begin
if Result[i] = 0 then continue;
if Line[i+1] = #9 then
Result[i] := FTabWidth - p mod FTabWidth;
p := p + Result[i];
end
end;
function TSynEditStringTabExpander.GetLengthOfLongestLine: integer;
var
i, j, MaxLen: integer;
@ -204,74 +241,5 @@ begin
Result := 0;
end;
function TSynEditStringTabExpander.LogicalToPhysicalCol(Line: PChar;
LineLen: integer; LogicalPos, StartBytePos, StartPhysicalPos: integer): integer;
var
BytePos, ByteLen: integer;
ScreenPos: integer;
begin
ByteLen := LineLen;
// map UTF8 and Tab chars
ScreenPos := StartPhysicalPos;
BytePos:= StartBytePos;
while BytePos<LogicalPos do begin
if (BytePos <= ByteLen) then begin
if Line[BytePos-1] = #9 then begin
inc(ScreenPos, TabWidth - ((ScreenPos-1) mod TabWidth));
inc(BytePos);
end else begin
inc(ScreenPos);
if IsUTF8 then
inc(BytePos,UTF8CharacterLength(@Line[BytePos-1]))
else
inc(BytePos);
end;
end else begin
// beyond end of line
inc(ScreenPos,LogicalPos-BytePos);
break;
end;
end;
if (BytePos>LogicalPos) and (ScreenPos>StartPhysicalPos) then
dec(ScreenPos);
Result := ScreenPos;
end;
function TSynEditStringTabExpander.PhysicalToLogicalCol(const Line: string;
PhysicalPos, StartBytePos, StartPhysicalPos: integer): integer;
var
BytePos, ByteLen: integer;
ScreenPos: integer;
PLine: PChar;
begin
ByteLen := Length(Line);
ScreenPos := StartPhysicalPos;
BytePos := StartBytePos;
PLine := PChar(Line);
// map utf and tab chars
while ScreenPos < PhysicalPos do begin
if (BytePos <= ByteLen) then begin
if (PLine[BytePos-1] <> #9) then begin
inc(ScreenPos);
if IsUTF8 then
inc(BytePos,UTF8CharacterLength(@PLine[BytePos-1]))
else
inc(BytePos);
end else begin
inc(ScreenPos, TabWidth - ((ScreenPos-1) mod TabWidth));
inc(BytePos);
end;
end else begin
// beyond end of line
inc(BytePos,PhysicalPos-ScreenPos);
break;
end;
end;
if (ScreenPos>PhysicalPos) and (BytePos>1) and (BytePos-2<ByteLen)
and (PLine[BytePos-2]=#9) then
dec(BytePos);
Result := BytePos;
end;
end.

View File

@ -109,7 +109,7 @@ begin
and ((IndentLen>length(s)) or (s[IndentLen] in [#9,' '])) do
inc(IndentLen);
end;
IndentLen:=AEditor.LogicalToPhysicalCol(s,IndentLen);// consider tabs
IndentLen:=AEditor.LogicalToPhysicalCol(s, p.y - 1, IndentLen);// consider tabs
dec(IndentLen);
end;
p := AEditor.BlockBegin;