dglobals.pp 31 KB

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