diff --git a/ide/buildlazdialog.pas b/ide/buildlazdialog.pas index 52459bcde7..fd68acfc57 100644 --- a/ide/buildlazdialog.pas +++ b/ide/buildlazdialog.pas @@ -570,7 +570,7 @@ begin if NewUnitDirectory<>'' then // FPC interpretes '\ ' as an escape for a space in a path, // so make sure the directory doesn't end with the path delimeter. - AppendExtraOption('-FU'+ChompPathDelim(NewTargetDirectory)); + AppendExtraOption('-FU'+ChompPathDelim(NewUnitDirectory)); if NewTargetDirectory<>'' then // FPC interpretes '\ ' as an escape for a space in a path, diff --git a/ide/codehelp.pas b/ide/codehelp.pas index 42912ae6d0..0d3043312a 100644 --- a/ide/codehelp.pas +++ b/ide/codehelp.pas @@ -315,6 +315,7 @@ type function SourcePosToFPDocHint(const aFilename: string; X,Y: integer; Caption: string=''): string; function OwnerToFPDocHint(AnOwner: TObject): string; + function FPDocLinkToURL(FPDocFile: TLazFPDocFile; const LinkID: string): string; public // Event lists procedure RemoveAllHandlersOfObject(AnObject: TObject); @@ -2496,7 +2497,7 @@ begin HTMLHint:=HTMLHint+s; end; HTMLHint:=HTMLHint+GetFPDocNodeAsHTML(FPDocFile,ElementNode.FindNode(FPDocItemNames[fpdiErrors])); - // todo HTMLHint:=HTMLHint+GetFPDocNodeAsHTML(FPDocFile,ElementNode.FindNode(FPDocItemNames[fpdiSeeAlso])); + HTMLHint:=HTMLHint+GetFPDocNodeAsHTML(FPDocFile,ElementNode.FindNode(FPDocItemNames[fpdiSeeAlso])); HTMLHint:=HTMLHint+GetFPDocNodeAsHTML(FPDocFile,ElementNode.FindNode(FPDocItemNames[fpdiExample])); end; end; @@ -2533,7 +2534,7 @@ begin except on E: Exception do begin debugln(['TCodeHelpManager.GetHTMLHint2 Exception: ',E.Message]); - DumpExceptionBackTrace; + //DumpExceptionBackTrace; end; end; @@ -2633,7 +2634,7 @@ function TCodeHelpManager.GetFPDocNodeAsHTML(FPDocFile: TLazFPDocFile; if (Attr=nil) or (Attr.NodeValue='') then exit; s:=AddChilds(Node); if s='' then s:=Attr.NodeValue; - Result:=Result+''+s+'
'; + Result:=Result+''+s+'
'; end else if (Node.NodeName='example') then begin Attr:=Node.Attributes.GetNamedItem('file'); if (Attr=nil) or (Attr.NodeValue='') then exit; @@ -2833,6 +2834,24 @@ begin +TLazPackage(AnOwner).Name+''; end; +function TCodeHelpManager.FPDocLinkToURL(FPDocFile: TLazFPDocFile; + const LinkID: string): string; +begin + Result:=LinkID; + if Result='' then exit; + if Result[1]='#' then begin + // has already a package + exit; + end; + if FPDocFile.GetElementWithName(Result)<>nil then begin + // link target is in this unit => prepend package and unit name + Result:='#'+FPDocFile.GetPackageName+'.'+FPDocFile.GetModuleName+'.'+Result; + end else begin + // link target is not in this unit, but same package => prepend package name + Result:='#'+FPDocFile.GetPackageName+'.'+Result; + end; +end; + procedure TCodeHelpManager.FreeDocs; var AVLNode: TAvgLvlTreeNode; diff --git a/ide/idehelpmanager.pas b/ide/idehelpmanager.pas index 57ad992240..b44a6f5f8a 100644 --- a/ide/idehelpmanager.pas +++ b/ide/idehelpmanager.pas @@ -41,8 +41,8 @@ uses PascalParserTool, FindDeclarationTool, // IDEIntf PropEdits, ObjectInspector, FormEditingIntf, ProjectIntf, TextTools, - LazHelpIntf, LazHelpHTML, HelpFPDoc, MacroIntf, IDEWindowIntf, IDEMsgIntf, - PackageIntf, LazIDEIntf, HelpIntfs, IDEHelpIntf, + IDEDialogs, LazHelpIntf, LazHelpHTML, HelpFPDoc, MacroIntf, IDEWindowIntf, + IDEMsgIntf, PackageIntf, LazIDEIntf, HelpIntfs, IDEHelpIntf, // IDE LazarusIDEStrConsts, TransferMacros, DialogProcs, IDEOptionDefs, ObjInspExt, EnvironmentOpts, AboutFrm, MsgView, Project, PackageDefs, MainBar, @@ -75,6 +75,7 @@ type FProviders: TLIHProviders; procedure SetProviders(const AValue: TLIHProviders); procedure OpenNextURL(Data: PtrInt); // called via Application.QueueAsyncCall + procedure OpenFPDoc(Path: string); public NextURL: string; destructor Destroy; override; @@ -540,6 +541,7 @@ var begin fWaitingForAsync:=false; SplitURL(NextURL,URLScheme,URLPath,URLParams); + debugln(['TLazIDEHTMLProvider.OpenNextURL "',URLScheme,'" :// "',URLPath,'" & "',URLParams,'"']); if URLScheme='source' then begin p:=Point(1,1); if REMatches(URLPath,'(.*)\((.*),(.*)\)') then begin @@ -554,6 +556,109 @@ begin end else if (URLScheme='openpackage') and (URLPath<>'') and IsValidIdent(URLPath) then begin PackageEditingInterface.DoOpenPackageWithName(URLPath,[],false); + end else if (URLScheme='fpdoc') and (URLParams<>'') then begin + OpenFPDoc(URLParams); + end; +end; + +procedure TLazIDEHTMLProvider.OpenFPDoc(Path: string); +var + RestPath: string; + + function ExtractSubPath: string; + var + p: SizeInt; + begin + p:=System.Pos('.',RestPath); + if p<1 then p:=length(RestPath)+1; + Result:=copy(RestPath,1,p-1); + RestPath:=copy(RestPath,p+1,length(RestPath)); + end; + + procedure InvalidPathError(Msg: string); + begin + debugln(['InvalidPathError Path="',Path,'" Msg="',Msg,'"']); + IDEMessageDialog('Unable to open fpdoc help', + 'The fpdoc path "'+Path+'" is invalid.'#13+Msg,mtError,[mbCancel]); + end; + +var + PkgName: String; + Pkg: TLazPackage; + AnUnitName: String; + PkgFile: TPkgFile; + ContextList: TPascalHelpContextList; + ElementName: String; + Filename: String; + ErrMsg: string; + PascalHelpContextLists: TList; + i: Integer; +begin + RestPath:=Path; + PkgName:=ExtractSubPath; + if (PkgName='') or (PkgName[1]<>'#') then begin + InvalidPathError('It does not start with a package name, for example #rtl.'); + exit; + end; + PkgName:=copy(PkgName,2,length(PkgName)); + if (PkgName='') or not IsValidIdent(PkgName) then begin + InvalidPathError('It does not start with a package name, for example #rtl.'); + exit; + end; + if SysUtils.CompareText(PkgName,'rtl')=0 then PkgName:='fcl'; + Pkg:=TLazPackage(PackageEditingInterface.FindPackageWithName(PkgName)); + if Pkg=nil then begin + InvalidPathError('Package "'+PkgName+'" not found.'); + exit; + end; + if Pkg.IsVirtual then begin + InvalidPathError('Package "'+PkgName+'" has no help.'); + exit; + end; + + AnUnitName:=ExtractSubPath; + if (AnUnitName='') or (not IsValidIdent(AnUnitName)) then begin + InvalidPathError('Unit name "'+AnUnitName+'" is invalid.'); + exit; + end; + + Filename:=''; + PkgFile:=Pkg.FindUnit(AnUnitName); + if (PkgFile<>nil) and (PkgFile.FileType in PkgFileRealUnitTypes) then begin + // normal unit in lpk + if PkgFile.IsVirtual then begin + InvalidPathError('Unit "'+PkgFile.Filename+'" has no help.'); + exit; + end; + Filename:=PkgFile.Filename; + end else if SysUtils.CompareText(PkgName,'fcl')=0 then begin + // search in FPC sources + Filename:=CodeToolBoss.DirectoryCachePool.FindUnitInUnitSet('',AnUnitName); + end; + if Filename='' then begin + InvalidPathError('Unit "'+AnUnitName+'" has no help.'); + exit; + end; + + PascalHelpContextLists:=TList.Create; + try + // create a context list (and add it as sole element to the PascalHelpContextLists) + ContextList:=TPascalHelpContextList.Create; + PascalHelpContextLists.Add(ContextList); + ContextList.Add(pihcFilename,Filename); + ContextList.Add(pihcSourceName,AnUnitName); + repeat + ElementName:=ExtractSubPath; + if ElementName='' then break; + ContextList.Add(pihcType,ElementName); + until false; + ShowHelpForPascalContexts(Filename,Point(1,1),PascalHelpContextLists,ErrMsg); + finally + if PascalHelpContextLists<>nil then begin + for i:=0 to PascalHelpContextLists.Count-1 do + TObject(PascalHelpContextLists[i]).Free; + PascalHelpContextLists.Free; + end; end; end; @@ -572,9 +677,8 @@ var begin Result:=false; SplitURL(NextURL,URLScheme,URLPath,URLParams); - if (URLScheme='file') or (URLScheme='lazdoc') then begin + if (URLScheme='file') or (URLScheme='lazdoc') or (URLScheme='fpdoc') then Result:=true; - end; end; procedure TLazIDEHTMLProvider.OpenURLAsync(const URL: string); @@ -1173,12 +1277,8 @@ function TIDEHelpManager.ConvertCodePosToPascalHelpContext( procedure AddContext(Descriptor: TPascalHelpContextType; const Context: string); - var - CurContext: TPascalHelpContext; begin - CurContext.Descriptor:=Descriptor; - CurContext.Context:=Context; - Result.Add(CurContext); + Result.Add(Descriptor,Context); //debugln(' AddContext Descriptor=',dbgs(ord(Descriptor)),' Context="',Context,'"'); end; diff --git a/ideintf/idehelpintf.pas b/ideintf/idehelpintf.pas index 0a613a46a6..a829ba5fe9 100644 --- a/ideintf/idehelpintf.pas +++ b/ideintf/idehelpintf.pas @@ -107,8 +107,9 @@ type destructor Destroy; override; function URLHasStream(const URL: string): boolean; virtual; abstract; { The standard IDE implementation supports for OpenURLAsync the following: - source://local-file-name : this opens the file local-file-name in the editor - openpackage://package-name : this opens the package editor of the package with the name package-name + source://local-file-name : open a file local-file-name in the source editor + openpackage://package-name : open a package editor + fpdoc://#package-name.unitname.element : this opens the help for the fpdoc entry } procedure OpenURLAsync(const URL: string); virtual; abstract; function GetStream(const URL: string; Shared: boolean diff --git a/lcl/lazhelpintf.pas b/lcl/lazhelpintf.pas index dc3826b595..0115161adf 100644 --- a/lcl/lazhelpintf.pas +++ b/lcl/lazhelpintf.pas @@ -66,6 +66,7 @@ type function GetItems(Index: integer): TPascalHelpContext; public procedure Add(const Context: TPascalHelpContext); + procedure Add(Descriptor: TPascalHelpContextType; const Context: string); procedure Insert(Index: integer; const Context: TPascalHelpContext); procedure Clear; destructor Destroy; override; @@ -2255,6 +2256,16 @@ begin fItems[FCount-1]:=Context; end; +procedure TPascalHelpContextList.Add(Descriptor: TPascalHelpContextType; + const Context: string); +var + CurContext: TPascalHelpContext; +begin + CurContext.Descriptor:=Descriptor; + CurContext.Context:=Context; + Add(CurContext); +end; + procedure TPascalHelpContextList.Insert(Index: integer; const Context: TPascalHelpContext); begin