dglobals.pp 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267
  1. {
  2. FPDoc - Free Pascal Documentation Tool
  3. Copyright (C) 2000 - 2002 by
  4. Areca Systems GmbH / Sebastian Guenther, [email protected]
  5. * Global declarations
  6. * Link list management
  7. * Document node tree
  8. * Main engine
  9. See the file COPYING, included in this distribution,
  10. for details about the copyright.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. }
  15. {$MODE objfpc}
  16. {$H+}
  17. unit dGlobals;
  18. interface
  19. uses Classes, DOM, PasTree, PParser;
  20. Var
  21. LEOL : Integer;
  22. resourcestring
  23. // Output strings
  24. SDocPackageTitle = 'Reference for package ''%s''';
  25. SDocPrograms = 'Programs';
  26. SDocUnits = 'Units';
  27. SDocUnitTitle = 'Reference for unit ''%s''';
  28. SDocInterfaceSection = 'Interface section';
  29. SDocImplementationSection = 'Implementation section';
  30. SDocUsedUnits = 'Used units';
  31. SDocUsedUnitsByUnitXY = 'Used units by unit ''%s''';
  32. SDocConstsTypesVars = 'Constants, types and variables';
  33. SDocResStrings = 'Resource strings';
  34. SDocTypes = 'Types';
  35. SDocConstants = 'Constants';
  36. SDocClasses = 'Classes';
  37. SDocProceduresAndFunctions = 'Procedures and functions';
  38. SDocVariables = 'Variables';
  39. SDocUnitOverview = 'Overview of unit ''%s''';
  40. SDocOverview = 'Overview';
  41. SDocSearch = 'Search';
  42. SDocDeclaration = 'Declaration';
  43. SDocDescription = 'Description';
  44. SDocErrors = 'Errors';
  45. SDocSeeAlso = 'See also';
  46. SDocExample = 'Example';
  47. SDocArguments = 'Arguments';
  48. SDocFunctionResult = 'Function result';
  49. SDocRemark = 'Remark: ';
  50. SDocMethodOverview = 'Method overview';
  51. SDocPropertyOverview = 'Property overview';
  52. SDocPage = 'Page';
  53. SDocMethod = 'Method';
  54. SDocProperty = 'Property';
  55. SDocAccess = 'Access';
  56. SDocInheritance = 'Inheritance';
  57. SDocProperties = 'Properties';
  58. SDocMethods = 'Methods';
  59. SDocEvents = 'Events';
  60. SDocByName = 'by Name';
  61. SDocValue = 'Value';
  62. SDocExplanation = 'Explanation';
  63. SDocProcedure = 'Procedure';
  64. SDocValuesForEnum = 'Enumeration values for type %s';
  65. SDocSourcePosition = 'Source position: %s line %d';
  66. SDocSynopsis = 'Synopsis';
  67. SDocVisibility = 'Visibility';
  68. SDocOpaque = 'Opaque type';
  69. // Topics
  70. SDocRelatedTopics = 'Related topics';
  71. SDocUp = 'Up';
  72. SDocNext = 'Next';
  73. SDocPrevious = 'Previous';
  74. // Various backend constants
  75. SDocChapter = 'Chapter';
  76. SDocSection = 'Section';
  77. SDocSubSection = 'Subsection';
  78. SDocTable = 'Table';
  79. SDocListing = 'Listing';
  80. // Man page usage
  81. SManUsageManSection = 'Use ASection as the man page section';
  82. SManUsageNoUnitPrefix = 'Do not prefix man pages with unit name.';
  83. SManUsageWriterDescr = 'UNIX man page output.';
  84. SManUsagePackageDescription = 'Use descr as the description of man pages';
  85. // HTML usage
  86. SHTMLUsageFooter = 'Append xhtml from file as footer to html page';
  87. // CHM usage
  88. SCHMUsageTOC = 'Use [File] as the table of contents. Usually a .hhc file.';
  89. SCHMUsageIndex = 'Use [File] as the index. Usually a .hhk file.';
  90. SCHMUsageDefPage = 'Set the "Home" page relative to where it lives in the chm. i.e. "/index.html"';
  91. SCHMUsageOtrFiles= 'A txt file containing a list of files to be added relative to the working directory.';
  92. SCHMUsageCSSFile = 'Filename of a .css file to be included in the chm.';
  93. SCHMUsageAutoTOC = 'Automatically generate a Table of Contents. Ignores --toc-file';
  94. SCHMUsageAutoIDX = 'Automatically generate an Index. Ignores --index-file';
  95. STitle = 'FPDoc - Free Pascal Documentation Tool';
  96. SVersion = 'Version %s [%s]';
  97. SCopyright = '(c) 2000 - 2003 Areca Systems GmbH / Sebastian Guenther, [email protected]';
  98. SCmdLineHelp = 'Usage: %s [options]';
  99. SUsageOption010 = '--content Create content file for package cross-references';
  100. SUsageOption020 = '--cputarget=value Set the target CPU for the scanner.';
  101. SUsageOption030 = '--descr=name use name as description file. ';
  102. SUsageOption040 = ' This option is allowed more than once';
  103. SUsageOption050 = '--format=fmt Select output format.';
  104. SUsageOption060 = '--help Show this help.';
  105. SUsageOption070 = '--hide-protected Do not show protected methods in overview';
  106. SUsageOption080 = '--import=file Import content file for package cross-references';
  107. SUsageOption090 = '--input=cmd use cmd as input for the parser.';
  108. SUsageOption100 = ' At least one input option is required.';
  109. SUsageOption110 = '--lang=lng Select output language.';
  110. SUsageOption120 = '--ostarget=value Set the target OS for the scanner.';
  111. SUsageOption130 = '--output=name use name as the output name.';
  112. SUsageOption140 = ' Each backend interpretes this as needed.';
  113. SUsageOption150 = '--package=name Set the package name for which to create output';
  114. SUsageOption160 = '--show-private Show private methods.';
  115. SUsageOption170 = '--warn-no-node Warn if no documentation node was found.';
  116. SUsageFormats = 'The following output formats are supported by this fpdoc:';
  117. SUsageBackendHelp = 'Specify an output format, combined with --help to get more help for this backend.';
  118. SUsageFormatSpecific = 'Output format "%s" supports the following options:';
  119. SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
  120. SCmdLineInvalidFormat = 'Invalid format "%s" specified';
  121. SCmdLineOutputOptionMissing = 'Need an output filename, please specify one with --output=<filename>';
  122. SWritingPages = 'Writing %d pages...';
  123. SNeedPackageName = 'No package name specified. Please specify one using the --package option.';
  124. SDone = 'Done.';
  125. SErrCouldNotCreateOutputDir = 'Could not create output directory "%s"';
  126. SErrCouldNotCreateFile = 'Could not create file "%s": %s';
  127. Const
  128. SVisibility: array[TPasMemberVisibility] of string =
  129. ('Default', 'Private', 'Protected', 'Public',
  130. 'Published', 'Automated');
  131. type
  132. // Assumes a list of TObject instances and frees them on destruction
  133. TObjectList = class(TList)
  134. public
  135. destructor Destroy; override;
  136. end;
  137. { Link entry tree
  138. TFPDocEngine stores the root of the entry tree in its property
  139. "RootLinkNode". The root has one child node for each package, for which
  140. documentation links are available. The children of a package node
  141. are module nodes; and the children of a module node are the top-level
  142. declarations of this module; the next level in the tree stores e.g. record
  143. members, and so on...
  144. }
  145. TLinkNode = class
  146. private
  147. FFirstChild, FNextSibling: TLinkNode;
  148. FName: String;
  149. FLink: String;
  150. public
  151. constructor Create(const AName, ALink: String);
  152. destructor Destroy; override;
  153. function FindChild(const APathName: String): TLinkNode;
  154. function CreateChildren(const APathName, ALinkTo: String): TLinkNode;
  155. // Properties for tree structure
  156. property FirstChild: TLinkNode read FFirstChild;
  157. property NextSibling: TLinkNode read FNextSibling;
  158. // Link properties
  159. property Name: String read FName;
  160. property Link: String read FLink;
  161. end;
  162. { Documentation entry tree
  163. TFPDocEngine stores the root of the entry tree in its property
  164. "RootDocNode". The root has one child node for each package, for which
  165. documentation is being provided by the user. The children of a package node
  166. are module nodes; and the children of a module node are the top-level
  167. declarations of this module; the next level in the tree stores e.g. record
  168. members, and so on...
  169. }
  170. TDocNode = class
  171. private
  172. FFirstChild, FNextSibling: TDocNode;
  173. FName: String;
  174. FNode: TDOMElement;
  175. FIsSkipped: Boolean;
  176. FShortDescr: TDOMElement;
  177. FDescr: TDOMElement;
  178. FErrorsDoc: TDOMElement;
  179. FSeeAlso: TDOMElement;
  180. FFirstExample: TDOMElement;
  181. FLink: String;
  182. FTopicNode : Boolean;
  183. FRefCount : Integer;
  184. public
  185. constructor Create(const AName: String; ANode: TDOMElement);
  186. destructor Destroy; override;
  187. Function IncRefcount : Integer;
  188. function FindChild(const APathName: String): TDocNode;
  189. function CreateChildren(const APathName: String): TDocNode;
  190. // Properties for tree structure
  191. property FirstChild: TDocNode read FFirstChild;
  192. property NextSibling: TDocNode read FNextSibling;
  193. // Basic properties
  194. property Name: String read FName;
  195. property Node: TDOMElement read FNode;
  196. // Data fetched from the XML document
  197. property IsSkipped: Boolean read FIsSkipped;
  198. property ShortDescr: TDOMElement read FShortDescr;
  199. property Descr: TDOMElement read FDescr;
  200. property ErrorsDoc: TDOMElement read FErrorsDoc;
  201. property SeeAlso: TDOMElement read FSeeAlso;
  202. property FirstExample: TDOMElement read FFirstExample;
  203. property Link: String read FLink;
  204. Property TopicNode : Boolean Read FTopicNode;
  205. Property RefCount : Integer Read FRefCount;
  206. end;
  207. // The main FPDoc engine
  208. TFPDocEngine = class(TPasTreeContainer)
  209. protected
  210. DescrDocs: TObjectList; // List of XML documents
  211. DescrDocNames: TStringList; // Names of the XML documents
  212. FRootLinkNode: TLinkNode;
  213. FRootDocNode: TDocNode;
  214. FPackages: TList; // List of TFPPackage objects
  215. CurModule: TPasModule;
  216. CurPackageDocNode: TDocNode;
  217. public
  218. constructor Create;
  219. destructor Destroy; override;
  220. procedure SetPackageName(const APackageName: String);
  221. procedure ReadContentFile(const AFilename, ALinkPrefix: String);
  222. procedure WriteContentFile(const AFilename: String);
  223. function CreateElement(AClass: TPTreeElement; const AName: String;
  224. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  225. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  226. override;
  227. function FindElement(const AName: String): TPasElement; override;
  228. function FindModule(const AName: String): TPasModule; override;
  229. // Link tree support
  230. procedure AddLink(const APathName, ALinkTo: String);
  231. function FindAbsoluteLink(const AName: String): String;
  232. function ResolveLink(AModule: TPasModule; const ALinkDest: String): String;
  233. // Documentation file support
  234. procedure AddDocFile(const AFilename: String);
  235. // Documentation retrieval
  236. function FindDocNode(AElement: TPasElement): TDocNode;
  237. function FindDocNode(ARefModule: TPasModule; const AName: String): TDocNode;
  238. function FindShortDescr(AElement: TPasElement): TDOMElement;
  239. function FindShortDescr(ARefModule: TPasModule;
  240. const AName: String): TDOMElement;
  241. function GetExampleFilename(const ExElement: TDOMElement): String;
  242. property RootLinkNode: TLinkNode read FRootLinkNode;
  243. property RootDocNode: TDocNode read FRootDocNode;
  244. property Package: TPasPackage read FPackage;
  245. Output: String;
  246. HasContentFile: Boolean;
  247. HidePrivate: Boolean; // Hide private class members in output?
  248. HideProtected: Boolean; // Hide protected class members in output?
  249. WarnNoNode : Boolean; // Warn if no description node found for element.
  250. end;
  251. procedure TranslateDocStrings(const Lang: String);
  252. Function IsLinkNode(Node : TDomNode) : Boolean;
  253. Function IsExampleNode(Example : TDomNode) : Boolean;
  254. // returns true is link is an absolute URI
  255. Function IsLinkAbsolute(ALink: String): boolean;
  256. implementation
  257. uses SysUtils, Gettext, XMLRead;
  258. const
  259. AbsoluteLinkPrefixes : array[0..2] of string = ('/', 'http://', 'ms-its:');
  260. { TObjectList }
  261. destructor TObjectList.Destroy;
  262. var
  263. i: Integer;
  264. begin
  265. for i := 0 to Count - 1 do
  266. TObject(Items[i]).Free;
  267. inherited Destroy;
  268. end;
  269. { TLinkNode }
  270. constructor TLinkNode.Create(const AName, ALink: String);
  271. begin
  272. inherited Create;
  273. FName := AName;
  274. FLink := ALink;
  275. end;
  276. destructor TLinkNode.Destroy;
  277. begin
  278. if Assigned(FirstChild) then
  279. FirstChild.Free;
  280. if Assigned(NextSibling) then
  281. NextSibling.Free;
  282. inherited Destroy;
  283. end;
  284. function TLinkNode.FindChild(const APathName: String): TLinkNode;
  285. var
  286. DotPos: Integer;
  287. ChildName: String;
  288. Child: TLinkNode;
  289. begin
  290. if Length(APathName) = 0 then
  291. Result := Self
  292. else
  293. begin
  294. DotPos := Pos('.', APathName);
  295. if DotPos = 0 then
  296. ChildName := APathName
  297. else
  298. ChildName := Copy(APathName, 1, DotPos - 1);
  299. Child := FirstChild;
  300. while Assigned(Child) do
  301. begin
  302. if CompareText(Child.Name, ChildName) = 0 then
  303. begin
  304. if DotPos = 0 then
  305. Result := Child
  306. else
  307. Result := Child.FindChild(
  308. Copy(APathName, DotPos + 1, Length(APathName)));
  309. exit;
  310. end;
  311. Child := Child.NextSibling;
  312. end;
  313. Result := nil;
  314. end;
  315. end;
  316. function TLinkNode.CreateChildren(const APathName, ALinkTo: String): TLinkNode;
  317. var
  318. DotPos: Integer;
  319. ChildName: String;
  320. Child, LastChild: TLinkNode;
  321. begin
  322. if Length(APathName) = 0 then
  323. Result := Self
  324. else
  325. begin
  326. DotPos := Pos('.', APathName);
  327. if DotPos = 0 then
  328. ChildName := APathName
  329. else
  330. ChildName := Copy(APathName, 1, DotPos - 1);
  331. Child := FirstChild;
  332. LastChild := nil;
  333. while Assigned(Child) do
  334. begin
  335. if CompareText(Child.Name, ChildName) = 0 then
  336. begin
  337. if DotPos = 0 then
  338. Result := Child
  339. else
  340. Result := Child.CreateChildren(
  341. Copy(APathName, DotPos + 1, Length(APathName)), ALinkTo);
  342. exit;
  343. end;
  344. LastChild := Child;
  345. Child := Child.NextSibling;
  346. end;
  347. { No child found, let's create one if we are at the end of the path }
  348. if DotPos > 0 then
  349. // !!!: better throw an exception
  350. WriteLn('Link path does not exist: ', APathName);
  351. Result := TLinkNode.Create(ChildName, ALinkTo);
  352. if Assigned(LastChild) then
  353. LastChild.FNextSibling := Result
  354. else
  355. FFirstChild := Result;
  356. end;
  357. end;
  358. { TDocNode }
  359. constructor TDocNode.Create(const AName: String; ANode: TDOMElement);
  360. begin
  361. inherited Create;
  362. FName := AName;
  363. FNode := ANode;
  364. end;
  365. destructor TDocNode.Destroy;
  366. begin
  367. if Assigned(FirstChild) then
  368. FirstChild.Free;
  369. if Assigned(NextSibling) then
  370. NextSibling.Free;
  371. inherited Destroy;
  372. end;
  373. Function TDocNode.IncRefcount : Integer;
  374. begin
  375. Inc(FRefCount);
  376. Result:=FRefCount;
  377. end;
  378. function TDocNode.FindChild(const APathName: String): TDocNode;
  379. var
  380. DotPos: Integer;
  381. ChildName: String;
  382. Child: TDocNode;
  383. begin
  384. if Length(APathName) = 0 then
  385. Result := Self
  386. else
  387. begin
  388. DotPos := Pos('.', APathName);
  389. if DotPos = 0 then
  390. ChildName := APathName
  391. else
  392. ChildName := Copy(APathName, 1, DotPos - 1);
  393. Child := FirstChild;
  394. while Assigned(Child) do
  395. begin
  396. if CompareText(Child.Name, ChildName) = 0 then
  397. begin
  398. if DotPos = 0 then
  399. Result := Child
  400. else
  401. Result := Child.FindChild(
  402. Copy(APathName, DotPos + 1, Length(APathName)));
  403. exit;
  404. end;
  405. Child := Child.NextSibling;
  406. end;
  407. Result := nil;
  408. end;
  409. end;
  410. function TDocNode.CreateChildren(const APathName: String): TDocNode;
  411. var
  412. DotPos: Integer;
  413. ChildName: String;
  414. Child: TDocNode;
  415. begin
  416. if Length(APathName) = 0 then
  417. Result := Self
  418. else
  419. begin
  420. DotPos := Pos('.', APathName);
  421. if DotPos = 0 then
  422. ChildName := APathName
  423. else
  424. ChildName := Copy(APathName, 1, DotPos - 1);
  425. Child := FirstChild;
  426. while Assigned(Child) do
  427. begin
  428. if CompareText(Child.Name, ChildName) = 0 then
  429. begin
  430. if DotPos = 0 then
  431. Result := Child
  432. else
  433. Result := Child.CreateChildren(
  434. Copy(APathName, DotPos + 1, Length(APathName)));
  435. exit;
  436. end;
  437. Child := Child.NextSibling;
  438. end;
  439. // No child found, let's create one
  440. Result := TDocNode.Create(ChildName, nil);
  441. if Assigned(FirstChild) then
  442. begin
  443. Result.FNextSibling := FirstChild;
  444. FFirstChild := Result;
  445. end else
  446. FFirstChild := Result;
  447. if DotPos > 0 then
  448. Result := Result.CreateChildren(
  449. Copy(APathName, DotPos + 1, Length(APathName)));
  450. end;
  451. end;
  452. { TFPDocEngine }
  453. constructor TFPDocEngine.Create;
  454. begin
  455. inherited Create;
  456. DescrDocs := TObjectList.Create;
  457. DescrDocNames := TStringList.Create;
  458. FRootLinkNode := TLinkNode.Create('', '');
  459. FRootDocNode := TDocNode.Create('', nil);
  460. HidePrivate := True;
  461. FPackages := TList.Create;
  462. end;
  463. destructor TFPDocEngine.Destroy;
  464. var
  465. i: Integer;
  466. begin
  467. for i := 0 to FPackages.Count - 1 do
  468. TPasPackage(FPackages[i]).Release;
  469. FPackages.Free;
  470. FRootDocNode.Free;
  471. FRootLinkNode.Free;
  472. DescrDocNames.Free;
  473. DescrDocs.Free;
  474. inherited Destroy;
  475. end;
  476. procedure TFPDocEngine.SetPackageName(const APackageName: String);
  477. begin
  478. ASSERT(not Assigned(Package));
  479. FPackage := TPasPackage(inherited CreateElement(TPasPackage,
  480. '#' + APackageName, nil, '', 0));
  481. FPackages.Add(FPackage);
  482. CurPackageDocNode := RootDocNode.FindChild('#' + APackageName);
  483. If Assigned(CurPackageDocNode) then
  484. CurPackageDocNode.IncRefCount;
  485. end;
  486. procedure TFPDocEngine.ReadContentFile(const AFilename, ALinkPrefix: String);
  487. var
  488. f: Text;
  489. procedure ReadLinkTree;
  490. var
  491. s: String;
  492. PrevSpaces, ThisSpaces, i, StackIndex: Integer;
  493. CurParent, PrevSibling, NewNode: TLinkNode;
  494. ParentStack, SiblingStack: array[0..7] of TLinkNode;
  495. begin
  496. PrevSpaces := 0;
  497. CurParent := RootLinkNode;
  498. PrevSibling := CurParent.FirstChild;
  499. if assigned(PrevSibling) then
  500. while assigned(PrevSibling.NextSibling) do
  501. PrevSibling := PrevSibling.NextSibling;
  502. StackIndex := 0;
  503. while True do
  504. begin
  505. ReadLn(f, s);
  506. if Length(s) = 0 then
  507. break;
  508. ThisSpaces := 0;
  509. while s[ThisSpaces + 1] = ' ' do
  510. Inc(ThisSpaces);
  511. if ThisSpaces <> PrevSpaces then
  512. begin
  513. if ThisSpaces > PrevSpaces then
  514. begin
  515. { Dive down one level }
  516. ParentStack[StackIndex] := CurParent;
  517. SiblingStack[StackIndex] := PrevSibling;
  518. Inc(StackIndex);
  519. CurParent := PrevSibling;
  520. PrevSibling := nil;
  521. end else
  522. while PrevSpaces > ThisSpaces do
  523. begin
  524. Dec(StackIndex);
  525. CurParent := ParentStack[StackIndex];
  526. PrevSibling := SiblingStack[StackIndex];
  527. Dec(PrevSpaces);
  528. end;
  529. PrevSpaces := ThisSpaces;
  530. end;
  531. i := ThisSpaces + 1;
  532. while s[i] <> ' ' do
  533. Inc(i);
  534. NewNode := TLinkNode.Create(Copy(s, ThisSpaces + 1, i - ThisSpaces - 1),
  535. ALinkPrefix + Copy(s, i + 1, Length(s)));
  536. if Assigned(PrevSibling) then
  537. PrevSibling.FNextSibling := NewNode
  538. else
  539. CurParent.FFirstChild := NewNode;
  540. PrevSibling := NewNode;
  541. end;
  542. end;
  543. procedure ReadClasses;
  544. function CreateClass(const AName: String): TPasClassType;
  545. var
  546. DotPos, DotPos2, i: Integer;
  547. s: String;
  548. HPackage: TPasPackage;
  549. Module: TPasModule;
  550. begin
  551. // Find or create package
  552. DotPos := Pos('.', AName);
  553. s := Copy(AName, 1, DotPos - 1);
  554. HPackage := nil;
  555. for i := 0 to FPackages.Count - 1 do
  556. if CompareText(TPasPackage(FPackages[i]).Name, s) = 0 then
  557. begin
  558. HPackage := TPasPackage(FPackages[i]);
  559. break;
  560. end;
  561. if not Assigned(HPackage) then
  562. begin
  563. HPackage := TPasPackage(inherited CreateElement(TPasPackage, s, nil,
  564. '', 0));
  565. FPackages.Add(HPackage);
  566. end;
  567. // Find or create module
  568. DotPos2 := DotPos;
  569. repeat
  570. Inc(DotPos2);
  571. until AName[DotPos2] = '.';
  572. s := Copy(AName, DotPos + 1, DotPos2 - DotPos - 1);
  573. Module := nil;
  574. for i := 0 to HPackage.Modules.Count - 1 do
  575. if CompareText(TPasModule(HPackage.Modules[i]).Name, s) = 0 then
  576. begin
  577. Module := TPasModule(HPackage.Modules[i]);
  578. break;
  579. end;
  580. if not Assigned(Module) then
  581. begin
  582. Module := TPasModule.Create(s, HPackage);
  583. Module.InterfaceSection := TPasSection.Create('', Module);
  584. HPackage.Modules.Add(Module);
  585. end;
  586. // Create node for class
  587. Result := TPasClassType.Create(Copy(AName, DotPos2 + 1, Length(AName)),
  588. Module.InterfaceSection);
  589. Result.ObjKind := okClass;
  590. Module.InterfaceSection.Declarations.Add(Result);
  591. Module.InterfaceSection.Classes.Add(Result);
  592. end;
  593. var
  594. s, Name: String;
  595. CurClass: TPasClassType;
  596. i: Integer;
  597. Member: TPasElement;
  598. begin
  599. CurClass := nil;
  600. while True do
  601. begin
  602. ReadLn(f, s);
  603. if Length(s) = 0 then
  604. break;
  605. if s[1] = '#' then
  606. begin
  607. // New class
  608. i := Pos(' ', s);
  609. CurClass := CreateClass(Copy(s, 1, i - 1));
  610. end else
  611. begin
  612. i := Pos(' ', s);
  613. if i = 0 then
  614. Name := Copy(s, 3, Length(s))
  615. else
  616. Name := Copy(s, 3, i - 3);
  617. case s[2] of
  618. 'M':
  619. Member := TPasProcedure.Create(Name, CurClass);
  620. 'P':
  621. begin
  622. Member := TPasProperty.Create(Name, CurClass);
  623. if i > 0 then
  624. while i <= Length(s) do
  625. begin
  626. case s[i] of
  627. 'r':
  628. TPasProperty(Member).ReadAccessorName := '<dummy>';
  629. 'w':
  630. TPasProperty(Member).WriteAccessorName := '<dummy>';
  631. 's':
  632. TPasProperty(Member).StoredAccessorName := '<dummy>';
  633. end;
  634. Inc(i);
  635. end;
  636. end;
  637. 'V':
  638. Member := TPasVariable.Create(Name, CurClass);
  639. else
  640. raise Exception.Create('Invalid member type: ' + s[2]);
  641. end;
  642. CurClass.Members.Add(Member);
  643. end;
  644. end;
  645. end;
  646. var
  647. s: String;
  648. begin
  649. if not FileExists(AFileName) then
  650. raise EInOutError.Create('File not found: ' + AFileName);
  651. Assign(f, AFilename);
  652. Reset(f);
  653. while not EOF(f) do
  654. begin
  655. ReadLn(f, s);
  656. if (Length(s) = 0) or (s[1] = '#') then
  657. continue;
  658. if s = ':link tree' then
  659. ReadLinkTree
  660. else if s = ':classes' then
  661. ReadClasses
  662. else
  663. repeat
  664. ReadLn(f, s);
  665. until EOF(f) or (Length(s) = 0);
  666. end;
  667. Close(f);
  668. end;
  669. procedure TFPDocEngine.WriteContentFile(const AFilename: String);
  670. var
  671. ContentFile: Text;
  672. procedure ProcessLinkNode(ALinkNode: TLinkNode; const AIdent: String);
  673. var
  674. ChildNode: TLinkNode;
  675. begin
  676. WriteLn(ContentFile, AIdent, ALinkNode.Name, ' ', ALinkNode.Link);
  677. ChildNode := ALinkNode.FirstChild;
  678. while Assigned(ChildNode) do
  679. begin
  680. ProcessLinkNode(ChildNode, AIdent + ' ');
  681. ChildNode := ChildNode.NextSibling;
  682. end;
  683. end;
  684. var
  685. LinkNode: TLinkNode;
  686. i, j, k: Integer;
  687. Module: TPasModule;
  688. ClassDecl: TPasClassType;
  689. Member: TPasElement;
  690. s: String;
  691. begin
  692. Assign(ContentFile, AFilename);
  693. Rewrite(ContentFile);
  694. try
  695. WriteLn(ContentFile, '# FPDoc Content File');
  696. WriteLn(ContentFile, ':link tree');
  697. LinkNode := RootLinkNode.FirstChild;
  698. while Assigned(LinkNode) do
  699. begin
  700. if LinkNode.Name = Package.Name then
  701. begin
  702. ProcessLinkNode(LinkNode, '');
  703. end;
  704. LinkNode := LinkNode.NextSibling;
  705. end;
  706. if Assigned(Package) then
  707. begin
  708. WriteLn(ContentFile);
  709. WriteLn(ContentFile, ':classes');
  710. for i := 0 to Package.Modules.Count - 1 do
  711. begin
  712. Module := TPasModule(Package.Modules[i]);
  713. for j := 0 to Module.InterfaceSection.Classes.Count - 1 do
  714. begin
  715. ClassDecl := TPasClassType(Module.InterfaceSection.Classes[j]);
  716. Write(ContentFile, ClassDecl.PathName, ' ');
  717. if Assigned(ClassDecl.AncestorType) then
  718. WriteLn(ContentFile, ClassDecl.AncestorType.PathName)
  719. else if ClassDecl.ObjKind = okClass then
  720. WriteLn(ContentFile, '.TObject');
  721. for k := 0 to ClassDecl.Members.Count - 1 do
  722. begin
  723. Member := TPasElement(ClassDecl.Members[k]);
  724. Write(ContentFile, Chr(Ord(Member.Visibility) + Ord('0')));
  725. SetLength(s, 0);
  726. if Member.ClassType = TPasVariable then
  727. Write(ContentFile, 'V')
  728. else if Member.ClassType = TPasProperty then
  729. begin
  730. Write(ContentFile, 'P');
  731. if Length(TPasProperty(Member).ReadAccessorName) > 0 then
  732. s := s + 'r';
  733. if Length(TPasProperty(Member).WriteAccessorName) > 0 then
  734. s := s + 'w';
  735. if Length(TPasProperty(Member).StoredAccessorName) > 0 then
  736. s := s + 's';
  737. end else
  738. Write(ContentFile, 'M'); // Member must be a method
  739. Write(ContentFile, Member.Name);
  740. if Length(s) > 0 then
  741. WriteLn(ContentFile, ' ', s)
  742. else
  743. WriteLn(ContentFile);
  744. end;
  745. end;
  746. end;
  747. end;
  748. finally
  749. Close(ContentFile);
  750. end;
  751. end;
  752. function TFPDocEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  753. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  754. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  755. begin
  756. Result := AClass.Create(AName, AParent);
  757. Result.Visibility := AVisibility;
  758. if AClass.InheritsFrom(TPasModule) then
  759. CurModule := TPasModule(Result);
  760. Result.SourceFilename := ASourceFilename;
  761. Result.SourceLinenumber := ASourceLinenumber;
  762. end;
  763. function TFPDocEngine.FindElement(const AName: String): TPasElement;
  764. function FindInModule(AModule: TPasModule; const LocalName: String): TPasElement;
  765. var
  766. l: TList;
  767. i: Integer;
  768. begin
  769. l := AModule.InterfaceSection.Declarations;
  770. for i := 0 to l.Count - 1 do
  771. begin
  772. Result := TPasElement(l[i]);
  773. if CompareText(Result.Name, LocalName) = 0 then
  774. exit;
  775. end;
  776. Result := nil;
  777. end;
  778. var
  779. i: Integer;
  780. //ModuleName, LocalName: String;
  781. Module: TPasElement;
  782. begin
  783. {!!!: Don't know if we ever will have to use the following:
  784. i := Pos('.', AName);
  785. if i <> 0 then
  786. begin
  787. WriteLn('Dot found in name: ', AName);
  788. Result := nil;
  789. end else
  790. begin}
  791. Result := FindInModule(CurModule, AName);
  792. if not Assigned(Result) then
  793. for i := CurModule.InterfaceSection.UsesList.Count - 1 downto 0 do
  794. begin
  795. Module := TPasElement(CurModule.InterfaceSection.UsesList[i]);
  796. if Module.ClassType = TPasModule then
  797. begin
  798. Result := FindInModule(TPasModule(Module), AName);
  799. if Assigned(Result) then
  800. exit;
  801. end;
  802. end;
  803. {end;}
  804. end;
  805. function TFPDocEngine.FindModule(const AName: String): TPasModule;
  806. function FindInPackage(APackage: TPasPackage): TPasModule;
  807. var
  808. i: Integer;
  809. begin
  810. for i := 0 to APackage.Modules.Count - 1 do
  811. begin
  812. Result := TPasModule(APackage.Modules[i]);
  813. if CompareText(Result.Name, AName) = 0 then
  814. exit;
  815. end;
  816. Result := nil;
  817. end;
  818. var
  819. i: Integer;
  820. begin
  821. Result := FindInPackage(Package);
  822. if not Assigned(Result) then
  823. for i := FPackages.Count - 1 downto 0 do
  824. begin
  825. if TPasPackage(FPackages[i]) = Package then
  826. continue;
  827. Result := FindInPackage(TPasPackage(FPackages[i]));
  828. if Assigned(Result) then
  829. exit;
  830. end;
  831. end;
  832. procedure TFPDocEngine.AddLink(const APathName, ALinkTo: String);
  833. begin
  834. RootLinkNode.CreateChildren(APathName, ALinkTo);
  835. end;
  836. function TFPDocEngine.FindAbsoluteLink(const AName: String): String;
  837. var
  838. LinkNode: TLinkNode;
  839. begin
  840. LinkNode := RootLinkNode.FindChild(AName);
  841. if Assigned(LinkNode) then
  842. Result := LinkNode.Link
  843. else
  844. SetLength(Result, 0);
  845. end;
  846. function TFPDocEngine.ResolveLink(AModule: TPasModule;
  847. const ALinkDest: String): String;
  848. var
  849. i: Integer;
  850. ThisPackage: TLinkNode;
  851. UnitList: TList;
  852. begin
  853. //WriteLn('ResolveLink(', ALinkDest, ')... ');
  854. if Length(ALinkDest) = 0 then
  855. begin
  856. SetLength(Result, 0);
  857. exit;
  858. end;
  859. if (ALinkDest[1] = '#') or (not assigned(AModule)) then
  860. Result := FindAbsoluteLink(ALinkDest)
  861. else
  862. begin
  863. Result := ResolveLink(AModule, AModule.PathName + '.' + ALinkDest);
  864. if Length(Result) > 0 then
  865. exit;
  866. { Try all packages }
  867. SetLength(Result, 0);
  868. ThisPackage := RootLinkNode.FirstChild;
  869. while Assigned(ThisPackage) do
  870. begin
  871. Result := ResolveLink(AModule, ThisPackage.Name + '.' + ALinkDest);
  872. if Length(Result) > 0 then
  873. exit;
  874. ThisPackage := ThisPackage.NextSibling;
  875. end;
  876. if Length(Result) = 0 then
  877. begin
  878. { Okay, then we have to try all imported units of the current module }
  879. UnitList := AModule.InterfaceSection.UsesList;
  880. for i := UnitList.Count - 1 downto 0 do
  881. begin
  882. { Try all packages }
  883. ThisPackage := RootLinkNode.FirstChild;
  884. while Assigned(ThisPackage) do
  885. begin
  886. Result := ResolveLink(AModule, ThisPackage.Name + '.' +
  887. TPasType(UnitList[i]).Name + '.' + ALinkDest);
  888. if Length(Result) > 0 then
  889. exit;
  890. ThisPackage := ThisPackage.NextSibling;
  891. end;
  892. end;
  893. end;
  894. end;
  895. if Length(Result) = 0 then
  896. for i := Length(ALinkDest) downto 1 do
  897. if ALinkDest[i] = '.' then
  898. begin
  899. Result := ResolveLink(AModule, Copy(ALinkDest, 1, i - 1));
  900. exit;
  901. end;
  902. end;
  903. procedure TFPDocEngine.AddDocFile(const AFilename: String);
  904. function ReadNode(OwnerDocNode: TDocNode; Element: TDOMElement): TDocNode;
  905. var
  906. Subnode: TDOMNode;
  907. begin
  908. if OwnerDocNode = RootDocNode then
  909. Result := OwnerDocNode.CreateChildren('#' + Element['name'])
  910. else
  911. Result := OwnerDocNode.CreateChildren(Element['name']);
  912. Result.FNode := Element;
  913. Result.FLink := Element['link'];
  914. Result.FIsSkipped := Element['skip'] = '1';
  915. Subnode := Element.FirstChild;
  916. while Assigned(Subnode) do
  917. begin
  918. if Subnode.NodeType = ELEMENT_NODE then
  919. begin
  920. if Subnode.NodeName = 'short' then
  921. Result.FShortDescr := TDOMElement(Subnode)
  922. else if Subnode.NodeName = 'descr' then
  923. Result.FDescr := TDOMElement(Subnode)
  924. else if Subnode.NodeName = 'errors' then
  925. Result.FErrorsDoc := TDOMElement(Subnode)
  926. else if Subnode.NodeName = 'seealso' then
  927. Result.FSeeAlso := TDOMElement(Subnode)
  928. else if (Subnode.NodeName = 'example') and
  929. not Assigned(Result.FirstExample) then
  930. Result.FFirstExample := TDOMElement(Subnode);
  931. end;
  932. Subnode := Subnode.NextSibling;
  933. end;
  934. end;
  935. Procedure ReadTopics(TopicNode : TDocNode);
  936. Var
  937. SubNode : TDOMNode;
  938. begin
  939. SubNode:=TopicNode.FNode.FirstChilD;
  940. While Assigned(SubNode) do
  941. begin
  942. If (SubNode.NodeType=ELEMENT_NODE) and (SubNode.NodeName='topic') then
  943. With ReadNode(TopicNode,TDomElement(SubNode)) do
  944. // We could allow recursion here, but we won't, because it doesn't work on paper.
  945. FTopicNode:=True;
  946. SubNode:=Subnode.NextSibling;
  947. end;
  948. end;
  949. var
  950. i: Integer;
  951. Node, Subnode, Subsubnode: TDOMNode;
  952. Element: TDOMElement;
  953. Doc: TXMLDocument;
  954. PackageDocNode, TopicNode,ModuleDocNode: TDocNode;
  955. begin
  956. ReadXMLFile(Doc, AFilename);
  957. DescrDocs.Add(Doc);
  958. DescrDocNames.Add(AFilename);
  959. Node := Doc.DocumentElement.FirstChild;
  960. while Assigned(Node) do
  961. begin
  962. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'package') then
  963. begin
  964. PackageDocNode := ReadNode(RootDocNode, TDOMElement(Node));
  965. PackageDocNode.IncRefCount;
  966. // Scan all 'module' elements within this package element
  967. Subnode := Node.FirstChild;
  968. while Assigned(Subnode) do
  969. begin
  970. if (Subnode.NodeType = ELEMENT_NODE) then
  971. begin
  972. If (Subnode.NodeName = 'module') then
  973. begin
  974. ModuleDocNode := ReadNode(PackageDocNode, TDOMElement(Subnode));
  975. // Scan all 'element' elements within this module element
  976. Subsubnode := Subnode.FirstChild;
  977. while Assigned(Subsubnode) do
  978. begin
  979. if (Subsubnode.NodeType = ELEMENT_NODE) then
  980. begin
  981. if (Subsubnode.NodeName = 'element') then
  982. ReadNode(ModuleDocNode, TDOMElement(Subsubnode))
  983. else if (SubSubNode.NodeName='topic') then
  984. begin
  985. TopicNode:=ReadNode(ModuleDocNode,TDomElement(SubSubNode));
  986. TopicNode.FTopicNode:=True;
  987. ReadTopics(TopicNode);
  988. end;
  989. end;
  990. Subsubnode := Subsubnode.NextSibling;
  991. end;
  992. end
  993. else if (SubNode.NodeName='topic') then
  994. begin
  995. TopicNode:=ReadNode(PackageDocNode,TDomElement(SubNode));
  996. TopicNode.FTopicNode:=True;
  997. ReadTopics(TopicNode);
  998. end;
  999. end;
  1000. Subnode := Subnode.NextSibling;
  1001. end;
  1002. end;
  1003. Node := Node.NextSibling;
  1004. end;
  1005. end;
  1006. function TFPDocEngine.FindDocNode(AElement: TPasElement): TDocNode;
  1007. begin
  1008. Result:=Nil;
  1009. If Assigned(AElement) then
  1010. begin
  1011. if AElement.InheritsFrom(TPasUnresolvedTypeRef) then
  1012. Result := FindDocNode(AElement.GetModule, AElement.Name)
  1013. else
  1014. Result := RootDocNode.FindChild(AElement.PathName);
  1015. if (Result=Nil) and
  1016. WarnNoNode and
  1017. (Length(AElement.PathName)>0) and
  1018. (AElement.PathName[1]='#') then
  1019. Writeln('No documentation node found for identifier : ',AElement.PathName);
  1020. end;
  1021. end;
  1022. function TFPDocEngine.FindDocNode(ARefModule: TPasModule;
  1023. const AName: String): TDocNode;
  1024. var
  1025. CurPackage: TDocNode;
  1026. UnitList: TList;
  1027. i: Integer;
  1028. begin
  1029. if Length(AName) = 0 then
  1030. Result := nil
  1031. else
  1032. begin
  1033. if AName[1] = '#' then
  1034. Result := RootDocNode.FindChild(AName)
  1035. else
  1036. Result := RootDocNode.FindChild(Package.Name + '.' + AName);
  1037. if (not Assigned(Result)) and Assigned(ARefModule) then
  1038. Result := RootDocNode.FindChild(ARefModule.PathName + '.' + AName);
  1039. if (not Assigned(Result)) and (AName[1] <> '#') then
  1040. begin
  1041. CurPackage := RootDocNode.FirstChild;
  1042. while Assigned(CurPackage) do
  1043. begin
  1044. Result := RootDocNode.FindChild(CurPackage.Name + '.' + AName);
  1045. if Assigned(Result) then
  1046. break;
  1047. CurPackage := CurPackage.NextSibling;
  1048. end;
  1049. if not Assigned(Result) then
  1050. begin
  1051. { Okay, then we have to try all imported units of the current module }
  1052. UnitList := CurModule.InterfaceSection.UsesList;
  1053. for i := UnitList.Count - 1 downto 0 do
  1054. begin
  1055. { Try all packages }
  1056. CurPackage := RootDocNode.FirstChild;
  1057. while Assigned(CurPackage) do
  1058. begin
  1059. Result := RootDocNode.FindChild(CurPackage.Name + '.' +
  1060. TPasType(UnitList[i]).Name + '.' + AName);
  1061. if Assigned(Result) then
  1062. break;
  1063. CurPackage := CurPackage.NextSibling;
  1064. end;
  1065. end;
  1066. end;
  1067. end;
  1068. end;
  1069. end;
  1070. function TFPDocEngine.FindShortDescr(AElement: TPasElement): TDOMElement;
  1071. var
  1072. DocNode: TDocNode;
  1073. begin
  1074. DocNode := FindDocNode(AElement);
  1075. if Assigned(DocNode) then
  1076. Result := DocNode.ShortDescr
  1077. else
  1078. Result := nil;
  1079. end;
  1080. function TFPDocEngine.FindShortDescr(ARefModule: TPasModule;
  1081. const AName: String): TDOMElement;
  1082. var
  1083. DocNode: TDocNode;
  1084. begin
  1085. DocNode := FindDocNode(ARefModule, AName);
  1086. if Assigned(DocNode) then
  1087. Result := DocNode.ShortDescr
  1088. else
  1089. Result := nil;
  1090. end;
  1091. function TFPDocEngine.GetExampleFilename(const ExElement: TDOMElement): String;
  1092. var
  1093. i: Integer;
  1094. fn : String;
  1095. begin
  1096. Result:='';
  1097. for i := 0 to DescrDocs.Count - 1 do
  1098. begin
  1099. Fn:=ExElement['file'];
  1100. if (FN<>'') and (TDOMDocument(DescrDocs[i]) = ExElement.OwnerDocument) then
  1101. begin
  1102. Result := ExtractFilePath(DescrDocNames[i]) + FN;
  1103. if (ExtractFileExt(Result)='') then
  1104. Result:=Result+'.pp';
  1105. end;
  1106. end;
  1107. end;
  1108. { Global helpers }
  1109. procedure TranslateDocStrings(const Lang: String);
  1110. var
  1111. mo: TMOFile;
  1112. begin
  1113. {$IFDEF Unix}
  1114. mo := TMOFile.Create(Format('/usr/local/share/locale/%s/LC_MESSAGES/dglobals.mo', [Lang]));
  1115. {$ELSE}
  1116. mo := TMOFile.Create(Format('intl/dglobals.%s.mo', [Lang]));
  1117. {$ENDIF}
  1118. try
  1119. TranslateResourceStrings(mo);
  1120. finally
  1121. mo.Free;
  1122. end;
  1123. end;
  1124. Function IsLinkNode(Node : TDomNode) : Boolean;
  1125. begin
  1126. Result:=Assigned(Node) and (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link');
  1127. end;
  1128. Function IsExampleNode(Example : TDomNode) : Boolean;
  1129. begin
  1130. Result:=Assigned(Example) and (Example.NodeType = ELEMENT_NODE) and (Example.NodeName = 'example')
  1131. end;
  1132. function IsLinkAbsolute(ALink: String): boolean;
  1133. var
  1134. i: integer;
  1135. begin
  1136. Result := false;
  1137. for i := low(AbsoluteLinkPrefixes) to high(AbsoluteLinkPrefixes) do
  1138. if CompareText(AbsoluteLinkPrefixes[i], copy(ALink,1,length(AbsoluteLinkPrefixes[i])))=0 then begin
  1139. Result := true;
  1140. break;
  1141. end;
  1142. end;
  1143. initialization
  1144. LEOL:=Length(LineEnding);
  1145. end.