mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-05 21:34:11 +02:00
1230 lines
37 KiB
ObjectPascal
1230 lines
37 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
TCustomCodeTool is the ancestor class for code tools which parses code
|
|
beginning with the Main Source code. It can parse atoms, the smallest code
|
|
elements in source code, create new code tree nodes and provides several
|
|
useful functions for parsing and changing code.
|
|
|
|
}
|
|
unit CustomCodeTool;
|
|
|
|
{$ifdef FPC}{$mode objfpc}{$endif}{$H+}
|
|
|
|
interface
|
|
|
|
{$I codetools.inc}
|
|
|
|
uses
|
|
{$IFDEF MEM_CHECK}
|
|
MemCheck,
|
|
{$ENDIF}
|
|
Classes, SysUtils, CodeTree, CodeAtom, SourceLog, KeywordFuncLists,
|
|
BasicCodeTools, LinkScanner, CodeCache, AVL_Tree, TypInfo, SourceChanger;
|
|
|
|
type
|
|
TCustomCodeTool = class(TObject)
|
|
private
|
|
//FIgnoreMissingIncludeFiles: boolean;
|
|
FLastScannerChangeStep: integer;
|
|
FScanner: TLinkScanner;
|
|
protected
|
|
KeyWordFuncList: TKeyWordFunctionList;
|
|
FForceUpdateNeeded: boolean;
|
|
function DefaultKeyWordFunc: boolean;
|
|
procedure BuildDefaultKeyWordFunctions; virtual;
|
|
procedure SetScanner(NewScanner: TLinkScanner); virtual;
|
|
procedure RaiseException(const AMessage: string); virtual;
|
|
public
|
|
Tree: TCodeTree;
|
|
|
|
// current Values, Position, Node ...
|
|
CurPos: TAtomPosition;
|
|
Src: string;
|
|
UpperSrc: string;
|
|
SrcLen: integer;
|
|
CurNode: TCodeTreeNode;
|
|
LastAtoms: TAtomRing;
|
|
|
|
CheckFilesOnDisk: boolean;
|
|
IndentSize: integer;
|
|
VisibleEditorLines: integer;
|
|
JumpCentered: boolean;
|
|
CursorBeyondEOL: boolean;
|
|
|
|
ErrorPosition: TCodeXYPosition;
|
|
|
|
property Scanner: TLinkScanner read FScanner write SetScanner;
|
|
|
|
function FindDeepestNodeAtPos(P: integer;
|
|
ExceptionOnNotFound: boolean): TCodeTreeNode;
|
|
function CaretToCleanPos(Caret: TCodeXYPosition;
|
|
var CleanPos: integer): integer; // 0=valid CleanPos
|
|
//-1=CursorPos was skipped, CleanPos between two links
|
|
// 1=CursorPos beyond scanned code
|
|
//-2=X,Y beyond source
|
|
function CleanPosToCaret(CleanPos: integer;
|
|
var Caret:TCodeXYPosition): boolean; // true=ok, false=invalid CleanPos
|
|
function CleanPosToCaretAndTopLine(CleanPos: integer;
|
|
var Caret:TCodeXYPosition; var NewTopLine: integer): boolean; // true=ok, false=invalid CleanPos
|
|
procedure GetLineInfo(ACleanPos: integer;
|
|
var ALineStart, ALineEnd, AFirstAtomStart, ALastAtomEnd: integer);
|
|
|
|
function UpdateNeeded(OnlyInterfaceNeeded: boolean): boolean;
|
|
procedure BeginParsing(DeleteNodes, OnlyInterfaceNeeded: boolean); virtual;
|
|
procedure MoveCursorToNodeStart(ANode: TCodeTreeNode);
|
|
procedure MoveCursorToCleanPos(ACleanPos: integer);
|
|
function ReadTilSection(SectionType: TCodeTreeNodeDesc): boolean;
|
|
function ReadTilBracketClose(ExceptionOnNotFound: boolean): boolean;
|
|
function ReadBackTilBracketClose(ExceptionOnNotFound: boolean): boolean;
|
|
function DoAtom: boolean; virtual;
|
|
procedure ReadNextAtom;
|
|
procedure UndoReadNextAtom;
|
|
function AtomIs(const AnAtom: shortstring): boolean;
|
|
function UpAtomIs(const AnAtom: shortstring): boolean;
|
|
function ReadNextAtomIs(const AnAtom: shortstring): boolean;
|
|
function ReadNextUpAtomIs(const AnAtom: shortstring): boolean;
|
|
function ReadNextAtomIsChar(const c: char): boolean;
|
|
function AtomIsChar(const c: char): boolean;
|
|
function AtomIsWord: boolean;
|
|
function AtomIsKeyWord: boolean;
|
|
function AtomIsNumber: boolean;
|
|
function AtomIsStringConstant: boolean;
|
|
function AtomIsIdentifier(ExceptionOnNotFound: boolean): boolean;
|
|
function LastAtomIs(BackIndex: integer;
|
|
const AnAtom: shortstring): boolean; // 0=current, 1=prior current, ...
|
|
function LastUpAtomIs(BackIndex: integer;
|
|
const AnAtom: shortstring): boolean; // 0=current, 1=prior current, ...
|
|
function GetAtom: string;
|
|
function GetUpAtom: string;
|
|
function CompareNodeSrc(ANode: TCodeTreeNode;
|
|
const ASource: string): integer;
|
|
function CompareNodeUpSrc(ANode: TCodeTreeNode;
|
|
const ASource: string): integer;
|
|
function CompareSrcIdentifiers(
|
|
CleanStartPos1, CleanStartPos2: integer): boolean;
|
|
function CompareSrcIdentifier(CleanStartPos: integer;
|
|
const Identifier: string): boolean;
|
|
procedure ReadPriorAtom;
|
|
|
|
procedure CreateChildNode;
|
|
procedure EndChildNode;
|
|
|
|
procedure Clear; virtual;
|
|
function NodeDescToStr(Desc: integer): string;
|
|
function NodeSubDescToStr(Desc, SubDesc: integer): string;
|
|
function ConsistencyCheck: integer; // 0 = ok
|
|
procedure WriteDebugTreeReport;
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
ECodeToolError = class(Exception);
|
|
|
|
|
|
implementation
|
|
|
|
|
|
{ TCustomCodeTool }
|
|
|
|
constructor TCustomCodeTool.Create;
|
|
begin
|
|
inherited Create;
|
|
Tree:=TCodeTree.Create;
|
|
KeyWordFuncList:=TKeyWordFunctionList.Create;
|
|
BuildDefaultKeyWordFunctions;
|
|
LastAtoms:=TAtomRing.Create;
|
|
IndentSize:=2;
|
|
VisibleEditorLines:=20;
|
|
CursorBeyondEOL:=true;
|
|
FForceUpdateNeeded:=false;
|
|
Clear;
|
|
end;
|
|
|
|
destructor TCustomCodeTool.Destroy;
|
|
begin
|
|
Clear;
|
|
LastAtoms.Free;
|
|
Tree.Free;
|
|
KeyWordFuncList.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCustomCodeTool.Clear;
|
|
begin
|
|
Tree.Clear;
|
|
CurPos.StartPos:=1;
|
|
CurPos.EndPos:=-1;
|
|
LastAtoms.Clear;
|
|
end;
|
|
|
|
procedure TCustomCodeTool.RaiseException(const AMessage: string);
|
|
var CaretXY: TCodeXYPosition;
|
|
begin
|
|
ErrorPosition.Code:=nil;
|
|
if (CleanPosToCaret(CurPos.StartPos,CaretXY))
|
|
and (CaretXY.Code<>nil) then begin
|
|
ErrorPosition:=CaretXY;
|
|
end else if (Scanner<>nil) and (Scanner.MainCode<>nil) then begin
|
|
ErrorPosition.Code:=TCodeBuffer(Scanner.MainCode);
|
|
ErrorPosition.Y:=-1;
|
|
end;
|
|
raise ECodeToolError.Create(AMessage);
|
|
end;
|
|
|
|
procedure TCustomCodeTool.SetScanner(NewScanner: TLinkScanner);
|
|
begin
|
|
if NewScanner=FScanner then exit;
|
|
Clear;
|
|
FScanner:=NewScanner;
|
|
if FScanner<>nil then
|
|
FLastScannerChangeStep:=Scanner.ChangeStep;
|
|
FForceUpdateNeeded:=true;
|
|
end;
|
|
|
|
function TCustomCodeTool.NodeDescToStr(Desc: integer): string;
|
|
begin
|
|
Result:=NodeDescriptionAsString(Desc);
|
|
end;
|
|
|
|
function TCustomCodeTool.NodeSubDescToStr(Desc, SubDesc: integer): string;
|
|
begin
|
|
if SubDesc<>0 then
|
|
Result:='(unknown subdescriptor '+IntToStr(SubDesc)+')'
|
|
else
|
|
Result:='';
|
|
case Desc of
|
|
ctnProcedure:
|
|
case SubDesc of
|
|
// CodeTreeNodeSubDescriptors
|
|
ctnsForwardDeclaration : Result:='Forward';
|
|
end;
|
|
ctnClass:
|
|
case SubDesc of
|
|
// CodeTreeNodeSubDescriptors
|
|
ctnsForwardDeclaration : Result:='Forward';
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeTool.AtomIs(const AnAtom: shortstring): boolean;
|
|
var AnAtomLen,i : integer;
|
|
begin
|
|
Result:=false;
|
|
if (CurPos.StartPos<=SrcLen) and (CurPos.EndPos<=SrcLen+1)
|
|
and (CurPos.StartPos>=1) then begin
|
|
AnAtomLen:=length(AnAtom);
|
|
if AnAtomLen=CurPos.EndPos-CurPos.StartPos then begin
|
|
for i:=1 to AnAtomLen do
|
|
if AnAtom[i]<>Src[CurPos.StartPos-1+i] then exit;
|
|
Result:=true;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeTool.UpAtomIs(const AnAtom: shortstring): boolean;
|
|
var AnAtomLen,i : integer;
|
|
begin
|
|
Result:=false;
|
|
if (CurPos.StartPos<SrcLen) and (CurPos.EndPos<=SrcLen+1)
|
|
and (CurPos.StartPos>=1) then begin
|
|
AnAtomLen:=length(AnAtom);
|
|
if AnAtomLen=CurPos.EndPos-CurPos.StartPos then begin
|
|
for i:=1 to AnAtomLen do
|
|
if AnAtom[i]<>UpperSrc[CurPos.StartPos-1+i] then exit;
|
|
Result:=true;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeTool.ReadNextAtomIs(const AnAtom: shortstring): boolean;
|
|
begin
|
|
ReadNextAtom;
|
|
Result:=AtomIs(AnAtom);
|
|
end;
|
|
|
|
function TCustomCodeTool.ReadNextAtomIsChar(const c: char): boolean;
|
|
begin
|
|
ReadNextAtom;
|
|
Result:=AtomIsChar(c);
|
|
end;
|
|
|
|
function TCustomCodeTool.ReadNextUpAtomIs(const AnAtom: shortstring): boolean;
|
|
begin
|
|
ReadNextAtom;
|
|
Result:=UpAtomIs(AnAtom);
|
|
end;
|
|
|
|
function TCustomCodeTool.CompareNodeSrc(ANode: TCodeTreeNode;
|
|
const ASource: string): integer;
|
|
var ASrcLen, i, NodeSrcLen : integer;
|
|
begin
|
|
if (ANode.StartPos<=SrcLen) and (ANode.EndPos<=SrcLen+1)
|
|
and (ANode.StartPos>=1) then begin
|
|
ASrcLen:=length(ASource);
|
|
NodeSrcLen:=ANode.EndPos-ANode.StartPos;
|
|
if ASrcLen=NodeSrcLen then begin
|
|
for i:=1 to ASrcLen do
|
|
if ASource[i]<>Src[ANode.StartPos-1+i] then begin
|
|
if ASource[i]>Src[ANode.StartPos-1+i] then
|
|
Result:=1
|
|
else
|
|
Result:=-1;
|
|
exit;
|
|
end;
|
|
Result:=0;
|
|
end else if ASrcLen<NodeSrcLen then
|
|
Result:=1
|
|
else
|
|
Result:=-1;
|
|
end else
|
|
Result:=-1;
|
|
end;
|
|
|
|
function TCustomCodeTool.CompareNodeUpSrc(ANode: TCodeTreeNode;
|
|
const ASource: string): integer;
|
|
var ASrcLen, i, NodeSrcLen : integer;
|
|
begin
|
|
if (ANode.StartPos<=SrcLen) and (ANode.EndPos<=SrcLen+1)
|
|
and (ANode.StartPos>=1) then begin
|
|
ASrcLen:=length(ASource);
|
|
NodeSrcLen:=ANode.EndPos-ANode.StartPos;
|
|
if ASrcLen<=NodeSrcLen then begin
|
|
i:=1;
|
|
while (i<=ASrcLen) and (IsIdentChar[Src[ANode.StartPos-1+i]]) do begin
|
|
if ASource[i]<>UpperSrc[ANode.StartPos-1+i] then begin
|
|
if ASource[i]>UpperSrc[ANode.StartPos-1+i] then
|
|
Result:=1
|
|
else
|
|
Result:=-1;
|
|
exit;
|
|
end;
|
|
inc(i);
|
|
end;
|
|
Result:=0;
|
|
end else
|
|
Result:=-1;
|
|
end else
|
|
Result:=-1;
|
|
end;
|
|
|
|
function TCustomCodeTool.CompareSrcIdentifiers(
|
|
CleanStartPos1, CleanStartPos2: integer): boolean;
|
|
begin
|
|
Result:=(CleanStartPos1>=1) and (CleanStartPos1<=SrcLen)
|
|
and (CleanStartPos2>=1) and (CleanStartPos2<=SrcLen);
|
|
if not Result then exit;
|
|
while (CleanStartPos1<=SrcLen) and (IsIdentChar[Src[CleanStartPos1]]) do begin
|
|
if (UpperSrc[CleanStartPos1]<>UpperSrc[CleanStartPos2]) then begin
|
|
Result:=false;
|
|
exit;
|
|
end;
|
|
inc(CleanStartPos1);
|
|
inc(CleanStartPos2);
|
|
end;
|
|
Result:=(CleanStartPos2>SrcLen) or (not IsIdentChar[Src[CleanStartPos2]]);
|
|
end;
|
|
|
|
function TCustomCodeTool.AtomIsChar(const c: char): boolean;
|
|
begin
|
|
Result:=(CurPos.StartPos<=SrcLen)
|
|
and (CurPos.EndPos-CurPos.StartPos=1)
|
|
and (Src[CurPos.StartPos]=c);
|
|
end;
|
|
|
|
function TCustomCodeTool.AtomIsWord: boolean;
|
|
begin
|
|
Result:=(CurPos.StartPos<=SrcLen)
|
|
and (IsIdentStartChar[UpperSrc[CurPos.StartPos]])
|
|
end;
|
|
|
|
function TCustomCodeTool.AtomIsKeyWord: boolean;
|
|
begin
|
|
Result:=(CurPos.StartPos<=SrcLen)
|
|
and (IsIdentStartChar[UpperSrc[CurPos.StartPos]])
|
|
and (WordIsKeyWord.DoItUpperCase(UpperSrc,CurPos.StartPos,
|
|
CurPos.EndPos-CurPos.StartPos));
|
|
end;
|
|
|
|
function TCustomCodeTool.AtomIsIdentifier(ExceptionOnNotFound: boolean):boolean;
|
|
begin
|
|
if CurPos.StartPos<=SrcLen then begin
|
|
if IsIdentStartChar[UpperSrc[CurPos.StartPos]] then begin
|
|
if not WordIsKeyWord.DoItUpperCase(UpperSrc,CurPos.StartPos,
|
|
CurPos.EndPos-CurPos.StartPos) then
|
|
Result:=true
|
|
else begin
|
|
if ExceptionOnNotFound then
|
|
RaiseException(
|
|
'syntax error: identifier expected, but keyword '+GetAtom+' found')
|
|
else
|
|
Result:=false;
|
|
end;
|
|
end else begin
|
|
if ExceptionOnNotFound then
|
|
RaiseException(
|
|
'syntax error: identifier expected, but '+GetAtom+' found')
|
|
else
|
|
Result:=false;
|
|
end;
|
|
end else begin
|
|
if ExceptionOnNotFound then
|
|
RaiseException('unexpected end of file (identifier expected)')
|
|
else
|
|
Result:=false;
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeTool.AtomIsNumber: boolean;
|
|
begin
|
|
Result:=(CurPos.StartPos<=SrcLen)
|
|
and (Src[CurPos.StartPos] in ['0'..'9','%','$']);
|
|
end;
|
|
|
|
function TCustomCodeTool.AtomIsStringConstant: boolean;
|
|
begin
|
|
Result:=(CurPos.StartPos<=SrcLen)
|
|
and (Src[CurPos.StartPos] in ['''','#']);
|
|
end;
|
|
|
|
function TCustomCodeTool.LastAtomIs(BackIndex: integer;
|
|
const AnAtom: shortstring): boolean;
|
|
var ap: TAtomPosition;
|
|
AnAtomLen: integer;
|
|
i: integer;
|
|
begin
|
|
Result:=false;
|
|
if (BackIndex>=0) and (BackIndex<LastAtoms.Count) then begin
|
|
ap:=LastAtoms.GetValueAt(BackIndex);
|
|
Result:=false;
|
|
if (ap.StartPos<SrcLen) and (ap.EndPos<=SrcLen+1)
|
|
and (ap.StartPos>=1) then begin
|
|
AnAtomLen:=length(AnAtom);
|
|
if AnAtomLen=ap.EndPos-ap.StartPos then begin
|
|
for i:=1 to AnAtomLen do
|
|
if AnAtom[i]<>Src[ap.StartPos-1+i] then exit;
|
|
Result:=true;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeTool.LastUpAtomIs(BackIndex: integer;
|
|
const AnAtom: shortstring): boolean;
|
|
var ap: TAtomPosition;
|
|
AnAtomLen: integer;
|
|
i: integer;
|
|
begin
|
|
Result:=false;
|
|
if (BackIndex>=0) and (BackIndex<LastAtoms.Count) then begin
|
|
ap:=LastAtoms.GetValueAt(BackIndex);
|
|
Result:=false;
|
|
if (ap.StartPos<SrcLen) and (ap.EndPos<=SrcLen+1)
|
|
and (ap.StartPos>=1) then begin
|
|
AnAtomLen:=length(AnAtom);
|
|
if AnAtomLen=ap.EndPos-ap.StartPos then begin
|
|
for i:=1 to AnAtomLen do
|
|
if AnAtom[i]<>UpperSrc[ap.StartPos-1+i] then exit;
|
|
Result:=true;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeTool.GetAtom: string;
|
|
begin
|
|
Result:=copy(Src,CurPos.StartPos,CurPos.EndPos-CurPos.StartPos);
|
|
end;
|
|
|
|
function TCustomCodeTool.GetUpAtom: string;
|
|
begin
|
|
Result:=copy(UpperSrc,CurPos.StartPos,CurPos.EndPos-CurPos.StartPos);
|
|
end;
|
|
|
|
procedure TCustomCodeTool.ReadNextAtom;
|
|
var c1, c2: char;
|
|
CommentLvl: integer;
|
|
begin
|
|
// Skip all spaces and comments
|
|
CommentLvl:=0;
|
|
if (CurPos.StartPos<CurPos.EndPos) and (CurPos.StartPos>=1) then
|
|
LastAtoms.Add(CurPos);
|
|
CurPos.StartPos:=CurPos.EndPos;
|
|
//if CurPos.StartPos<1 then CurPos.StartPos:=SrcLen+1;
|
|
while CurPos.StartPos<=SrcLen do begin
|
|
if IsCommentStartChar[Src[CurPos.StartPos]] then begin
|
|
case Src[CurPos.StartPos] of
|
|
'{': // pascal comment
|
|
begin
|
|
CommentLvl:=1;
|
|
inc(CurPos.StartPos);
|
|
while (CurPos.StartPos<=SrcLen) and (CommentLvl>0) do begin
|
|
case Src[CurPos.StartPos] of
|
|
'{': if Scanner.NestedComments then inc(CommentLvl);
|
|
'}': dec(CommentLvl);
|
|
end;
|
|
inc(CurPos.StartPos);
|
|
end;
|
|
end;
|
|
'/': // Delphi comment
|
|
if (CurPos.StartPos<SrcLen) and (Src[CurPos.StartPos+1]='/') then begin
|
|
inc(CurPos.StartPos,2);
|
|
while (CurPos.StartPos<=SrcLen)
|
|
and (not (Src[CurPos.StartPos] in [#10,#13])) do
|
|
inc(CurPos.StartPos);
|
|
inc(CurPos.StartPos);
|
|
if (CurPos.StartPos<=SrcLen) and (Src[CurPos.StartPos] in [#10,#13])
|
|
and (Src[CurPos.StartPos-1]<>Src[CurPos.StartPos]) then
|
|
inc(CurPos.StartPos);
|
|
end else
|
|
break;
|
|
'(': // old turbo pascal comment
|
|
if (CurPos.StartPos<SrcLen) and (Src[CurPos.StartPos+1]='*') then begin
|
|
inc(CurPos.StartPos,3);
|
|
while (CurPos.StartPos<=SrcLen)
|
|
and ((Src[CurPos.StartPos-1]<>'*') or (Src[CurPos.StartPos]<>')')) do
|
|
inc(CurPos.StartPos);
|
|
inc(CurPos.StartPos);
|
|
end else
|
|
break;
|
|
end;
|
|
end else if IsSpaceChar[Src[CurPos.StartPos]] then begin
|
|
repeat
|
|
inc(CurPos.StartPos);
|
|
until (CurPos.StartPos>SrcLen)
|
|
or (not (IsSpaceChar[Src[CurPos.StartPos]]));
|
|
end else begin
|
|
break;
|
|
end;
|
|
end;
|
|
CurPos.EndPos:=CurPos.StartPos;
|
|
if CurPos.StartPos>SrcLen then
|
|
exit;
|
|
// read atom
|
|
c1:=UpperSrc[CurPos.EndPos];
|
|
case c1 of
|
|
'_','A'..'Z':
|
|
begin
|
|
inc(CurPos.EndPos);
|
|
while (CurPos.EndPos<=SrcLen)
|
|
and (IsIdentChar[UpperSrc[CurPos.EndPos]]) do
|
|
inc(CurPos.EndPos);
|
|
end;
|
|
'''','#':
|
|
begin
|
|
while (CurPos.EndPos<=SrcLen) do begin
|
|
case (Src[CurPos.EndPos]) of
|
|
'#':
|
|
begin
|
|
inc(CurPos.EndPos);
|
|
while (CurPos.EndPos<=SrcLen)
|
|
and (IsNumberChar[Src[CurPos.EndPos]]) do
|
|
inc(CurPos.EndPos);
|
|
end;
|
|
'''':
|
|
begin
|
|
inc(CurPos.EndPos);
|
|
while (CurPos.EndPos<=SrcLen)
|
|
and (Src[CurPos.EndPos]<>'''') do
|
|
inc(CurPos.EndPos);
|
|
inc(CurPos.EndPos);
|
|
end;
|
|
else
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
'0'..'9':
|
|
begin
|
|
inc(CurPos.EndPos);
|
|
while (CurPos.EndPos<=SrcLen) and (IsNumberChar[Src[CurPos.EndPos]]) do
|
|
inc(CurPos.EndPos);
|
|
if (CurPos.EndPos<SrcLen)
|
|
and (Src[CurPos.EndPos]='.') and (Src[CurPos.EndPos+1]<>'.') then begin
|
|
// real type number
|
|
inc(CurPos.EndPos);
|
|
while (CurPos.EndPos<=SrcLen) and (IsNumberChar[Src[CurPos.EndPos]])
|
|
do
|
|
inc(CurPos.EndPos);
|
|
if (CurPos.EndPos<=SrcLen) and (UpperSrc[CurPos.EndPos]='E') then
|
|
begin
|
|
// read exponent
|
|
inc(CurPos.EndPos);
|
|
if (CurPos.EndPos<=SrcLen) and (Src[CurPos.EndPos] in ['-','+'])
|
|
then inc(CurPos.EndPos);
|
|
while (CurPos.EndPos<=SrcLen) and (IsNumberChar[Src[CurPos.EndPos]])
|
|
do
|
|
inc(CurPos.EndPos);
|
|
end;
|
|
end;
|
|
end;
|
|
'%':
|
|
begin
|
|
inc(CurPos.EndPos);
|
|
while (CurPos.EndPos<=SrcLen) and (Src[CurPos.EndPos] in ['0'..'1']) do
|
|
inc(CurPos.EndPos);
|
|
end;
|
|
'$':
|
|
begin
|
|
inc(CurPos.EndPos);
|
|
while (CurPos.EndPos<=SrcLen)
|
|
and (IsHexNumberChar[UpperSrc[CurPos.EndPos]]) do
|
|
inc(CurPos.EndPos);
|
|
end;
|
|
else
|
|
inc(CurPos.EndPos);
|
|
if CurPos.EndPos<=SrcLen then begin
|
|
c2:=Src[CurPos.EndPos];
|
|
// test for double char operators :=, +=, -=, /=, *=, <>, <=, >=, **, ><
|
|
if ((c2='=') and (IsEqualOperatorStartChar[c1]))
|
|
or ((c1='<') and (c2='>'))
|
|
or ((c1='>') and (c2='<'))
|
|
or ((c1='.') and (c2='.'))
|
|
or ((c1='*') and (c2='*'))
|
|
or ((c1='@') and (c2='@'))
|
|
then inc(CurPos.EndPos);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomCodeTool.ReadPriorAtom;
|
|
|
|
procedure ReadStringConstantBackward;
|
|
var PrePos: integer;
|
|
begin
|
|
while (CurPos.StartPos>1) do begin
|
|
case Src[CurPos.StartPos-1] of
|
|
'''':
|
|
begin
|
|
dec(CurPos.StartPos);
|
|
repeat
|
|
dec(CurPos.StartPos);
|
|
until (CurPos.StartPos<1) or (Src[CurPos.StartPos]='''');
|
|
end;
|
|
'0'..'9':
|
|
begin
|
|
// test if char constant
|
|
PrePos:=CurPos.StartPos-1;
|
|
while (PrePos>1) and (IsNumberChar[Src[PrePos]]) do
|
|
dec(PrePos);
|
|
if (PrePos<1) then break;
|
|
if Src[PrePos]='#' then
|
|
CurPos.StartPos:=PrePos
|
|
else
|
|
break;
|
|
end;
|
|
else
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
type
|
|
TNumberType = (ntDecimal, ntHexadecimal, ntBinary, ntIdentifier,
|
|
ntCharConstant, ntFloat, ntFloatWithExponent);
|
|
TNumberTypes = set of TNumberType;
|
|
|
|
const
|
|
AllNumberTypes: TNumberTypes = [ntDecimal, ntHexadecimal, ntBinary,
|
|
ntIdentifier, ntCharConstant, ntFloat, ntFloatWithExponent];
|
|
|
|
var c1, c2: char;
|
|
CommentLvl, PrePos: integer;
|
|
ForbiddenNumberTypes: TNumberTypes;
|
|
begin
|
|
if LastAtoms.Count>0 then begin
|
|
UndoReadNextAtom;
|
|
exit;
|
|
end;
|
|
// Skip all spaces and comments
|
|
CommentLvl:=0;
|
|
dec(CurPos.StartPos);
|
|
while CurPos.StartPos>=1 do begin
|
|
if IsCommentEndChar[Src[CurPos.StartPos]] then begin
|
|
case Src[CurPos.StartPos] of
|
|
'}': // pascal comment
|
|
begin
|
|
CommentLvl:=1;
|
|
dec(CurPos.StartPos);
|
|
while (CurPos.StartPos>=1) and (CommentLvl>0) do begin
|
|
case Src[CurPos.StartPos] of
|
|
'}': if Scanner.NestedComments then inc(CommentLvl);
|
|
'{': dec(CommentLvl);
|
|
end;
|
|
dec(CurPos.StartPos);
|
|
end;
|
|
end;
|
|
#10,#13: // possible Delphi comment
|
|
begin
|
|
// read backwards till line start or comment start
|
|
dec(CurPos.StartPos);
|
|
if (CurPos.StartPos>=1) and (Src[CurPos.StartPos] in [#10,#13])
|
|
and (Src[CurPos.StartPos+1]<>Src[CurPos.StartPos]) then
|
|
dec(CurPos.StartPos);
|
|
PrePos:=CurPos.StartPos;
|
|
while (PrePos>1) do begin
|
|
case Src[PrePos] of
|
|
'/':
|
|
if Src[PrePos-1]='/' then begin
|
|
// this was a delphi comment -> skip comment
|
|
CurPos.StartPos:=PrePos-2;
|
|
break;
|
|
end;
|
|
#10,#13:
|
|
// it was just a line break
|
|
break;
|
|
end;
|
|
dec(PrePos);
|
|
end;
|
|
end;
|
|
')': // old turbo pascal comment
|
|
if (CurPos.StartPos>1) and (Src[CurPos.StartPos-1]='*') then begin
|
|
dec(CurPos.StartPos,3);
|
|
while (CurPos.StartPos>=1)
|
|
and ((Src[CurPos.StartPos]<>'(') or (Src[CurPos.StartPos+1]<>'*')) do
|
|
dec(CurPos.StartPos);
|
|
dec(CurPos.StartPos);
|
|
end else
|
|
break;
|
|
end;
|
|
end else if IsSpaceChar[Src[CurPos.StartPos]] then begin
|
|
repeat
|
|
dec(CurPos.StartPos);
|
|
until (CurPos.StartPos<1) or (Src[CurPos.StartPos] in [#10,#13])
|
|
or (not (IsSpaceChar[Src[CurPos.StartPos]]));
|
|
end else begin
|
|
break;
|
|
end;
|
|
end;
|
|
// CurPos.StartPos now points to the last char of the prior atom
|
|
CurPos.EndPos:=CurPos.StartPos+1;
|
|
if CurPos.StartPos<1 then
|
|
exit;
|
|
// read atom
|
|
c2:=UpperSrc[CurPos.StartPos];
|
|
case c2 of
|
|
'_','A'..'Z':
|
|
begin
|
|
// definitely an identifier or a keyword
|
|
while (CurPos.StartPos>1)
|
|
and (IsIdentChar[UpperSrc[CurPos.StartPos-1]]) do
|
|
dec(CurPos.StartPos);
|
|
end;
|
|
'''':
|
|
begin
|
|
inc(CurPos.StartPos);
|
|
ReadStringConstantBackward;
|
|
end;
|
|
'0'..'9':
|
|
begin
|
|
// could be a decimal number, an identifier, a hex number,
|
|
// a binary number, a char constant, a float, a float with exponent
|
|
ForbiddenNumberTypes:=[];
|
|
while true do begin
|
|
case UpperSrc[CurPos.StartPos] of
|
|
'0'..'1':
|
|
;
|
|
'2'..'9':
|
|
ForbiddenNumberTypes:=ForbiddenNumberTypes+[ntBinary];
|
|
'A'..'D','F':
|
|
ForbiddenNumberTypes:=ForbiddenNumberTypes
|
|
+[ntBinary,ntDecimal,ntCharConstant,ntFloat,ntFloatWithExponent];
|
|
'E':
|
|
ForbiddenNumberTypes:=ForbiddenNumberTypes
|
|
+[ntBinary,ntDecimal,ntCharConstant,ntFloat];
|
|
'G'..'Z','_':
|
|
ForbiddenNumberTypes:=AllNumberTypes-[ntIdentifier];
|
|
'.':
|
|
begin
|
|
// could be the point of a float
|
|
if (ntFloat in ForbiddenNumberTypes)
|
|
or (CurPos.StartPos<=1) or (Src[CurPos.StartPos-1]='.') then begin
|
|
inc(CurPos.StartPos);
|
|
break;
|
|
end;
|
|
dec(CurPos.StartPos);
|
|
// this was the part of a float after the point
|
|
// -> read decimal in front
|
|
ForbiddenNumberTypes:=AllNumberTypes-[ntDecimal];
|
|
end;
|
|
'+','-':
|
|
begin
|
|
// could be part of an exponent
|
|
if (ntFloatWithExponent in ForbiddenNumberTypes)
|
|
or (CurPos.StartPos<=1) or (UpperSrc[CurPos.StartPos-1]<>'E') then
|
|
begin
|
|
inc(CurPos.StartPos);
|
|
break;
|
|
end;
|
|
dec(CurPos.StartPos);
|
|
// this was the exponent of a float -> read the float
|
|
ForbiddenNumberTypes:=AllNumberTypes-[ntFloat];
|
|
end;
|
|
'#': // char constant found
|
|
begin
|
|
if (ntCharConstant in ForbiddenNumberTypes) then
|
|
inc(CurPos.StartPos);
|
|
ReadStringConstantBackward;
|
|
break;
|
|
end;
|
|
'$':
|
|
begin
|
|
// hexadecimal number found
|
|
if (ntHexadecimal in ForbiddenNumberTypes) then
|
|
inc(CurPos.StartPos);
|
|
break;
|
|
end;
|
|
'%':
|
|
begin
|
|
// binary number found
|
|
if (ntBinary in ForbiddenNumberTypes) then
|
|
inc(CurPos.StartPos);
|
|
break;
|
|
end;
|
|
else
|
|
begin
|
|
inc(CurPos.StartPos);
|
|
break;
|
|
end;
|
|
end;
|
|
if ForbiddenNumberTypes=AllNumberTypes then begin
|
|
inc(CurPos.StartPos);
|
|
break;
|
|
end;
|
|
if CurPos.StartPos<=1 then exit;
|
|
dec(CurPos.StartPos);
|
|
end;
|
|
end;
|
|
else
|
|
if CurPos.StartPos>1 then begin
|
|
c1:=Src[CurPos.StartPos-1];
|
|
// test for double char operators :=, +=, -=, /=, *=, <>, <=, >=, **, ><
|
|
if ((c2='=') and (IsEqualOperatorStartChar[c1]))
|
|
or ((c1='<') and (c2='>'))
|
|
or ((c1='>') and (c2='<'))
|
|
or ((c1='.') and (c2='.'))
|
|
or ((c1='*') and (c2='*'))
|
|
or ((c1='@') and (c2='@'))
|
|
then dec(CurPos.StartPos);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomCodeTool.UndoReadNextAtom;
|
|
begin
|
|
if LastAtoms.Count>0 then begin
|
|
CurPos:=LastAtoms.GetValueAt(0);
|
|
LastAtoms.UndoLastAdd;
|
|
end else
|
|
RaiseException('TCustomCodeTool.UndoReadNextAtom impossible');
|
|
end;
|
|
|
|
function TCustomCodeTool.ReadTilSection(
|
|
SectionType: TCodeTreeNodeDesc): boolean;
|
|
var SectionID: TCodeTreeNodeDesc;
|
|
begin
|
|
Result:=false;
|
|
if not (SectionType in AllCodeSections) then exit;
|
|
Result:=false;
|
|
repeat
|
|
ReadNextAtom;
|
|
if (CurPos.StartPos>SrcLen) then break;
|
|
if IsKeyWordSection.DoItUppercase(UpperSrc,CurPos.StartPos,
|
|
CurPos.EndPos-CurPos.StartPos)
|
|
and (not LastAtomIs(1,'=')) then begin
|
|
if UpAtomIs('UNIT') then
|
|
SectionID:=ctnUnit
|
|
else if UpAtomIs('PROGRAM') then
|
|
SectionID:=ctnProgram
|
|
else if UpAtomIs('PACKAGE') then
|
|
SectionID:=ctnPackage
|
|
else if UpAtomIs('LIBRARY') then
|
|
SectionID:=ctnLibrary
|
|
else if UpAtomIs('INTERFACE') then
|
|
SectionID:=ctnInterface
|
|
else if UpAtomIs('IMPLEMENTATION') then
|
|
SectionID:=ctnImplementation
|
|
else if UpAtomIs('INITIALIZATION') then
|
|
SectionID:=ctnInitialization
|
|
else if UpAtomIs('FINALIZATION') then
|
|
SectionID:=ctnFinalization
|
|
else
|
|
SectionID:=ctnNone;
|
|
if (SectionType=SectionID)
|
|
or ((SectionType=ctnInterface)
|
|
and (SectionID in [ctnProgram,ctnPackage,ctnLibrary])) then begin
|
|
Result:=true; exit;
|
|
end;
|
|
if SectionID>SectionType then
|
|
exit;
|
|
end;
|
|
until false;
|
|
end;
|
|
|
|
function TCustomCodeTool.ReadTilBracketClose(
|
|
ExceptionOnNotFound: boolean): boolean;
|
|
// reads code brackets (not comment brackets)
|
|
var CloseBracket, AntiCloseBracket: char;
|
|
Start: TAtomPosition;
|
|
begin
|
|
Result:=false;
|
|
if AtomIsChar('(') then begin
|
|
CloseBracket:=')';
|
|
AntiCloseBracket:=']';
|
|
end else if AtomIsChar('[') then begin
|
|
CloseBracket:=']';
|
|
AntiCloseBracket:=')';
|
|
end else begin
|
|
if ExceptionOnNotFound then
|
|
RaiseException(
|
|
'syntax error: bracket open expected, but '+GetAtom+' found');
|
|
exit;
|
|
end;
|
|
Start:=CurPos;
|
|
repeat
|
|
ReadNextAtom;
|
|
if (AtomIsChar(CloseBracket)) then break;
|
|
if (CurPos.StartPos>SrcLen) or AtomIsChar(AntiCloseBracket)
|
|
or UpAtomIs('END') then begin
|
|
CurPos:=Start;
|
|
if ExceptionOnNotFound then
|
|
RaiseException(
|
|
'syntax error: bracket '+CloseBracket+' not found');
|
|
exit;
|
|
end;
|
|
if (AtomIsChar('(')) or (AtomIsChar('[')) then begin
|
|
if not ReadTilBracketClose(ExceptionOnNotFound) then exit;
|
|
end;
|
|
until false;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TCustomCodeTool.ReadBackTilBracketClose(
|
|
ExceptionOnNotFound: boolean): boolean;
|
|
// reads code brackets (not comment brackets)
|
|
var CloseBracket, AntiCloseBracket: char;
|
|
Start: TAtomPosition;
|
|
begin
|
|
Result:=false;
|
|
if AtomIsChar(')') then begin
|
|
CloseBracket:='(';
|
|
AntiCloseBracket:='[';
|
|
end else if AtomIsChar(']') then begin
|
|
CloseBracket:='[';
|
|
AntiCloseBracket:='(';
|
|
end else begin
|
|
if ExceptionOnNotFound then
|
|
RaiseException(
|
|
'syntax error: bracket close expected, but '+GetAtom+' found');
|
|
exit;
|
|
end;
|
|
Start:=CurPos;
|
|
repeat
|
|
ReadPriorAtom;
|
|
if (AtomIsChar(CloseBracket)) then break;
|
|
if (CurPos.StartPos<1) or AtomIsChar(AntiCloseBracket)
|
|
or UpAtomIs('END') or UpAtomIs('BEGIN') then begin
|
|
CurPos:=Start;
|
|
if ExceptionOnNotFound then
|
|
RaiseException(
|
|
'syntax error: bracket '+CloseBracket+' not found');
|
|
exit;
|
|
end;
|
|
if (AtomIsChar(')')) or (AtomIsChar(']')) then begin
|
|
if not ReadBackTilBracketClose(ExceptionOnNotFound) then exit;
|
|
end;
|
|
until false;
|
|
Result:=true;
|
|
end;
|
|
|
|
procedure TCustomCodeTool.BeginParsing(DeleteNodes,
|
|
OnlyInterfaceNeeded: boolean);
|
|
begin
|
|
Scanner.Scan(OnlyInterfaceNeeded,CheckFilesOnDisk);
|
|
Src:=Scanner.CleanedSrc;
|
|
FLastScannerChangeStep:=Scanner.ChangeStep;
|
|
UpperSrc:=UpperCaseStr(Src);
|
|
SrcLen:=length(Src);
|
|
CurPos.StartPos:=1;
|
|
CurPos.EndPos:=1;
|
|
LastAtoms.Clear;
|
|
CurNode:=nil;
|
|
if DeleteNodes then Tree.Clear;
|
|
end;
|
|
|
|
procedure TCustomCodeTool.MoveCursorToNodeStart(ANode: TCodeTreeNode);
|
|
begin
|
|
CurPos.StartPos:=ANode.StartPos;
|
|
CurPos.EndPos:=ANode.StartPos;
|
|
LastAtoms.Clear;
|
|
CurNode:=ANode;
|
|
end;
|
|
|
|
procedure TCustomCodeTool.MoveCursorToCleanPos(ACleanPos: integer);
|
|
begin
|
|
CurPos.StartPos:=ACleanPos;
|
|
CurPos.EndPos:=ACleanPos;
|
|
LastAtoms.Clear;
|
|
CurNode:=nil;
|
|
end;
|
|
|
|
procedure TCustomCodeTool.CreateChildNode;
|
|
var NewNode: TCodeTreeNode;
|
|
begin
|
|
NewNode:=NodeMemManager.NewNode;
|
|
Tree.AddNodeAsLastChild(CurNode,NewNode);
|
|
CurNode:=NewNode;
|
|
CurNode.StartPos:=CurPos.StartPos;
|
|
end;
|
|
|
|
procedure TCustomCodeTool.EndChildNode;
|
|
begin
|
|
CurNode:=CurNode.Parent;
|
|
end;
|
|
|
|
procedure TCustomCodeTool.BuildDefaultKeyWordFunctions;
|
|
begin
|
|
KeyWordFuncList.Clear;
|
|
KeyWordFuncList.DefaultKeyWordFunction:=
|
|
{$ifdef FPC}@{$endif}DefaultKeyWordFunc;
|
|
end;
|
|
|
|
function TCustomCodeTool.DoAtom: boolean;
|
|
begin
|
|
if (CurPos.StartPos>SrcLen) or (CurPos.EndPos<=CurPos.StartPos) then
|
|
Result:=false
|
|
else if IsIdentStartChar[Src[CurPos.StartPos]] then
|
|
Result:=KeyWordFuncList.DoItUppercase(UpperSrc,CurPos.StartPos,
|
|
CurPos.EndPos-CurPos.StartPos)
|
|
else
|
|
Result:=true;
|
|
end;
|
|
|
|
function TCustomCodeTool.DefaultKeyWordFunc: boolean;
|
|
begin
|
|
Result:=true;
|
|
end;
|
|
|
|
function TCustomCodeTool.ConsistencyCheck: integer;
|
|
// 0 = ok
|
|
begin
|
|
Result:=Tree.ConsistencyCheck;
|
|
if Result<>0 then begin
|
|
dec(Result,100); exit;
|
|
end;
|
|
Result:=0;
|
|
end;
|
|
|
|
procedure TCustomCodeTool.WriteDebugTreeReport;
|
|
|
|
procedure WriteSrcSubString(A,Len: integer);
|
|
var i: integer;
|
|
begin
|
|
write('"');
|
|
for i:=A to A+Len-1 do begin
|
|
if (i>0) and (i<SrcLen) and (ord(Src[i])>31) then
|
|
write(Src[i]);
|
|
end;
|
|
write('"');
|
|
end;
|
|
|
|
procedure WriteSubTree(RootNode: TCodeTreeNode; Indent: string);
|
|
begin
|
|
while RootNode<>nil do begin
|
|
write(Indent);
|
|
with RootNode do begin
|
|
write(NodeDescToStr(Desc),'(',NodeSubDescToStr(Desc,SubDesc),') ');
|
|
write(' Start=',StartPos,' ');
|
|
WriteSrcSubString(StartPos,5);
|
|
write(' End=',EndPos,' ');
|
|
WriteSrcSubString(EndPos-5,5);
|
|
{$ifdef fpc}
|
|
write(' Self=',HexStr(Cardinal(RootNode),8));
|
|
write(' P=',HexStr(Cardinal(Parent),8));
|
|
write(' NB=',HexStr(Cardinal(NextBrother),8));
|
|
//write(' PB=',HexStr(Cardinal(PriorBrother),8));
|
|
//write(' FC=',HexStr(Cardinal(FirstChild),8));
|
|
//write(' LC=',HexStr(Cardinal(LastChild),8));
|
|
{$endif}
|
|
end;
|
|
writeln('');
|
|
WriteSubTree(RootNode.FirstChild,Indent+' ');
|
|
RootNode:=RootNode.NextBrother;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
writeln('[TCustomCodeTool.WriteDebugTreeReport] Consistency=',
|
|
ConsistencyCheck);
|
|
WriteSubTree(Tree.Root,' ');
|
|
end;
|
|
|
|
function TCustomCodeTool.FindDeepestNodeAtPos(P: integer;
|
|
ExceptionOnNotFound: boolean): TCodeTreeNode;
|
|
|
|
function SearchInNode(ANode: TCodeTreeNode): TCodeTreeNode;
|
|
begin
|
|
if ANode<>nil then begin
|
|
//writeln('SearchInNode ',NodeDescriptionAsString(ANode.Desc),
|
|
//',',ANode.StartPos,',',ANode.EndPos,', p=',p,
|
|
//' "',copy(Src,ANode.StartPos,4),'" - "',copy(Src,ANode.EndPos-5,4),'"');
|
|
if (ANode.StartPos<=P) and ((ANode.EndPos>P) or (ANode.EndPos<1)) then
|
|
begin
|
|
// first search in childs
|
|
Result:=SearchInNode(ANode.FirstChild);
|
|
if Result=nil then
|
|
// no child found -> take this node
|
|
Result:=ANode;
|
|
end else
|
|
// search in next node
|
|
Result:=SearchInNode(ANode.NextBrother);
|
|
end else
|
|
Result:=nil;
|
|
end;
|
|
|
|
// TCustomCodeTool.FindDeepestNodeAtPos
|
|
begin
|
|
Result:=SearchInNode(Tree.Root);
|
|
if (Result=nil) and ExceptionOnNotFound then begin
|
|
MoveCursorToCleanPos(P);
|
|
RaiseException('no node found at cursor');
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeTool.CaretToCleanPos(Caret: TCodeXYPosition;
|
|
var CleanPos: integer): integer;
|
|
begin
|
|
//writeln('TCustomCodeTool.CaretToCleanPos A ',Caret.Code.Filename,' ',Caret.Code.SourceLength);
|
|
Caret.Code.LineColToPosition(Caret.Y,Caret.X,CleanPos);
|
|
//writeln('TCustomCodeTool.CaretToCleanPos B ',CleanPos,',',Caret.Y,',',Caret.X);
|
|
if (CleanPos>=1) then
|
|
Result:=Scanner.CursorToCleanPos(CleanPos,Caret.Code,CleanPos)
|
|
else
|
|
Result:=-2; // x,y beyond source
|
|
//writeln('TCustomCodeTool.CaretToCleanPos C CleanPos=',CleanPos,' Result=',Result);
|
|
end;
|
|
|
|
function TCustomCodeTool.CleanPosToCaret(CleanPos: integer;
|
|
var Caret:TCodeXYPosition): boolean; // true=ok, false=invalid CleanPos
|
|
var p: integer;
|
|
Code: Pointer;
|
|
begin
|
|
Result:=Scanner.CleanedPosToCursor(CleanPos,p,Code);
|
|
if Result then begin
|
|
Caret.Code:=TCodeBuffer(Code);
|
|
TCodeBuffer(Code).AbsoluteToLineCol(p,Caret.Y,Caret.X);
|
|
Result:=(Caret.Y>=0);
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeTool.CleanPosToCaretAndTopLine(CleanPos: integer;
|
|
var Caret:TCodeXYPosition; var NewTopLine: integer): boolean;
|
|
// true=ok, false=invalid CleanPos
|
|
begin
|
|
Result:=CleanPosToCaret(CleanPos,Caret);
|
|
if Result then begin
|
|
if JumpCentered then begin
|
|
NewTopLine:=Caret.Y-(VisibleEditorLines shr 1);
|
|
if NewTopLine<1 then NewTopLine:=1;
|
|
end else
|
|
NewTopLine:=Caret.Y;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomCodeTool.GetLineInfo(ACleanPos: integer;
|
|
var ALineStart, ALineEnd, AFirstAtomStart, ALastAtomEnd: integer);
|
|
begin
|
|
if ACleanPos>=1 then begin
|
|
if ACleanPos<=SrcLen then begin
|
|
// search line start
|
|
ALineStart:=ACleanPos;
|
|
while (ALineStart>=1) and (not (Src[ALineStart] in [#10,#13])) do
|
|
dec(ALineStart);
|
|
inc(ALineStart);
|
|
// search line end
|
|
ALineEnd:=ACleanPos;
|
|
while (ALineEnd>=1) and (not (Src[ALineEnd] in [#10,#13])) do
|
|
inc(ALineEnd);
|
|
// search first atom in line
|
|
CurPos.StartPos:=ALineStart;
|
|
CurPos.EndPos:=ALineStart;
|
|
ReadNextAtom;
|
|
AFirstAtomStart:=CurPos.StartPos;
|
|
// search last atom in line
|
|
repeat
|
|
ALastAtomEnd:=CurPos.EndPos;
|
|
ReadNextAtom;
|
|
until CurPos.EndPos>ALineEnd;
|
|
end else begin
|
|
ALineStart:=Srclen+1;
|
|
ALineEnd:=Srclen+1;
|
|
AFirstAtomStart:=Srclen+1;
|
|
ALastAtomEnd:=Srclen+1;
|
|
end;
|
|
end else begin
|
|
ALineStart:=1;
|
|
ALineEnd:=1;
|
|
AFirstAtomStart:=1;
|
|
ALastAtomEnd:=1;
|
|
end;
|
|
end;
|
|
|
|
function TCustomCodeTool.UpdateNeeded(OnlyInterfaceNeeded: boolean): boolean;
|
|
begin
|
|
{$IFDEF CTDEBUG}
|
|
writeln('TCustomCodeTool.UpdateNeeded A ',Scanner<>nil);
|
|
{$ENDIF}
|
|
if FForceUpdateNeeded then begin
|
|
Result:=true;
|
|
exit;
|
|
end;
|
|
Result:=(FLastScannerChangeStep<>Scanner.ChangeStep)
|
|
or (Scanner.UpdateNeeded(OnlyInterfaceNeeded, CheckFilesOnDisk));
|
|
FForceUpdateNeeded:=Result;
|
|
{$IFDEF CTDEBUG}
|
|
writeln('TCustomCodeTool.UpdateNeeded END');
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TCustomCodeTool.CompareSrcIdentifier(CleanStartPos: integer;
|
|
const Identifier: string): boolean;
|
|
var IdentPos, Len: integer;
|
|
begin
|
|
Result:=false;
|
|
Len:=length(Identifier);
|
|
if (CleanStartPos<1) or (CleanStartPos>SrcLen-Len+1) or (Identifier='') then
|
|
exit;
|
|
IdentPos:=1;
|
|
while (IdentPos<=Len) and (IsIdentChar[Src[CleanStartPos]]) do begin
|
|
if UpChars[Identifier[IdentPos]]<>UpperSrc[CleanStartPos] then
|
|
exit;
|
|
inc(IdentPos);
|
|
inc(CleanStartPos);
|
|
end;
|
|
Result:=(IdentPos>Len)
|
|
and ((CleanStartPos>Srclen) or (not IsIdentChar[Src[CleanStartPos]]));
|
|
end;
|
|
|
|
|
|
end.
|