unit dlinepos_utf8;

{$MODE FPC}
{$MODESWITCH DEFAULTPARAMETERS}
{$MODESWITCH OUT}
{$MODESWITCH RESULT}

interface

function FindLineStartUTF8(SourceStart: PAnsiChar;
                           Cursor: PAnsiChar): PAnsiChar;
      //
      //  Searches backward starting with the Cursor for the beginning of line
      //  and returns pointer to the beginning. The SourceStart is the limit
      //  for searching:
      //
      //      SourceStart <= Result <= Cursor
      //
      //  Returns SourceStart if there is no newlines or Cursor<SourceStart.
      //

function ComputeLinesUTF8(SourceStart, LineStart: PAnsiChar): PtrInt; inline;
      //
      //  Computes and returns line number of the LineStart from the
      //  SourceStart.  Numeration starts with 1.
      //

function ComputePosUTF8(LineStart, Cursor: PAnsiChar): PtrInt;
      //
      //  Computes character position of the Cursor in the line started with
      //  the LineStart.

implementation

function FindLineStartUTF8(SourceStart: PAnsiChar;
                           Cursor: PAnsiChar): PAnsiChar;
begin
  while Cursor > SourceStart do begin
    if (Cursor - 1)^ in [#10, #13] then
      Exit(Cursor);
    Dec(Cursor);
  end;
  Exit(SourceStart);
end;

function _ComputeLinesUTF8(S, SEnd: PAnsiChar; Counter: PtrInt): PtrInt;
begin
  while S < SEnd do begin
    case Ord(S^) of
      13: begin
            Inc(Counter);
            if (S + 1 >= SEnd) or (S[1] <> #10) then begin
              Inc(S);
            end else
              Inc(S, 2); // 'CR LF' sequence is treated as one newline
          end;
      10: begin
            Inc(Counter);
            Inc(S);
          end;
      // TODO UTF-8 newlines
    else
      Inc(S);
    end;
  end;
  Exit(Counter);
end;

function ComputeLinesUTF8(SourceStart, LineStart: PAnsiChar): PtrInt; inline;
begin
  if SourceStart <= LineStart then begin
    Exit(_ComputeLinesUTF8(SourceStart, LineStart, 1));
  end else
    Exit(- _ComputeLinesUTF8(LineStart, SourceStart, 0));
end;

function _ComputePosUTF8(S, SEnd: PAnsiChar; Counter: PtrInt): PtrInt; inline;
begin
  while S < SEnd do begin
    if Byte(S^) in [0..%01111111, %11000000..0] then
      Inc(Counter);
    Inc(S);
  end;
  Exit(Counter);
end;

function ComputePosUTF8(LineStart, Cursor: PAnsiChar): PtrInt;
begin
  if LineStart <= Cursor then begin
    Exit(_ComputePosUTF8(LineStart, Cursor, 1));
  end else
    Exit(- _ComputePosUTF8(Cursor, LineStart, 0));
end;

end.
