IDE: lazdoc editor: implemented creating new elements

git-svn-id: trunk@13336 -
This commit is contained in:
mattias 2007-12-15 10:47:00 +00:00
parent 2611c3b386
commit 3545742d9e
5 changed files with 537 additions and 131 deletions

View File

@ -134,7 +134,7 @@
<Unit14>
<Filename Value="lazdemsg.pp"/>
<IsPartOfProject Value="True"/>
<UnitName Value="lazdemsg"/>
<UnitName Value="LazDEMsg"/>
</Unit14>
<Unit15>
<Filename Value="lazdeopts.pp"/>

View File

@ -28,6 +28,8 @@ unit LazDoc;
{$mode objfpc}{$H+}
{ $define VerboseLazDoc}
interface
uses
@ -37,7 +39,7 @@ uses
CodeCache, CacheCodeTools, FileProcs,
Laz_DOM, Laz_XMLRead, Laz_XMLWrite,
// IDEIntf
MacroIntf, PackageIntf, LazHelpIntf, ProjectIntf, LazIDEIntf,
MacroIntf, PackageIntf, LazHelpIntf, ProjectIntf, IDEDialogs, LazIDEIntf,
// IDE
CompilerOptions, IDEProcs, PackageDefs, EnvironmentOpts, DialogProcs;
@ -84,7 +86,8 @@ type
destructor Destroy; override;
function GetModuleNode: TDOMNode;
function GetFirstElement: TDOMNode;
function GetElementWithName(const ElementName: string): TDOMNode;
function GetElementWithName(const ElementName: string;
CreateIfNotExists: boolean = false): TDOMNode;
function GetChildValuesAsString(Node: TDOMNode): String;
function GetValuesFromNode(Node: TDOMNode): TFPDocElementValues;
function GetValueFromNode(Node: TDOMNode; Item: TFPDocItem): string;
@ -102,6 +105,7 @@ type
SourceFilename: string;
FPDocFilename: string;
FPDocFilenameTimeStamp: integer;
FilesTimeStamp: integer;
end;
{ TLazDocElement }
@ -166,6 +170,9 @@ type
const AMethod: TMethod);
procedure CallDocChangeEvents(HandlerType: TLazDocManagerHandler;
Doc: TLazFPDocFile);
function DoCreateFPDocFileForSource(const SrcFilename: string): string;
function CreateFPDocFile(const ExpandedFilename, PackageName,
ModuleName: string): TCodeBuffer;
public
constructor Create;
destructor Destroy; override;
@ -183,7 +190,8 @@ type
out CacheWasUsed: boolean): string;
function GetFPDocFilenameForSource(SrcFilename: string;
ResolveIncludeFiles: Boolean;
out CacheWasUsed: boolean): string;
out CacheWasUsed: boolean;
CreateIfNotExists: boolean = false): string;
function CodeNodeToElementName(Tool: TFindDeclarationTool;
CodeNode: TCodeTreeNode): string;
function GetFPDocNode(Tool: TCodeTool; CodeNode: TCodeTreeNode; Complete: boolean;
@ -192,12 +200,18 @@ type
function GetDeclarationChain(Code: TCodeBuffer; X, Y: integer;
out ListOfPCodeXYPosition: TFPList;
out CacheWasUsed: boolean): TLazDocParseResult;
function GetCodeContext(CodePos: PCodeXYPosition;
out FindContext: TFindContext;
Complete: boolean;
out CacheWasUsed: boolean): TLazDocParseResult;
function GetElementChain(Code: TCodeBuffer; X, Y: integer; Complete: boolean;
out Chain: TLazDocElementChain;
out CacheWasUsed: boolean): TLazDocParseResult;
function GetHint(Code: TCodeBuffer; X, Y: integer; Complete: boolean;
out Hint: string;
out CacheWasUsed: boolean): TLazDocParseResult;
function CreateElement(Code: TCodeBuffer; X, Y: integer;
out Element: TLazDocElement): Boolean;
public
// Event lists
procedure RemoveAllHandlersOfObject(AnObject: TObject);
@ -308,11 +322,14 @@ begin
//proceed to element
Result := Result.FirstChild;
while Result.NodeName <> 'element' do
while (Result<>nil) and (Result.NodeName <> 'element') do
Result := Result.NextSibling;
end;
function TLazFPDocFile.GetElementWithName(const ElementName: string): TDOMNode;
function TLazFPDocFile.GetElementWithName(const ElementName: string;
CreateIfNotExists: boolean): TDOMNode;
var
ModuleNode: TDOMNode;
begin
Result:=GetFirstElement;
//DebugLn(['TLazFPDocFile.GetElementWithName ',ElementName,' GetFirstElement=',GetFirstElement<>nil]);
@ -325,6 +342,19 @@ begin
exit;
Result:=Result.NextSibling;
end;
if (Result=nil) and CreateIfNotExists then begin
DebugLn(['TLazFPDocFile.GetElementWithName creating ',ElementName]);
ModuleNode:=GetModuleNode;
if ModuleNode=nil then begin
DebugLn(['TLazFPDocFile.GetElementWithName create failed: missing module name. ElementName=',ElementName]);
exit;
end;
Result:=Doc.CreateElement('element');
DocChanging;
TDOMElement(Result).SetAttribute('name',ElementName);
ModuleNode.AppendChild(Result);
DocChanged;
end;
end;
function TLazFPDocFile.GetChildValuesAsString(Node: TDOMNode): String;
@ -335,8 +365,10 @@ begin
Child:=Node.FirstChild;
while Child<>nil do begin
//DebugLn(['TLazFPDocFile.GetChildValuesAsString ',dbgsName(Child)]);
if Child is TDOMText then
if Child is TDOMText then begin
//DebugLn(['TLazFPDocFile.GetChildValuesAsString Data="',TDOMText(Child).Data,'" Length=',TDOMText(Child).Length]);
Result:=Result+TDOMText(Child).Data;
end;
Child:=Child.NextSibling;
end;
end;
@ -379,6 +411,7 @@ var
begin
Result:='';
Child:=Node.FindNode(FPDocItemNames[Item]);
//DebugLn(['TLazFPDocFile.GetValueFromNode ',FPDocItemNames[Item],' Found=',Child<>nil]);
if Child<>nil then begin
if Item=fpdiExample then
Result := Child.Attributes.GetNamedItem('file').NodeValue
@ -391,6 +424,7 @@ procedure TLazFPDocFile.SetChildValue(Node: TDOMNode; const ChildName: string;
NewValue: string);
var
Child: TDOMNode;
TextNode: TDOMText;
begin
Child:=Node.FindNode(ChildName);
NewValue:=ToUnixLineEnding(NewValue);
@ -416,22 +450,21 @@ begin
if NewValue<>'' then begin
DebugLn(['TLazFPDocFile.SetChildValue Adding Name=',ChildName,' NewValue="',NewValue,'"']);
DocChanging;
Child := Doc.CreateTextNode(NewValue);
Child := Doc.CreateElement(ChildName);
Node.AppendChild(Child);
TextNode := Doc.CreateTextNode(NewValue);
Child.AppendChild(TextNode);
DocChanged;
end;
end else begin
end else if GetChildValuesAsString(Child)<>NewValue then begin
// change node
if Child.FirstChild=nil then begin
DebugLn(['TLazFPDocFile.SetChildValue FAILED ',Node.NodeName,' ChildName=',Child.NodeName,' Child.FirstChild=nil']);
exit;
end;
if Child.FirstChild.NodeValue <> NewValue then begin
DebugLn(['TLazDocForm.CheckAndWriteNode Changing ',Node.NodeName,' ChildName=',Child.NodeName,' OldValue=',Child.FirstChild.NodeValue,' NewValue="',NewValue,'"']);
DocChanging;
Child.FirstChild.NodeValue := NewValue;
DocChanged;
end;
DocChanging;
while Child.FirstChild<>nil do
Child.FirstChild.Free;
DebugLn(['TLazDocForm.CheckAndWriteNode Changing ',Node.NodeName,' ChildName=',Child.NodeName,' OldValue=',Child.FirstChild.NodeValue,' NewValue="',NewValue,'"']);
TextNode := Doc.CreateTextNode(NewValue);
Child.AppendChild(TextNode);
DocChanged;
end;
end;
@ -495,6 +528,194 @@ begin
TLazDocChangeEvent(FHandlers[HandlerType].Items[i])(Self,Doc);
end;
function TLazDocManager.DoCreateFPDocFileForSource(const SrcFilename: string
): string;
procedure CleanUpPkgList(var PkgList: TFPList);
var
i: Integer;
AProject: TLazProject;
BaseDir: String;
APackage: TLazPackage;
begin
if (PkgList=nil) then exit;
for i:=PkgList.Count-1 downto 0 do begin
if TObject(PkgList[i]) is TLazProject then begin
AProject:=TLazProject(PkgList[i]);
BaseDir:=ExtractFilePath(AProject.ProjectInfoFile);
if BaseDir<>'' then continue;
end else if TObject(PkgList[i]) is TLazPackage then begin
APackage:=TLazPackage(PkgList[i]);
BaseDir:=APackage.Directory;
if BaseDir<>'' then continue;
end;
// this owner can not be used
PkgList.Delete(i);
end;
if PkgList.Count=0 then
FreeAndNil(PkgList);
if PkgList.Count>1 then begin
// there are more than one possible owners
DebugLn(['TLazDocManager.DoCreateFPDocFileForSource.CleanUpPkgList Warning: overlapping projects/packages']);
end;
end;
function SelectNewLazDocPaths(const Title, BaseDir: string): string;
begin
Result:=LazSelectDirectory('Choose LazDoc directory for '+Title,BaseDir);
end;
var
PkgList: TFPList;
NewOwner: TObject;
AProject: TLazProject;
APackage: TLazPackage;
p: Integer;
LazDocPaths: String;
LazDocPackageName: String;
NewPath: String;
BaseDir: String;
Code: TCodeBuffer;
CurUnitName: String;
AVLNode: TAvgLvlTreeNode;
begin
Result:='';
DebugLn(['TLazDocManager.DoCreateFPDocFileForSource ',SrcFilename]);
if not FilenameIsAbsolute(SrcFilename) then begin
DebugLn(['TLazDocManager.DoCreateFPDocFileForSource failed, because file no absolute: ',SrcFilename]);
exit;
end;
PkgList:=nil;
try
// get all packages owning the file
PkgList:=PackageEditingInterface.GetOwnersOfUnit(SrcFilename);
CleanUpPkgList(PkgList);
if (PkgList=nil) then begin
PkgList:=PackageEditingInterface.GetOwnersOfUnit(SrcFilename);
CleanUpPkgList(PkgList);
end;
if PkgList=nil then begin
// no package/project found
MessageDlg('Package not found',
'The unit '+SrcFilename+' is not owned be any package or project.'#13
+'Please add the unit to a package or project.'#13
+'Unable to create the fpdoc file.',mtError,[mbCancel],0);
exit;
end;
NewOwner:=TObject(PkgList[0]);
if NewOwner is TLazProject then begin
AProject:=TLazProject(NewOwner);
BaseDir:=ExtractFilePath(AProject.ProjectInfoFile);
if AProject.LazDocPaths='' then
AProject.LazDocPaths:=SelectNewLazDocPaths(AProject.ShortDescription,BaseDir);
LazDocPaths:=AProject.LazDocPaths;
LazDocPackageName:=ExtractFileNameOnly(AProject.ProjectInfoFile);
end else if NewOwner is TLazPackage then begin
APackage:=TLazPackage(NewOwner);
BaseDir:=APackage.Directory;
if APackage.LazDocPaths='' then
APackage.LazDocPaths:=SelectNewLazDocPaths(APackage.Name,BaseDir);
LazDocPaths:=APackage.LazDocPaths;
LazDocPackageName:=APackage.Name;
end else begin
DebugLn(['TLazDocManager.DoCreateFPDocFileForSource unknown owner type ',dbgsName(NewOwner)]);
exit;
end;
p:=1;
repeat
NewPath:=GetNextDirectoryInSearchPath(LazDocPaths,p);
if not FilenameIsAbsolute(NewPath) then
NewPath:=AppendPathDelim(BaseDir)+NewPath;
if DirPathExistsCached(NewPath) then begin
// fpdoc directory found
Result:=AppendPathDelim(NewPath)+lowercase(ExtractFileNameOnly(SrcFilename))+'.xml';
Code:=CodeToolBoss.LoadFile(SrcFilename,true,false);
// get unitname
CurUnitName:=ExtractFileNameOnly(SrcFilename);
if Code<>nil then
CurUnitName:=CodeToolBoss.GetSourceName(Code,false);
// remove cache (source to fpdoc filename)
AVLNode:=FSrcToDocMap.FindKey(Pointer(SrcFilename),
@CompareAnsistringWithLDSrc2DocSrcFile);
if AVLNode<>nil then
FSrcToDocMap.FreeAndDelete(AVLNode);
// create fpdoc file
if CreateFPDocFile(Result,LazDocPackageName,CurUnitName)=nil then
Result:='';
exit;
end;
until false;
// no valid directory found
DebugLn(['TLazDocManager.DoCreateFPDocFileForSource LazDocModul="',LazDocPackageName,'" LazDocPaths="',LazDocPaths,'" ']);
MessageDlg('No valid lazdoc path',
LazDocPackageName+' does not have any valid lazdoc path.'#13
+'Unable to create the fpdoc file for '+SrcFilename,mtError,[mbCancel],0);
finally
PkgList.Free;
end;
end;
function TLazDocManager.CreateFPDocFile(const ExpandedFilename,
PackageName, ModuleName: string): TCodeBuffer;
var
Doc: TXMLDocument;
DescrNode: TDOMElement;
ms: TMemoryStream;
s: string;
ModuleNode: TDOMElement;
PackageNode: TDOMElement;
begin
Result:=nil;
if FileExistsCached(ExpandedFilename) then begin
Result:=CodeToolBoss.LoadFile(ExpandedFilename,true,false);
exit;
end;
Result:=CodeToolBoss.CreateFile(ExpandedFilename);
if Result=nil then begin
MessageDlg('Unable to create file',
'Unable to create file '+ExpandedFilename,mtError,[mbCancel],0);
exit;
end;
Doc:=nil;
ms:=nil;
try
Doc:=TXMLDocument.Create;
// <fpdoc-descriptions>
DescrNode:=Doc.CreateElement('fpdoc-descriptions');
Doc.AppendChild(DescrNode);
// <package name="packagename">
PackageNode:=Doc.CreateElement('package');
PackageNode.SetAttribute('name',PackageName);
DescrNode.AppendChild(PackageNode);
// <module name="unitname">
ModuleNode:=Doc.CreateElement('module');
ModuleNode.SetAttribute('name',ModuleName);
PackageNode.AppendChild(ModuleNode);
// write the XML to a string
ms:=TMemoryStream.Create;
WriteXMLFile(Doc,ms);
ms.Position:=0;
SetLength(s,ms.Size);
if s<>'' then
ms.Read(s[1],length(s));
// copy to codebuffer
//DebugLn(['TLazDocManager.CreateFPDocFile ',s]);
Result.Source:=s;
// save file
if SaveCodeBuffer(Result)<>mrOk then
Result:=nil;
finally
ms.Free;
Doc.Free;
end;
end;
constructor TLazDocManager.Create;
begin
FDocs:=TAvgLvlTree.Create(@CompareLazFPDocFilenames);
@ -647,7 +868,8 @@ begin
end;
function TLazDocManager.GetFPDocFilenameForSource(SrcFilename: string;
ResolveIncludeFiles: Boolean; out CacheWasUsed: boolean): string;
ResolveIncludeFiles: Boolean; out CacheWasUsed: boolean;
CreateIfNotExists: boolean): string;
var
FPDocName: String;
SearchPath: String;
@ -671,9 +893,9 @@ var
if not FilenameIsAbsolute(SrcFilename) then exit;
if CheckSourceDirectories then begin
PkgList:=PackageEditingInterface.GetOwnersOfUnit(SrcFilename);
end else begin
PkgList:=PackageEditingInterface.GetPossibleOwnersOfUnit(SrcFilename,[]);
end else begin
PkgList:=PackageEditingInterface.GetOwnersOfUnit(SrcFilename);
end;
// get all packages owning the file
if PkgList=nil then exit;
@ -730,46 +952,61 @@ begin
end;
end;
if not FilenameIsPascalSource(SrcFilename) then exit;
if not FilenameIsPascalSource(SrcFilename) then begin
DebugLn(['TLazDocManager.GetFPDocFilenameForSource error: not a source file: "',SrcFilename,'"']);
exit;
end;
// 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;
try
// first try cache
MapEntry:=nil;
AVLNode:=FSrcToDocMap.FindKey(Pointer(SrcFilename),
@CompareAnsistringWithLDSrc2DocSrcFile);
if AVLNode<>nil then begin
MapEntry:=TLDSourceToFPDocFile(AVLNode.Data);
if (MapEntry.FPDocFilenameTimeStamp=CompilerParseStamp)
and (MapEntry.FilesTimeStamp=FileStateCache.TimeStamp) then begin
Result:=MapEntry.FPDocFilename;
exit;
end;
end;
end;
CacheWasUsed:=false;
{$IFDEF VerboseLazDoc}
DebugLn(['TLazDocManager.GetFPDocFilenameForSource searching SrcFilename=',SrcFilename]);
{$ENDIF}
CacheWasUsed:=false;
// first check if the file is owned by any project/package
SearchPath:='';
CheckUnitOwners(false);
CheckUnitOwners(true);
CheckIfInLazarus;
{$IFDEF VerboseLazDoc}
DebugLn(['TLazDocManager.GetFPDocFilenameForSource searching SrcFilename=',SrcFilename]);
{$ENDIF}
// finally add the default paths
AddSearchPath(EnvironmentOptions.LazDocPaths,'');
FPDocName:=lowercase(ExtractFileNameOnly(SrcFilename))+'.xml';
{$IFDEF VerboseLazDoc}
DebugLn(['TLazDocManager.GetFPDocFilenameForSource Search ',FPDocName,' in "',SearchPath,'"']);
{$ENDIF}
Result:=SearchFileInPath(FPDocName,'',SearchPath,';',ctsfcAllCase);
// save to cache
if MapEntry=nil then begin
MapEntry:=TLDSourceToFPDocFile.Create;
MapEntry.SourceFilename:=SrcFilename;
FSrcToDocMap.Add(MapEntry);
// first check if the file is owned by any project/package
SearchPath:='';
CheckUnitOwners(false);// first check if file is owned by a package/project
CheckUnitOwners(true);// then check if the file is in a source directory of a package/project
CheckIfInLazarus;
// finally add the default paths
AddSearchPath(EnvironmentOptions.LazDocPaths,'');
FPDocName:=lowercase(ExtractFileNameOnly(SrcFilename))+'.xml';
{$IFDEF VerboseLazDoc}
DebugLn(['TLazDocManager.GetFPDocFilenameForSource Search ',FPDocName,' in "',SearchPath,'"']);
{$ENDIF}
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;
MapEntry.FilesTimeStamp:=FileStateCache.TimeStamp;
finally
if (Result='') and CreateIfNotExists then begin
Result:=DoCreateFPDocFileForSource(SrcFilename);
end;
{$IFDEF VerboseLazDoc}
DebugLn(['TLazDocManager.GetFPDocFilenameForSource SrcFilename="',SrcFilename,'" Result="',Result,'"']);
{$ENDIF}
end;
MapEntry.FPDocFilename:=Result;
MapEntry.FPDocFilenameTimeStamp:=CompilerParseStamp;
end;
function TLazDocManager.CodeNodeToElementName(Tool: TFindDeclarationTool;
@ -841,6 +1078,65 @@ begin
Result:=ldprFailed;
end;
function TLazDocManager.GetCodeContext(CodePos: PCodeXYPosition; out
FindContext: TFindContext; Complete: boolean; out CacheWasUsed: boolean
): TLazDocParseResult;
var
CurTool: TCodeTool;
CleanPos: integer;
Node: TCodeTreeNode;
begin
Result:=ldprFailed;
FindContext:=CleanFindContext;
CacheWasUsed:=true;
//DebugLn(['TLazDocManager.GetElementChain i=',i,' X=',CodePos^.X,' Y=',CodePos^.Y]);
if (CodePos=nil) or (CodePos^.Code=nil) or (CodePos^.X<1) or (CodePos^.Y<1)
then begin
DebugLn(['TLazDocManager.GetElementChain invalid CodePos']);
exit;
end;
// build CodeTree and find node
if not CodeToolBoss.Explore(CodePos^.Code,CurTool,false,true) then begin
DebugLn(['TLazDocManager.GetElementChain note: there was a parser error']);
end;
if CurTool=nil then begin
DebugLn(['TLazDocManager.GetElementChain explore failed']);
exit;
end;
if CurTool.CaretToCleanPos(CodePos^,CleanPos)<>0 then begin
DebugLn(['TLazDocManager.GetElementChain invalid CodePos']);
exit;
end;
Node:=CurTool.FindDeepestNodeAtPos(CleanPos,false);
if Node=nil then begin
DebugLn(['TLazDocManager.GetElementChain node not found']);
exit;
end;
// use only definition nodes
if (Node.Desc=ctnProcedureHead)
and (Node.Parent<>nil) and (Node.Parent.Desc=ctnProcedure) then
Node:=Node.Parent;
if not (Node.Desc in
(AllIdentifierDefinitions+[ctnProperty,ctnProcedure,ctnEnumIdentifier]))
then begin
DebugLn(['TLazDocManager.GetElementChain ignoring node ',Node.DescAsString]);
exit;
end;
if (CurTool.NodeIsForwardDeclaration(Node)) then begin
DebugLn(['TLazDocManager.GetElementChain ignoring forward']);
exit;
end;
// success
FindContext.Tool:=CurTool;
FindContext.Node:=Node;
Result:=ldprSuccess;
end;
function TLazDocManager.GetElementChain(Code: TCodeBuffer; X, Y: integer;
Complete: boolean; out Chain: TLazDocElementChain; out CacheWasUsed: boolean
): TLazDocParseResult;
@ -848,12 +1144,10 @@ var
ListOfPCodeXYPosition: TFPList;
i: Integer;
CodePos: PCodeXYPosition;
CurTool: TCodeTool;
CleanPos: integer;
LDElement: TLazDocElement;
SrcFilename: String;
FPDocFilename: String;
Node: TCodeTreeNode;
FindContext: TFindContext;
begin
Chain:=nil;
ListOfPCodeXYPosition:=nil;
@ -877,49 +1171,15 @@ begin
for i:=0 to ListOfPCodeXYPosition.Count-1 do begin
// get source position of declaration
CodePos:=PCodeXYPosition(ListOfPCodeXYPosition[i]);
//DebugLn(['TLazDocManager.GetElementChain i=',i,' X=',CodePos^.X,' Y=',CodePos^.Y]);
if (CodePos=nil) or (CodePos^.Code=nil) or (CodePos^.X<1) or (CodePos^.Y<1)
then begin
DebugLn(['TLazDocManager.GetElementChain i=',i,' invalid CodePos']);
continue;
end;
// build CodeTree and find node
if not CodeToolBoss.Explore(CodePos^.Code,CurTool,false,true) then begin
DebugLn(['TLazDocManager.GetElementChain i=',i,' explore failed']);
continue;
end;
if CurTool.CaretToCleanPos(CodePos^,CleanPos)<>0 then begin
DebugLn(['TLazDocManager.GetElementChain i=',i,' invalid CodePos']);
continue;
end;
Node:=CurTool.FindDeepestNodeAtPos(CleanPos,false);
if Node=nil then begin
DebugLn(['TLazDocManager.GetElementChain i=',i,' node not found']);
continue;
end;
// use only definition nodes
if (Node.Desc=ctnProcedureHead)
and (Node.Parent<>nil) and (Node.Parent.Desc=ctnProcedure) then
Node:=Node.Parent;
if not (Node.Desc in
(AllIdentifierDefinitions+[ctnProperty,ctnProcedure,ctnEnumIdentifier]))
then begin
DebugLn(['TLazDocManager.GetElementChain i=',i,' ignoring node ',Node.DescAsString]);
continue;
end;
if (CurTool.NodeIsForwardDeclaration(Node)) then begin
DebugLn(['TLazDocManager.GetElementChain i=',i,' ignoring forward']);
continue;
end;
Result:=GetCodeContext(CodePos,FindContext,Complete,CacheWasUsed);
if Result=ldprFailed then continue; // skip invalid contexts
if Result<>ldprSuccess then continue;
if (not CacheWasUsed) and (not Complete) then exit(ldprParsing);
// add element
LDElement:=Chain.Add;
LDElement.CodeXYPos:=CodePos^;
LDElement.CodeContext.Tool:=CurTool;
LDElement.CodeContext.Node:=Node;
LDElement.CodeContext:=FindContext;
//DebugLn(['TLazDocManager.GetElementChain i=',i,' CodeContext=',FindContextToString(LDElement.CodeContext)]);
// find corresponding FPDoc file
@ -1063,6 +1323,62 @@ begin
DebugLn(['TLazDocManager.GetHint END Hint="',Hint,'"']);
end;
function TLazDocManager.CreateElement(Code: TCodeBuffer; X, Y: integer;
out Element: TLazDocElement): Boolean;
var
CacheWasUsed: boolean;
SrcFilename: String;
FPDocFilename: String;
begin
Result:=false;
Element:=nil;
if Code=nil then begin
DebugLn(['TLazDocManager.CreateElement failed Code=nil']);
exit;
end;
DebugLn(['TLazDocManager.CreateElement START ',Code.Filename,' ',X,',',Y]);
Element:=TLazDocElement.Create;
try
// check if code context can have a fpdoc element
Element.CodeXYPos.Code:=Code;
Element.CodeXYPos.X:=X;
Element.CodeXYPos.Y:=Y;
if GetCodeContext(@Element.CodeXYPos,Element.CodeContext,true,
CacheWasUsed)<>ldprSuccess then
begin
DebugLn(['TLazDocManager.CreateElement GetCodeContext failed for ',Code.Filename,' ',X,',',Y]);
exit;
end;
Element.ElementName:=CodeNodeToElementName(Element.CodeContext.Tool,
Element.CodeContext.Node);
DebugLn(['TLazDocManager.CreateElement Element.ElementName=',Element.ElementName]);
// find / create fpdoc file
SrcFilename:=Element.CodeContext.Tool.MainFilename;
FPDocFilename:=GetFPDocFilenameForSource(SrcFilename,false,CacheWasUsed,true);
if FPDocFilename='' then begin
// no fpdoc file
DebugLn(['TLazDocManager.CreateElement unable to create fpdoc file for ',FPDocFilename]);
end;
DebugLn(['TLazDocManager.CreateElement FPDocFilename=',FPDocFilename]);
// parse fpdoc file
if not LoadFPDocFile(FPDocFilename,true,false,Element.FPDocFile,CacheWasUsed)
then begin
DebugLn(['TLazDocManager.CreateElement unable to load fpdoc file ',FPDocFilename]);
exit;
end;
Element.ElementNode:=Element.FPDocFile.GetElementWithName(
Element.ElementName,true);
Result:=Element.ElementNode<>nil;
finally
if not Result then
FreeAndNil(Element);
end;
end;
procedure TLazDocManager.FreeDocs;
var
AVLNode: TAvgLvlTreeNode;

View File

@ -1,11 +1,11 @@
object LazDocEditForm: TLazDocEditForm
Left = 205
Left = 303
Height = 117
Top = 511
Top = 548
Width = 753
HorzScrollBar.Page = 752
VertScrollBar.Page = 116
ActiveControl = InheritedShortEdit
ActiveControl = ShortEdit
Caption = 'LazDoc editor'
ClientHeight = 117
ClientWidth = 753
@ -16,9 +16,9 @@ object LazDocEditForm: TLazDocEditForm
Left = 17
Height = 117
Width = 736
ActivePage = InheritedTabSheet
ActivePage = ShortTabSheet
Align = alClient
TabIndex = 5
TabIndex = 0
TabOrder = 0
TabPosition = tpBottom
object ShortTabSheet: TTabSheet
@ -34,6 +34,33 @@ object LazDocEditForm: TLazDocEditForm
TabOrder = 0
Text = 'ShortEdit'
end
object CreateButton: TButton
AnchorSideTop.Control = ShortEdit
AnchorSideTop.Side = asrBottom
Left = 6
Height = 29
Top = 29
Width = 98
AutoSize = True
BorderSpacing.Around = 6
Caption = 'CreateButton'
OnClick = CreateButtonClick
TabOrder = 1
end
object SaveButton: TButton
AnchorSideLeft.Control = CreateButton
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = CreateButton
Left = 110
Height = 29
Top = 29
Width = 87
AutoSize = True
BorderSpacing.Left = 6
Caption = 'SaveButton'
OnClick = SaveButtonClick
TabOrder = 2
end
end
object DescrTabSheet: TTabSheet
Caption = 'DescrTabSheet'

View File

@ -1,21 +1,28 @@
{ This is an automatically generated lazarus resource file }
LazarusResources.Add('TLazDocEditForm','FORMDATA',[
'TPF0'#15'TLazDocEditForm'#14'LazDocEditForm'#4'Left'#3#205#0#6'Height'#2'u'#3
+'Top'#3#255#1#5'Width'#3#241#2#18'HorzScrollBar.Page'#3#240#2#18'VertScrollB'
+'ar.Page'#2't'#13'ActiveControl'#7#18'InheritedShortEdit'#7'Caption'#6#13'La'
+'zDoc editor'#12'ClientHeight'#2'u'#11'ClientWidth'#3#241#2#8'OnCreate'#7#10
+'FormCreate'#9'OnDestroy'#7#11'FormDestroy'#8'OnResize'#7#10'FormResize'#0#12
+'TPageControl'#11'PageControl'#4'Left'#2#17#6'Height'#2'u'#5'Width'#3#224#2
+#10'ActivePage'#7#17'InheritedTabSheet'#5'Align'#7#8'alClient'#8'TabIndex'#2
+#5#8'TabOrder'#2#0#11'TabPosition'#7#8'tpBottom'#0#9'TTabSheet'#13'ShortTabS'
+'heet'#7'Caption'#6#13'ShortTabSheet'#12'ClientHeight'#2'V'#11'ClientWidth'#3
+#220#2#0#5'TEdit'#9'ShortEdit'#6'Height'#2#23#5'Width'#3#218#2#5'Align'#7#5
+'alTop'#19'BorderSpacing.Right'#2#2#13'OnEditingDone'#7#22'DocumentationTagC'
+'hange'#8'TabOrder'#2#0#4'Text'#6#9'ShortEdit'#0#0#0#9'TTabSheet'#13'DescrTa'
+'bSheet'#7'Caption'#6#13'DescrTabSheet'#12'ClientHeight'#2'V'#11'ClientWidth'
+#3#220#2#0#5'TMemo'#9'DescrMemo'#6'Height'#2'R'#5'Width'#3#218#2#5'Align'#7#8
'TPF0'#15'TLazDocEditForm'#14'LazDocEditForm'#4'Left'#3'/'#1#6'Height'#2'u'#3
+'Top'#3'$'#2#5'Width'#3#241#2#18'HorzScrollBar.Page'#3#240#2#18'VertScrollBa'
+'r.Page'#2't'#13'ActiveControl'#7#9'ShortEdit'#7'Caption'#6#13'LazDoc editor'
+#12'ClientHeight'#2'u'#11'ClientWidth'#3#241#2#8'OnCreate'#7#10'FormCreate'#9
+'OnDestroy'#7#11'FormDestroy'#8'OnResize'#7#10'FormResize'#0#12'TPageControl'
+#11'PageControl'#4'Left'#2#17#6'Height'#2'u'#5'Width'#3#224#2#10'ActivePage'
+#7#13'ShortTabSheet'#5'Align'#7#8'alClient'#8'TabIndex'#2#0#8'TabOrder'#2#0
+#11'TabPosition'#7#8'tpBottom'#0#9'TTabSheet'#13'ShortTabSheet'#7'Caption'#6
+#13'ShortTabSheet'#12'ClientHeight'#2'V'#11'ClientWidth'#3#220#2#0#5'TEdit'#9
+'ShortEdit'#6'Height'#2#23#5'Width'#3#218#2#5'Align'#7#5'alTop'#19'BorderSpa'
+'cing.Right'#2#2#13'OnEditingDone'#7#22'DocumentationTagChange'#8'TabOrder'#2
+#0#4'Text'#6#9'ShortEdit'#0#0#7'TButton'#12'CreateButton'#21'AnchorSideTop.C'
+'ontrol'#7#9'ShortEdit'#18'AnchorSideTop.Side'#7#9'asrBottom'#4'Left'#2#6#6
+'Height'#2#29#3'Top'#2#29#5'Width'#2'b'#8'AutoSize'#9#20'BorderSpacing.Aroun'
+'d'#2#6#7'Caption'#6#12'CreateButton'#7'OnClick'#7#17'CreateButtonClick'#8'T'
+'abOrder'#2#1#0#0#7'TButton'#10'SaveButton'#22'AnchorSideLeft.Control'#7#12
+'CreateButton'#19'AnchorSideLeft.Side'#7#9'asrBottom'#21'AnchorSideTop.Contr'
+'ol'#7#12'CreateButton'#4'Left'#2'n'#6'Height'#2#29#3'Top'#2#29#5'Width'#2'W'
+#8'AutoSize'#9#18'BorderSpacing.Left'#2#6#7'Caption'#6#10'SaveButton'#7'OnCl'
+'ick'#7#15'SaveButtonClick'#8'TabOrder'#2#2#0#0#0#9'TTabSheet'#13'DescrTabSh'
+'eet'#7'Caption'#6#13'DescrTabSheet'#12'ClientHeight'#2'V'#11'ClientWidth'#3
+#220#2#0#5'TMemo'#9'DescrMemo'#6'Height'#2'R'#5'Width'#3#218#2#5'Align'#7#8
+'alClient'#19'BorderSpacing.Right'#2#2#20'BorderSpacing.Bottom'#2#4#13'Lines'
+'.Strings'#1#6#9'DescrMemo'#0#8'OnChange'#7#22'DocumentationTagChange'#8'Tab'
+'Order'#2#0#0#0#0#9'TTabSheet'#14'ErrorsTabSheet'#7'Caption'#6#14'ErrorsTabS'
@ -58,7 +65,7 @@ LazarusResources.Add('TLazDocEditForm','FORMDATA',[
+'bOrder'#2#1#0#0#0#9'TTabSheet'#17'InheritedTabSheet'#7'Caption'#6#17'Inheri'
+'tedTabSheet'#12'ClientHeight'#2'V'#11'ClientWidth'#3#220#2#0#6'TLabel'#19'I'
+'nheritedShortLabel'#6'Height'#2#20#3'Top'#2#2#5'Width'#3#220#2#5'Align'#7#5
+'alTop'#17'BorderSpacing.Top'#2#2#7'Caption'#6#19'InheritedShortLabel'#11'Pa'
,'alTop'#17'BorderSpacing.Top'#2#2#7'Caption'#6#19'InheritedShortLabel'#11'Pa'
+'rentColor'#8#0#0#5'TEdit'#18'InheritedShortEdit'#22'AnchorSideLeft.Control'
+#7#17'InheritedTabSheet'#21'AnchorSideTop.Control'#7#19'InheritedShortLabel'
+#18'AnchorSideTop.Side'#7#9'asrBottom'#23'AnchorSideRight.Control'#7#17'Inhe'
@ -66,7 +73,7 @@ LazarusResources.Add('TLazDocEditForm','FORMDATA',[
+#2#24#5'Width'#3#220#2#7'Anchors'#11#5'akTop'#6'akLeft'#7'akRight'#0#17'Bord'
+'erSpacing.Top'#2#2#8'ReadOnly'#9#8'TabOrder'#2#0#4'Text'#6#18'InheritedShor'
+'tEdit'#0#0#7'TButton'#21'MoveToInheritedButton'#6'Height'#2#29#3'Top'#2'6'#5
,'Width'#3#158#0#8'AutoSize'#9#7'Caption'#6#21'MoveToInheritedButton'#7'OnCli'
+'Width'#3#158#0#8'AutoSize'#9#7'Caption'#6#21'MoveToInheritedButton'#7'OnCli'
+'ck'#7#26'MoveToInheritedButtonClick'#8'TabOrder'#2#1#0#0#7'TButton'#23'Copy'
+'FromInheritedButton'#22'AnchorSideLeft.Control'#7#21'MoveToInheritedButton'
+#19'AnchorSideLeft.Side'#7#9'asrBottom'#21'AnchorSideTop.Control'#7#21'MoveT'
@ -122,7 +129,7 @@ LazarusResources.Add('TLazDocEditForm','FORMDATA',[
+#9'c #A37A4E",'#13#10'"} '#9'c #F0E1CF",'#13#10'"| '#9'c #BD964F",'#13#10'"1'
+' '#9'c #977740",'#13#10'"2 '#9'c #7C6038",'#13#10'"3 '#9'c #9C7749",'#13#10
+'"4 '#9'c #866340",'#13#10'"5 '#9'c #BA8D59",'#13#10'"6 '#9'c #634430",'#13
+#10'"7 '#9'c #8A6542",'#13#10'"8 '#9'c #C99761",'#13#10'"9 '#9'c #CC9B62",'
,#10'"7 '#9'c #8A6542",'#13#10'"8 '#9'c #C99761",'#13#10'"9 '#9'c #CC9B62",'
+#13#10'"0 '#9'c #6C5842",'#13#10'"a '#9'c #82705A",'#13#10'"b '#9'c #A47C4F"'
+','#13#10'"c '#9'c #614A2F",'#13#10'"d '#9'c #906C45",'#13#10'"e '#9'c #7C51'
+'3C",'#13#10'"f '#9'c #AD7F54",'#13#10'"g '#9'c #B78858",'#13#10'"h '#9'c #A'
@ -130,7 +137,7 @@ LazarusResources.Add('TLazDocEditForm','FORMDATA',[
+' #93705B",'#13#10'"l '#9'c #3F3636",'#13#10'"m '#9'c #D3D3D3",'#13#10'"n '#9
+'c #CE9E60",'#13#10'"o '#9'c #755738",'#13#10'"p '#9'c #A77951",'#13#10'"q '
+#9'c #CE9C63",'#13#10'"r '#9'c #3B271D",'#13#10'"s '#9'c #825F4B",'#13#10'"t'
,' '#9'c #D6D5D3",'#13#10'"u '#9'c #EEEAE0",'#13#10'"v '#9'c #AB7A53",'#13#10
+' '#9'c #D6D5D3",'#13#10'"u '#9'c #EEEAE0",'#13#10'"v '#9'c #AB7A53",'#13#10
+'"w '#9'c #745438",'#13#10'"x '#9'c #A77B50",'#13#10'"y '#9'c #948B83",'#13
+#10'"z '#9'c #261313",'#13#10'"A '#9'c #8B6843",'#13#10'"B '#9'c #634730",'
+#13#10'"C '#9'c #4B3924",'#13#10'"D '#9'c #B58757",'#13#10'"E '#9'c #816347"'
@ -186,7 +193,7 @@ LazarusResources.Add('TLazDocEditForm','FORMDATA',[
+' ",'#13#10'" ",'#13#10'" ",'
+#13#10'" ",'#13#10'" ",'#13#10'" '
+' "};'#13#10#9'NumGlyphs'#2#0#7'OnClick'#7#17'FormatButtonClick'#0#0#0#11
+'TOpenDialog'#10'OpenDialog'#5'Title'#6#17'Open example file'#6'Filter'#6#28
,'TOpenDialog'#10'OpenDialog'#5'Title'#6#17'Open example file'#6'Filter'#6#28
+'pascal file|*.pas; *.pp; *.p'#11'FilterIndex'#2#0#4'left'#2'@'#3'top'#2#24#0
+#0#0
]);

View File

@ -31,7 +31,7 @@ unit LazDocFrm;
{$mode objfpc}{$H+}
{ $define dbgLazDoc}
{ $define VerboseLazDoc}
interface
@ -68,6 +68,8 @@ type
TLazDocEditForm = class(TForm)
AddLinkButton: TButton;
BrowseExampleButton: TButton;
SaveButton: TButton;
CreateButton: TButton;
CopyFromInheritedButton: TButton;
MoveToInheritedButton: TButton;
InheritedShortEdit: TEdit;
@ -98,6 +100,7 @@ type
procedure AddLinkButtonClick(Sender: TObject);
procedure BrowseExampleButtonClick(Sender: TObject);
procedure CopyFromInheritedButtonClick(Sender: TObject);
procedure CreateButtonClick(Sender: TObject);
procedure DeleteLinkButtonClick(Sender: TObject);
procedure DocumentationTagChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
@ -108,6 +111,7 @@ type
procedure LinkListBoxClick(Sender: TObject);
procedure ApplicationIdle(Sender: TObject; var Done: Boolean);
procedure MoveToInheritedButtonClick(Sender: TObject);
procedure SaveButtonClick(Sender: TObject);
private
FCaretXY: TPoint;
FModified: Boolean;
@ -126,6 +130,7 @@ type
function FindInheritedIndex: integer;
procedure Save;
function GetValues: TFPDocElementValues;
procedure SetModified(const AValue: boolean);
function WriteNode(Element: TLazDocElement; Values: TFPDocElementValues;
Interactive: Boolean): Boolean;
procedure UpdateChain;
@ -137,6 +142,7 @@ type
procedure OnLazDocChanged(Sender: TObject; LazDocFPFile: TLazFPDocFile);
procedure LoadGUIValues(Element: TLazDocElement);
procedure MoveToInherited(Element: TLazDocElement);
function CreateElement(Element: TLazDocElement): Boolean;
public
procedure Reset;
procedure InvalidateChain;
@ -148,6 +154,7 @@ type
property Doc: TXMLdocument read GetDoc;
property SourceFilename: string read GetSourceFilename;
property CaretXY: TPoint read FCaretXY;
property Modified: boolean read FModified write SetModified;
end;
var
@ -232,6 +239,11 @@ begin
InsertRemarkButton.Hint := lisLazDocHintRemarkTag;
InsertVarTagButton.Hint := lisLazDocHintVarTag;
CreateButton.Caption := 'Create help item';
CreateButton.Enabled:=false;
SaveButton.Caption := 'Save';
SaveButton.Enabled:=false;
AddLinkButton.Caption := lisLazDocAddLinkButton;
DeleteLinkButton.Caption := lisLazDocDeleteLinkButton;
@ -413,6 +425,11 @@ begin
end;
end;
procedure TLazDocEditForm.SaveButtonClick(Sender: TObject);
begin
Save;
end;
function TLazDocEditForm.GetContextTitle(Element: TLazDocElement): string;
// get codetools path. for example: TButton.Align
begin
@ -487,6 +504,7 @@ begin
if (fChain<>nil) and (fChain.Count>0) then
Element:=fChain[0];
LoadGUIValues(Element);
SaveButton.Enabled:=FModified;
end;
procedure TLazDocEditForm.UpdateInheritedControls;
@ -597,7 +615,11 @@ var
OldModified: Boolean;
begin
OldModified:=FModified;
EnabledState := (Element<>nil) and (Element.ElementNode<>nil);
CreateButton.Enabled := (Element<>nil) and (Element.ElementNode=nil)
and (Element.ElementName<>'');
if EnabledState then
begin
@ -643,6 +665,25 @@ begin
WriteNode(Element,Values,true);
end;
function TLazDocEditForm.CreateElement(Element: TLazDocElement): Boolean;
var
NewElement: TLazDocElement;
begin
DebugLn(['TLazDocEditForm.CreateElement ']);
if (Element=nil) or (Element.ElementName='') then exit(false);
NewElement:=nil;
Include(FFlags,ldffWriting);
try
Result:=LazDocBoss.CreateElement(Element.CodeXYPos.Code,
Element.CodeXYPos.X,Element.CodeXYPos.Y,NewElement);
finally
Exclude(FFlags,ldffWriting);
NewElement.Free;
end;
Reset;
InvalidateChain;
end;
procedure TLazDocEditForm.Reset;
begin
FreeAndNil(fChain);
@ -656,7 +697,8 @@ begin
LinkListBox.Clear;
ExampleEdit.Clear;
FModified := False;
Modified := False;
CreateButton.Enabled:=false;
end;
procedure TLazDocEditForm.InvalidateChain;
@ -705,7 +747,7 @@ end;
procedure TLazDocEditForm.ClearEntry(DoSave: Boolean);
begin
FModified:=true;
Modified:=true;
ShortEdit.Text:='';
DescrMemo.Text:='';
ErrorsMemo.Text:='';
@ -729,6 +771,7 @@ begin
end else begin
FModified := False;
end;
SaveButton.Enabled:=false;
end;
function TLazDocEditForm.GetValues: TFPDocElementValues;
@ -740,6 +783,13 @@ begin
Result[fpdiExample]:=ExampleEdit.Text;
end;
procedure TLazDocEditForm.SetModified(const AValue: boolean);
begin
if FModified=AValue then exit;
FModified:=AValue;
SaveButton.Enabled:=FModified;
end;
function TLazDocEditForm.WriteNode(Element: TLazDocElement;
Values: TFPDocElementValues; Interactive: Boolean): Boolean;
var
@ -888,7 +938,7 @@ end;
procedure TLazDocEditForm.DocumentationTagChange(Sender: TObject);
begin
FModified := True;
Modified := True;
end;
function TLazDocEditForm.MakeLink: String;
@ -925,7 +975,7 @@ begin
if Trim(LinkIdComboBox.Text) <> '' then
begin
LinkListBox.Items.Add(MakeLink);
FModified := True;
Modified := True;
end;
end;
@ -951,7 +1001,13 @@ begin
mtConfirmation,[mrYes,'Replace',mrCancel],0)<>mrYes then exit;
end;
LoadGUIValues(fChain[i]);
FModified:=true;
Modified:=true;
end;
procedure TLazDocEditForm.CreateButtonClick(Sender: TObject);
begin
if (fChain=nil) or (fChain.Count=0) then exit;
CreateElement(fChain[0]);
end;
procedure TLazDocEditForm.DeleteLinkButtonClick(Sender: TObject);
@ -959,7 +1015,7 @@ begin
if LinkListBox.ItemIndex >= 0 then begin
LinkListBox.Items.Delete(LinkListBox.ItemIndex);
DebugLn(['TLazDocEditForm.DeleteLinkButtonClick ']);
FModified := True;
Modified := True;
end;
end;