mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-07 16:52:40 +02:00
717 lines
22 KiB
ObjectPascal
717 lines
22 KiB
ObjectPascal
{
|
|
/***************************************************************************
|
|
LazDoc.pas
|
|
----------
|
|
|
|
***************************************************************************/
|
|
|
|
***************************************************************************
|
|
* *
|
|
* 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. *
|
|
* *
|
|
***************************************************************************
|
|
}
|
|
unit LazDoc;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, LCLProc, FileUtil,
|
|
CodeAtom, CodeTree, CodeToolManager, FindDeclarationTool, CodeCache, CacheCodeTools,
|
|
FileProcs, AvgLvlTree,
|
|
Laz_DOM, Laz_XMLRead, Laz_XMLWrite,
|
|
MacroIntf, PackageIntf, LazHelpIntf, ProjectIntf, LazIDEIntf,
|
|
CompilerOptions, IDEProcs, PackageDefs, EnvironmentOpts;
|
|
|
|
type
|
|
{ TLazFPDocFile }
|
|
|
|
TLazFPDocFile = class
|
|
public
|
|
Filename: string;
|
|
Doc: TXMLdocument;
|
|
ChangeStep: integer;// the CodeBuffer.ChangeStep value, when Doc was build
|
|
CodeBuffer: TCodeBuffer;
|
|
destructor Destroy; override;
|
|
function GetModuleNode: TDOMNode;
|
|
function GetFirstElement: TDOMNode;
|
|
function GetElementWithName(const ElementName: string): TDOMNode;
|
|
end;
|
|
|
|
{ TLDSourceToFPDocFile - cache item for source to FPDoc file mapping }
|
|
|
|
TLDSourceToFPDocFile = class
|
|
public
|
|
SourceFilename: string;
|
|
FPDocFilename: string;
|
|
FPDocFilenameTimeStamp: integer;
|
|
end;
|
|
|
|
{ TLazDocElement }
|
|
|
|
TLazDocElement = class
|
|
public
|
|
CodeContext: TFindContext;
|
|
ElementName: string;
|
|
ElementNode: TDOMNode;
|
|
FPDocFile: TLazFPDocFile;
|
|
end;
|
|
|
|
{ TLazDocElementChain }
|
|
|
|
TLazDocElementChain = class
|
|
private
|
|
FItems: TFPList; // list of TLazDocElement
|
|
function GetCount: integer;
|
|
function GetItems(Index: integer): TLazDocElement;
|
|
function Add: TLazDocElement;
|
|
public
|
|
CodePos: TCodePosition;
|
|
IDEChangeStep: integer;
|
|
CodetoolsChangeStep: integer;
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
property Items[Index: integer]: TLazDocElement read GetItems; default;
|
|
property Count: integer read GetCount;
|
|
end;
|
|
|
|
TLazDocChangeEvent =
|
|
procedure(Sender: TObject; LazDocFPFile: TLazFPDocFile) of object;
|
|
|
|
TLazDocManagerHandler = (
|
|
ldmhDocChanging,
|
|
ldmhDocChanged
|
|
);
|
|
|
|
TLazDocParseResult = (
|
|
ldprParsing, // means: done a small step, but not yet finished the job
|
|
ldprFailed,
|
|
ldprSuccess
|
|
);
|
|
|
|
{ TLazDocManager }
|
|
|
|
TLazDocManager = class
|
|
private
|
|
FDocs: TAvgLvlTree;// tree of loaded TLazFPDocFile
|
|
FHandlers: array[TLazDocManagerHandler] of TMethodList;
|
|
FSrcToDocMap: TAvgLvlTree; // tree of TLDSourceToFPDocFile sorted for SourceFilename
|
|
FDeclarationCache: TDeclarationInheritanceCache;
|
|
procedure AddHandler(HandlerType: TLazDocManagerHandler;
|
|
const AMethod: TMethod; AsLast: boolean = false);
|
|
procedure RemoveHandler(HandlerType: TLazDocManagerHandler;
|
|
const AMethod: TMethod);
|
|
procedure CallDocChangeEvents(HandlerType: TLazDocManagerHandler;
|
|
Doc: TLazFPDocFile);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure FreeDocs;
|
|
procedure ClearSrcToDocMap;
|
|
|
|
function FindFPDocFile(const Filename: string): TLazFPDocFile;
|
|
function LoadFPDocFile(const Filename: string;
|
|
UpdateFromDisk, Revert: Boolean;
|
|
out ADocFile: TLazFPDocFile;
|
|
out CacheWasUsed: boolean): Boolean;
|
|
function GetFPDocFilenameForHelpContext(
|
|
Context: TPascalHelpContextList;
|
|
out CacheWasUsed: boolean): string;
|
|
function GetFPDocFilenameForSource(SrcFilename: string;
|
|
ResolveIncludeFiles: Boolean;
|
|
out CacheWasUsed: boolean): string;
|
|
function CodeNodeToElementName(Tool: TFindDeclarationTool;
|
|
CodeNode: TCodeTreeNode): string;
|
|
function GetFPDocNode(Tool: TCodeTool; CodeNode: TCodeTreeNode; Complete: boolean;
|
|
out FPDocFile: TLazFPDocFile; out DOMNode: TDOMNode;
|
|
out CacheWasUsed: boolean): TLazDocParseResult;
|
|
function GetDeclarationChain(Code: TCodeBuffer; X, Y: integer;
|
|
out ListOfPFindContext: TFPList;
|
|
out CacheWasUsed: boolean): TLazDocParseResult;
|
|
function GetElementChain(Code: TCodeBuffer; X, Y: integer; Complete: boolean;
|
|
out Chain: TLazDocElementChain;
|
|
out CacheWasUsed: boolean): TLazDocParseResult;
|
|
public
|
|
// Event lists
|
|
procedure RemoveAllHandlersOfObject(AnObject: TObject);
|
|
procedure AddHandlerOnChanging(const OnDocChangingEvent: TLazDocChangeEvent;
|
|
AsLast: boolean = false);
|
|
procedure RemoveHandlerOnChanging(const OnDocChangingEvent: TLazDocChangeEvent);
|
|
procedure AddHandlerOnChanged(const OnDocChangedEvent: TLazDocChangeEvent;
|
|
AsLast: boolean = false);
|
|
procedure RemoveHandlerOnChanged(const OnDocChangedEvent: TLazDocChangeEvent);
|
|
end;
|
|
|
|
var
|
|
LazDocBoss: TLazDocManager = nil;// set by the IDE
|
|
|
|
function CompareLazFPDocFilenames(Data1, Data2: Pointer): integer;
|
|
function CompareAnsistringWithLazFPDocFile(Key, Data: Pointer): integer;
|
|
function CompareLDSrc2DocSrcFilenames(Data1, Data2: Pointer): integer;
|
|
function CompareAnsistringWithLDSrc2DocSrcFile(Key, Data: Pointer): integer;
|
|
|
|
|
|
implementation
|
|
|
|
|
|
function CompareLazFPDocFilenames(Data1, Data2: Pointer): integer;
|
|
begin
|
|
Result:=CompareFilenames(TLazFPDocFile(Data1).Filename,
|
|
TLazFPDocFile(Data2).Filename);
|
|
end;
|
|
|
|
function CompareAnsistringWithLazFPDocFile(Key, Data: Pointer): integer;
|
|
begin
|
|
Result:=CompareFilenames(AnsiString(Key),TLazFPDocFile(Data).Filename);
|
|
end;
|
|
|
|
function CompareLDSrc2DocSrcFilenames(Data1, Data2: Pointer): integer;
|
|
begin
|
|
Result:=CompareFilenames(TLDSourceToFPDocFile(Data1).SourceFilename,
|
|
TLDSourceToFPDocFile(Data2).SourceFilename);
|
|
end;
|
|
|
|
function CompareAnsistringWithLDSrc2DocSrcFile(Key, Data: Pointer): integer;
|
|
begin
|
|
Result:=CompareFilenames(AnsiString(Key),TLDSourceToFPDocFile(Data).SourceFilename);
|
|
end;
|
|
|
|
{ TLazFPDocFile }
|
|
|
|
destructor TLazFPDocFile.Destroy;
|
|
begin
|
|
FreeAndNil(Doc);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TLazFPDocFile.GetModuleNode: TDOMNode;
|
|
begin
|
|
Result:=nil;
|
|
if Doc=nil then exit;
|
|
|
|
// get first node
|
|
Result := Doc.FindNode('fpdoc-descriptions');
|
|
if Result=nil then exit;
|
|
|
|
// proceed to package
|
|
Result := Result.FirstChild;
|
|
if Result=nil then exit;
|
|
|
|
// proceed to module
|
|
Result := Result.FirstChild;
|
|
while (Result<>nil) and (Result.NodeName <> 'module') do
|
|
Result := Result.NextSibling;
|
|
end;
|
|
|
|
function TLazFPDocFile.GetFirstElement: TDOMNode;
|
|
begin
|
|
//get first module node
|
|
Result := GetModuleNode;
|
|
if Result=nil then exit;
|
|
|
|
//proceed to element
|
|
Result := Result.FirstChild;
|
|
while Result.NodeName <> 'element' do
|
|
Result := Result.NextSibling;
|
|
end;
|
|
|
|
function TLazFPDocFile.GetElementWithName(const ElementName: string): TDOMNode;
|
|
begin
|
|
Result:=GetFirstElement;
|
|
while Result<>nil do begin
|
|
if (Result is TDomElement)
|
|
and (CompareText(TDomElement(Result).GetAttribute('name'),ElementName)=0)
|
|
then
|
|
exit;
|
|
Result:=Result.NextSibling;
|
|
end;
|
|
end;
|
|
|
|
procedure TLazDocManager.AddHandler(HandlerType: TLazDocManagerHandler;
|
|
const AMethod: TMethod; AsLast: boolean);
|
|
begin
|
|
if FHandlers[HandlerType]=nil then
|
|
FHandlers[HandlerType]:=TMethodList.Create;
|
|
FHandlers[HandlerType].Add(AMethod);
|
|
end;
|
|
|
|
procedure TLazDocManager.RemoveHandler(HandlerType: TLazDocManagerHandler;
|
|
const AMethod: TMethod);
|
|
begin
|
|
FHandlers[HandlerType].Remove(AMethod);
|
|
end;
|
|
|
|
procedure TLazDocManager.CallDocChangeEvents(HandlerType: TLazDocManagerHandler;
|
|
Doc: TLazFPDocFile);
|
|
var
|
|
i: LongInt;
|
|
begin
|
|
i:=FHandlers[HandlerType].Count;
|
|
while FHandlers[HandlerType].NextDownIndex(i) do
|
|
TLazDocChangeEvent(FHandlers[HandlerType].Items[i])(Self,Doc);
|
|
end;
|
|
|
|
constructor TLazDocManager.Create;
|
|
begin
|
|
FDocs:=TAvgLvlTree.Create(@CompareLazFPDocFilenames);
|
|
FSrcToDocMap:=TAvgLvlTree.Create(@CompareLDSrc2DocSrcFilenames);
|
|
FDeclarationCache:=TDeclarationInheritanceCache.Create(
|
|
@CodeToolBoss.FindDeclarationNodeAndOverload,
|
|
@CodeToolBoss.GetCodeTreeNodesDeletedStep);
|
|
end;
|
|
|
|
destructor TLazDocManager.Destroy;
|
|
begin
|
|
ClearSrcToDocMap;
|
|
FreeDocs;
|
|
FreeAndNil(FDocs);
|
|
FreeAndNil(FSrcToDocMap);
|
|
FreeAndNil(FDeclarationCache);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TLazDocManager.FindFPDocFile(const Filename: string): TLazFPDocFile;
|
|
var
|
|
Node: TAvgLvlTreeNode;
|
|
begin
|
|
Node:=FDocs.FindKey(Pointer(Filename),@CompareAnsistringWithLazFPDocFile);
|
|
if Node<>nil then
|
|
Result:=TLazFPDocFile(Node.Data)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TLazDocManager.LoadFPDocFile(const Filename: string; UpdateFromDisk,
|
|
Revert: Boolean; out ADocFile: TLazFPDocFile; out CacheWasUsed: boolean): Boolean;
|
|
var
|
|
MemStream: TMemoryStream;
|
|
begin
|
|
Result:=false;
|
|
CacheWasUsed:=true;
|
|
ADocFile:=FindFPDocFile(Filename);
|
|
if ADocFile=nil then begin
|
|
ADocFile:=TLazFPDocFile.Create;
|
|
ADocFile.Filename:=Filename;
|
|
FDocs.Add(ADocFile);
|
|
end;
|
|
ADocFile.CodeBuffer:=CodeToolBoss.LoadFile(Filename,UpdateFromDisk,Revert);
|
|
if ADocFile.CodeBuffer=nil then begin
|
|
DebugLn(['TLazDocForm.LoadFPDocFile unable to load "',Filename,'"']);
|
|
FreeAndNil(ADocFile.Doc);
|
|
exit;
|
|
end;
|
|
if (ADocFile.Doc<>nil)
|
|
and (ADocFile.ChangeStep=ADocFile.CodeBuffer.ChangeStep)
|
|
then begin
|
|
// no update needed
|
|
exit(true);
|
|
end;
|
|
CacheWasUsed:=false;
|
|
|
|
DebugLn(['TLazDocManager.LoadFPDocFile parsing ',ADocFile.Filename]);
|
|
CallDocChangeEvents(ldmhDocChanging,ADocFile);
|
|
|
|
// parse XML
|
|
ADocFile.ChangeStep:=ADocFile.CodeBuffer.ChangeStep;
|
|
FreeAndNil(ADocFile.Doc);
|
|
|
|
MemStream:=TMemoryStream.Create;
|
|
try
|
|
ADocFile.CodeBuffer.SaveToStream(MemStream);
|
|
MemStream.Position:=0;
|
|
Result:=false;
|
|
ReadXMLFile(ADocFile.Doc, MemStream);
|
|
Result:=true;
|
|
finally
|
|
if not Result then
|
|
FreeAndNil(ADocFile.Doc);
|
|
MemStream.Free;
|
|
CallDocChangeEvents(ldmhDocChanging,ADocFile);
|
|
end;
|
|
end;
|
|
|
|
function TLazDocManager.GetFPDocFilenameForHelpContext(
|
|
Context: TPascalHelpContextList; out CacheWasUsed: boolean): string;
|
|
var
|
|
i: Integer;
|
|
SrcFilename: String;
|
|
begin
|
|
Result:='';
|
|
CacheWasUsed:=true;
|
|
if Context=nil then exit;
|
|
for i:=0 to Context.Count-1 do begin
|
|
if Context.Items[i].Descriptor<>pihcFilename then continue;
|
|
SrcFilename:=Context.Items[i].Context;
|
|
Result:=GetFPDocFilenameForSource(SrcFilename,true,CacheWasUsed);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
function TLazDocManager.GetFPDocFilenameForSource(SrcFilename: string;
|
|
ResolveIncludeFiles: Boolean; out CacheWasUsed: boolean): string;
|
|
var
|
|
FPDocName: String;
|
|
SearchPath: String;
|
|
|
|
procedure AddSearchPath(Paths: string; const BaseDir: string);
|
|
begin
|
|
if Paths='' then exit;
|
|
if not IDEMacros.CreateAbsoluteSearchPath(Paths,BaseDir) then exit;
|
|
if Paths='' then exit;
|
|
SearchPath:=SearchPath+';'+Paths;
|
|
end;
|
|
|
|
procedure CheckUnitOwners(CheckSourceDirectories: boolean);
|
|
var
|
|
PkgList: TFPList;
|
|
i: Integer;
|
|
APackage: TLazPackage;
|
|
BaseDir: String;
|
|
AProject: TLazProject;
|
|
begin
|
|
if not FilenameIsAbsolute(SrcFilename) then exit;
|
|
|
|
if CheckSourceDirectories then begin
|
|
PkgList:=PackageEditingInterface.GetOwnersOfUnit(SrcFilename);
|
|
end else begin
|
|
PkgList:=PackageEditingInterface.GetPossibleOwnersOfUnit(SrcFilename,[]);
|
|
end;
|
|
// get all packages owning the file
|
|
if PkgList=nil then exit;
|
|
try
|
|
for i:=0 to PkgList.Count-1 do begin
|
|
if TObject(PkgList[i]) is TLazProject then begin
|
|
AProject:=TLazProject(PkgList[i]);
|
|
if AProject.LazDocPaths='' then continue;
|
|
BaseDir:=ExtractFilePath(AProject.ProjectInfoFile);
|
|
if BaseDir='' then continue;
|
|
// add lazdoc paths of project
|
|
AddSearchPath(AProject.LazDocPaths,BaseDir);
|
|
end else if TObject(PkgList[i]) is TLazPackage then begin
|
|
APackage:=TLazPackage(PkgList[i]);
|
|
if APackage.LazDocPaths='' then continue;
|
|
BaseDir:=APackage.Directory;
|
|
if BaseDir='' then continue;
|
|
// add lazdoc paths of package
|
|
AddSearchPath(APackage.LazDocPaths,BaseDir);
|
|
end;
|
|
end;
|
|
finally
|
|
PkgList.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure CheckIfInLazarus;
|
|
var
|
|
LazDir: String;
|
|
begin
|
|
if not FilenameIsAbsolute(SrcFilename) then exit;
|
|
LazDir:=AppendPathDelim(EnvironmentOptions.LazarusDirectory);
|
|
// check LCL
|
|
if FileIsInPath(SrcFilename,LazDir+'lcl') then begin
|
|
AddSearchPath(SetDirSeparators('docs/xml/lcl'),LazDir);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
CodeBuf: TCodeBuffer;
|
|
AVLNode: TAvgLvlTreeNode;
|
|
MapEntry: TLDSourceToFPDocFile;
|
|
begin
|
|
Result:='';
|
|
CacheWasUsed:=true;
|
|
|
|
if ResolveIncludeFiles then begin
|
|
CodeBuf:=CodeToolBoss.FindFile(SrcFilename);
|
|
if CodeBuf<>nil then begin
|
|
CodeBuf:=CodeToolBoss.GetMainCode(CodeBuf);
|
|
if CodeBuf<>nil then begin
|
|
SrcFilename:=CodeBuf.Filename;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if not FilenameIsPascalSource(SrcFilename) then exit;
|
|
|
|
// first try cache
|
|
MapEntry:=nil;
|
|
AVLNode:=FSrcToDocMap.FindKey(Pointer(SrcFilename),@CompareAnsistringWithLDSrc2DocSrcFile);
|
|
if AVLNode<>nil then begin
|
|
MapEntry:=TLDSourceToFPDocFile(AVLNode.Data);
|
|
if MapEntry.FPDocFilenameTimeStamp=CompilerParseStamp then begin
|
|
Result:=MapEntry.FPDocFilename;
|
|
exit;
|
|
end;
|
|
end;
|
|
CacheWasUsed:=false;
|
|
|
|
DebugLn(['TLazDocManager.GetFPDocFilenameForSource searching SrcFilename=',SrcFilename]);
|
|
|
|
// first check if the file is owned by any project/package
|
|
SearchPath:='';
|
|
CheckUnitOwners(false);
|
|
CheckUnitOwners(true);
|
|
CheckIfInLazarus;
|
|
|
|
// finally add the default paths
|
|
AddSearchPath(EnvironmentOptions.LazDocPaths,'');
|
|
FPDocName:=lowercase(ExtractFileNameOnly(SrcFilename))+'.xml';
|
|
DebugLn(['TLazDocManager.GetFPDocFilenameForSource Search ',FPDocName,' in "',SearchPath,'"']);
|
|
Result:=SearchFileInPath(FPDocName,'',SearchPath,';',ctsfcAllCase);
|
|
|
|
// save to cache
|
|
if MapEntry=nil then begin
|
|
MapEntry:=TLDSourceToFPDocFile.Create;
|
|
MapEntry.SourceFilename:=SrcFilename;
|
|
FSrcToDocMap.Add(MapEntry);
|
|
end;
|
|
MapEntry.FPDocFilename:=Result;
|
|
MapEntry.FPDocFilenameTimeStamp:=CompilerParseStamp;
|
|
end;
|
|
|
|
function TLazDocManager.CodeNodeToElementName(Tool: TFindDeclarationTool;
|
|
CodeNode: TCodeTreeNode): string;
|
|
var
|
|
NodeName: String;
|
|
begin
|
|
Result:='';
|
|
while CodeNode<>nil do begin
|
|
case CodeNode.Desc of
|
|
ctnVarDefinition, ctnConstDefinition, ctnTypeDefinition, ctnGenericType:
|
|
NodeName:=Tool.ExtractDefinitionName(CodeNode);
|
|
ctnProperty:
|
|
NodeName:=Tool.ExtractPropName(CodeNode,false);
|
|
ctnProcedure:
|
|
NodeName:=Tool.ExtractProcName(CodeNode,[]);
|
|
else NodeName:='';
|
|
end;
|
|
if NodeName<>'' then begin
|
|
if Result<>'' then
|
|
Result:='.'+Result;
|
|
Result:=NodeName+Result;
|
|
end;
|
|
CodeNode:=CodeNode.Parent;
|
|
end;
|
|
end;
|
|
|
|
function TLazDocManager.GetFPDocNode(Tool: TCodeTool; CodeNode: TCodeTreeNode;
|
|
Complete: boolean; out FPDocFile: TLazFPDocFile; out DOMNode: TDOMNode;
|
|
out CacheWasUsed: boolean): TLazDocParseResult;
|
|
var
|
|
SrcFilename: String;
|
|
FPDocFilename: String;
|
|
ElementName: String;
|
|
begin
|
|
FPDocFile:=nil;
|
|
DOMNode:=nil;
|
|
CacheWasUsed:=true;
|
|
|
|
// find corresponding FPDoc file
|
|
SrcFilename:=Tool.MainFilename;
|
|
FPDocFilename:=GetFPDocFilenameForSource(SrcFilename,false,CacheWasUsed);
|
|
if FPDocFilename='' then exit(ldprFailed);
|
|
if (not CacheWasUsed) and (not Complete) then exit(ldprParsing);
|
|
|
|
// load FPDoc file
|
|
if not LoadFPDocFile(FPDocFilename,true,false,FPDocFile,CacheWasUsed) then
|
|
exit(ldprFailed);
|
|
if (not CacheWasUsed) and (not Complete) then exit(ldprParsing);
|
|
|
|
// find FPDoc node
|
|
ElementName:=CodeNodeToElementName(Tool,CodeNode);
|
|
if ElementName='' then exit(ldprFailed);
|
|
DOMNode:=FPDocFile.GetElementWithName(ElementName);
|
|
if DOMNode=nil then exit(ldprFailed);
|
|
|
|
Result:=ldprSuccess;
|
|
end;
|
|
|
|
function TLazDocManager.GetDeclarationChain(Code: TCodeBuffer; X, Y: integer;
|
|
out ListOfPFindContext: TFPList; out CacheWasUsed: boolean
|
|
): TLazDocParseResult;
|
|
begin
|
|
if FDeclarationCache.FindDeclarations(Code,X,Y,ListOfPFindContext,
|
|
CacheWasUsed)
|
|
then
|
|
Result:=ldprSuccess
|
|
else
|
|
Result:=ldprFailed;
|
|
end;
|
|
|
|
function TLazDocManager.GetElementChain(Code: TCodeBuffer; X, Y: integer;
|
|
Complete: boolean; out Chain: TLazDocElementChain; out CacheWasUsed: boolean
|
|
): TLazDocParseResult;
|
|
var
|
|
ListOfPFindContext: TFPList;
|
|
i: Integer;
|
|
CodeContext: PFindContext;
|
|
LDElement: TLazDocElement;
|
|
SrcFilename: String;
|
|
FPDocFilename: String;
|
|
begin
|
|
Chain:=nil;
|
|
ListOfPFindContext:=nil;
|
|
try
|
|
DebugLn(['TLazDocManager.GetElementChain GetDeclarationChain...']);
|
|
// get the declaration chain
|
|
Result:=GetDeclarationChain(Code,X,Y,ListOfPFindContext,CacheWasUsed);
|
|
if Result<>ldprSuccess then exit;
|
|
if (not CacheWasUsed) and (not Complete) then exit(ldprParsing);
|
|
|
|
DebugLn(['TLazDocManager.GetElementChain init the element chain: ListOfPFindContext.Count=',ListOfPFindContext.Count,' ...']);
|
|
// init the element chain
|
|
Result:=ldprParsing;
|
|
Chain:=TLazDocElementChain.Create;
|
|
Chain.CodePos.Code:=Code;
|
|
Code.LineColToPosition(Y,X,Chain.CodePos.P);
|
|
// fill the element chain
|
|
for i:=0 to ListOfPFindContext.Count-1 do begin
|
|
LDElement:=Chain.Add;
|
|
// get source position of declaration
|
|
CodeContext:=PFindContext(ListOfPFindContext[i]);
|
|
LDElement.CodeContext:=CodeContext^;
|
|
DebugLn(['TLazDocManager.GetElementChain i=',i,' CodeContext=',FindContextToString(LDElement.CodeContext)]);
|
|
|
|
// find corresponding FPDoc file
|
|
SrcFilename:=LDElement.CodeContext.Tool.MainFilename;
|
|
FPDocFilename:=GetFPDocFilenameForSource(SrcFilename,false,CacheWasUsed);
|
|
DebugLn(['TLazDocManager.GetElementChain FPDocFilename=',FPDocFilename]);
|
|
if (not CacheWasUsed) and (not Complete) then exit(ldprParsing);
|
|
|
|
if FPDocFilename<>'' then begin
|
|
// load FPDoc file
|
|
LoadFPDocFile(FPDocFilename,true,false,LDElement.FPDocFile,
|
|
CacheWasUsed);
|
|
if (not CacheWasUsed) and (not Complete) then exit(ldprParsing);
|
|
end;
|
|
end;
|
|
|
|
// get fpdoc nodes
|
|
for i:=0 to Chain.Count-1 do begin
|
|
LDElement:=Chain[i];
|
|
// get fpdoc element path
|
|
LDElement.ElementName:=CodeNodeToElementName(LDElement.CodeContext.Tool,
|
|
LDElement.CodeContext.Node);
|
|
DebugLn(['TLazDocManager.GetElementChain i=',i,' Element=',LDElement.ElementName]);
|
|
// get fpdoc node
|
|
if (LDElement.FPDocFile<>nil) and (LDElement.ElementName<>'') then begin
|
|
LDElement.ElementNode:=
|
|
LDElement.FPDocFile.GetElementWithName(LDElement.ElementName);
|
|
end;
|
|
DebugLn(['TLazDocManager.GetElementChain ElementNode=',LDElement.ElementNode<>nil]);
|
|
end;
|
|
|
|
Result:=ldprSuccess;
|
|
finally
|
|
FreeListOfPFindContext(ListOfPFindContext);
|
|
if Result<>ldprSuccess then
|
|
FreeAndNil(Chain);
|
|
end;
|
|
end;
|
|
|
|
procedure TLazDocManager.FreeDocs;
|
|
var
|
|
AVLNode: TAvgLvlTreeNode;
|
|
begin
|
|
AVLNode:=FDocs.FindLowest;
|
|
while AVLNode<>nil do begin
|
|
CallDocChangeEvents(ldmhDocChanging,TLazFPDocFile(AVLNode.Data));
|
|
AVLNode:=FDocs.FindSuccessor(AVLNode);
|
|
end;
|
|
FDocs.FreeAndClear;
|
|
end;
|
|
|
|
procedure TLazDocManager.ClearSrcToDocMap;
|
|
begin
|
|
FSrcToDocMap.FreeAndClear;
|
|
end;
|
|
|
|
procedure TLazDocManager.RemoveAllHandlersOfObject(AnObject: TObject);
|
|
var
|
|
HandlerType: TLazDocManagerHandler;
|
|
begin
|
|
for HandlerType:=Low(TLazDocManagerHandler) to High(TLazDocManagerHandler) do
|
|
FHandlers[HandlerType].RemoveAllMethodsOfObject(AnObject);
|
|
end;
|
|
|
|
procedure TLazDocManager.AddHandlerOnChanging(
|
|
const OnDocChangingEvent: TLazDocChangeEvent; AsLast: boolean);
|
|
begin
|
|
AddHandler(ldmhDocChanging,TMethod(OnDocChangingEvent),AsLast);
|
|
end;
|
|
|
|
procedure TLazDocManager.RemoveHandlerOnChanging(
|
|
const OnDocChangingEvent: TLazDocChangeEvent);
|
|
begin
|
|
RemoveHandler(ldmhDocChanging,TMethod(OnDocChangingEvent));
|
|
end;
|
|
|
|
procedure TLazDocManager.AddHandlerOnChanged(
|
|
const OnDocChangedEvent: TLazDocChangeEvent; AsLast: boolean);
|
|
begin
|
|
AddHandler(ldmhDocChanged,TMethod(OnDocChangedEvent),AsLast);
|
|
end;
|
|
|
|
procedure TLazDocManager.RemoveHandlerOnChanged(
|
|
const OnDocChangedEvent: TLazDocChangeEvent);
|
|
begin
|
|
RemoveHandler(ldmhDocChanged,TMethod(OnDocChangedEvent));
|
|
end;
|
|
|
|
|
|
{ TLazDocElementChain }
|
|
|
|
function TLazDocElementChain.GetCount: integer;
|
|
begin
|
|
Result:=FItems.Count;
|
|
end;
|
|
|
|
function TLazDocElementChain.GetItems(Index: integer): TLazDocElement;
|
|
begin
|
|
Result:=TLazDocElement(FItems[Index]);
|
|
end;
|
|
|
|
function TLazDocElementChain.Add: TLazDocElement;
|
|
begin
|
|
Result:=TLazDocElement.Create;
|
|
FItems.Add(Result);
|
|
end;
|
|
|
|
constructor TLazDocElementChain.Create;
|
|
begin
|
|
FItems:=TFPList.Create;
|
|
end;
|
|
|
|
destructor TLazDocElementChain.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TLazDocElementChain.Clear;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i:=0 to FItems.Count-1 do TObject(FItems[i]).Free;
|
|
FItems.Clear;
|
|
end;
|
|
|
|
end.
|
|
|