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. ReadXMLFile(Doc, AFilename);
  874. DescrDocs.Add(Doc);
  875. DescrDocNames.Add(AFilename);
  876. Node := Doc.DocumentElement.FirstChild;
  877. while Assigned(Node) do
  878. begin
  879. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'package') then
  880. begin
  881. PackageDocNode := ReadNode(RootDocNode, TDOMElement(Node));
  882. // Scan all 'module' elements within this package element
  883. Subnode := Node.FirstChild;
  884. while Assigned(Subnode) do
  885. begin
  886. if (Subnode.NodeType = ELEMENT_NODE) then
  887. begin
  888. If (Subnode.NodeName = 'module') then
  889. begin
  890. ModuleDocNode := ReadNode(PackageDocNode, TDOMElement(Subnode));
  891. // Scan all 'element' elements within this module element
  892. Subsubnode := Subnode.FirstChild;
  893. while Assigned(Subsubnode) do
  894. begin
  895. if (Subsubnode.NodeType = ELEMENT_NODE) then
  896. begin
  897. if (Subsubnode.NodeName = 'element') then
  898. ReadNode(ModuleDocNode, TDOMElement(Subsubnode))
  899. else if (SubSubNode.NodeName='topic') then
  900. begin
  901. TopicNode:=ReadNode(ModuleDocNode,TDomElement(SubSubNode));
  902. TopicNode.FTopicNode:=True;
  903. ReadTopics(TopicNode);
  904. end;
  905. end;
  906. Subsubnode := Subsubnode.NextSibling;
  907. end;
  908. end
  909. else if (SubNode.NodeName='topic') then
  910. begin
  911. TopicNode:=ReadNode(PackageDocNode,TDomElement(SubNode));
  912. TopicNode.FTopicNode:=True;
  913. ReadTopics(TopicNode);
  914. end;
  915. end;
  916. Subnode := Subnode.NextSibling;
  917. end;
  918. end;
  919. Node := Node.NextSibling;
  920. end;
  921. end;
  922. function TFPDocEngine.FindDocNode(AElement: TPasElement): TDocNode;
  923. begin
  924. Result:=Nil;
  925. If Assigned(AElement) then
  926. begin
  927. if AElement.InheritsFrom(TPasUnresolvedTypeRef) then
  928. Result := FindDocNode(AElement.GetModule, AElement.Name)
  929. else
  930. Result := RootDocNode.FindChild(AElement.PathName);
  931. if (Result=Nil) and
  932. WarnNoNode and
  933. (Length(AElement.PathName)>0) and
  934. (AElement.PathName[1]='#') then
  935. Writeln('No documentation node found for identifier : ',AElement.PathName);
  936. end;
  937. end;
  938. function TFPDocEngine.FindDocNode(ARefModule: TPasModule;
  939. const AName: String): TDocNode;
  940. var
  941. CurPackage: TDocNode;
  942. UnitList: TList;
  943. i: Integer;
  944. begin
  945. if Length(AName) = 0 then
  946. Result := nil
  947. else
  948. begin
  949. if AName[1] = '#' then
  950. Result := RootDocNode.FindChild(AName)
  951. else
  952. Result := RootDocNode.FindChild(Package.Name + '.' + AName);
  953. if (not Assigned(Result)) and Assigned(ARefModule) then
  954. Result := RootDocNode.FindChild(ARefModule.PathName + '.' + AName);
  955. if (not Assigned(Result)) and (AName[1] <> '#') then
  956. begin
  957. CurPackage := RootDocNode.FirstChild;
  958. while Assigned(CurPackage) do
  959. begin
  960. Result := RootDocNode.FindChild(CurPackage.Name + '.' + AName);
  961. if Assigned(Result) then
  962. break;
  963. CurPackage := CurPackage.NextSibling;
  964. end;
  965. if not Assigned(Result) then
  966. begin
  967. { Okay, then we have to try all imported units of the current module }
  968. UnitList := CurModule.InterfaceSection.UsesList;
  969. for i := UnitList.Count - 1 downto 0 do
  970. begin
  971. { Try all packages }
  972. CurPackage := RootDocNode.FirstChild;
  973. while Assigned(CurPackage) do
  974. begin
  975. Result := RootDocNode.FindChild(CurPackage.Name + '.' +
  976. TPasType(UnitList[i]).Name + '.' + AName);
  977. if Assigned(Result) then
  978. break;
  979. CurPackage := CurPackage.NextSibling;
  980. end;
  981. end;
  982. end;
  983. end;
  984. end;
  985. end;
  986. function TFPDocEngine.FindShortDescr(AElement: TPasElement): TDOMElement;
  987. var
  988. DocNode: TDocNode;
  989. begin
  990. DocNode := FindDocNode(AElement);
  991. if Assigned(DocNode) then
  992. Result := DocNode.ShortDescr
  993. else
  994. Result := nil;
  995. end;
  996. function TFPDocEngine.FindShortDescr(ARefModule: TPasModule;
  997. const AName: String): TDOMElement;
  998. var
  999. DocNode: TDocNode;
  1000. begin
  1001. DocNode := FindDocNode(ARefModule, AName);
  1002. if Assigned(DocNode) then
  1003. Result := DocNode.ShortDescr
  1004. else
  1005. Result := nil;
  1006. end;
  1007. function TFPDocEngine.GetExampleFilename(const ExElement: TDOMElement): String;
  1008. var
  1009. i: Integer;
  1010. begin
  1011. for i := 0 to DescrDocs.Count - 1 do
  1012. if TDOMDocument(DescrDocs[i]) = ExElement.OwnerDocument then
  1013. begin
  1014. Result := ExtractFilePath(DescrDocNames[i]) + ExElement['file'];
  1015. if (ExtractFileExt(Result)='') then
  1016. Result:=Result+'.pp';
  1017. exit;
  1018. end;
  1019. SetLength(Result, 0);
  1020. end;
  1021. { Global helpers }
  1022. procedure TranslateDocStrings(const Lang: String);
  1023. var
  1024. mo: TMOFile;
  1025. begin
  1026. {$IFDEF Unix}
  1027. mo := TMOFile.Create(Format('/usr/local/share/locale/%s/LC_MESSAGES/dglobals.mo', [Lang]));
  1028. {$ELSE}
  1029. mo := TMOFile.Create(Format('intl/dglobals.%s.mo', [Lang]));
  1030. {$ENDIF}
  1031. try
  1032. TranslateResourceStrings(mo);
  1033. finally
  1034. mo.Free;
  1035. end;
  1036. end;
  1037. end.
  1038. {
  1039. $Log$
  1040. Revision 1.5 2004-08-28 18:47:48 michael
  1041. + Removed temporary warning about reading of doc file
  1042. Revision 1.4 2004/08/28 18:03:23 michael
  1043. + Added warning if docnode not found (option --warn-no-node
  1044. Revision 1.3 2004/06/06 10:53:02 michael
  1045. + Added Topic support
  1046. Revision 1.2 2003/11/28 12:51:37 sg
  1047. * Added support for source references
  1048. Revision 1.1 2003/03/17 23:03:20 michael
  1049. + Initial import in CVS
  1050. Revision 1.13 2003/03/13 22:02:13 sg
  1051. * New version with many bugfixes and our own parser (now independent of the
  1052. compiler source)
  1053. Revision 1.12 2002/11/15 19:44:18 sg
  1054. * Cosmetic changes
  1055. Revision 1.11 2002/10/12 17:00:45 michael
  1056. + Changes to be able to disable private/protected nodes in skeleton
  1057. Revision 1.10 2002/05/24 00:13:22 sg
  1058. * much improved new version, including many linking and output fixes
  1059. Revision 1.9 2002/03/25 23:16:24 sg
  1060. * fixed missing storing of documenation data for the DocNode of a module
  1061. (so e.g. the Unit Overview is working again)
  1062. Revision 1.8 2002/03/12 10:58:35 sg
  1063. * reworked linking engine and internal structure
  1064. Revision 1.7 2002/01/20 11:19:55 michael
  1065. + Added link attribute and property to TFPelement
  1066. Revision 1.6 2001/12/17 22:16:02 sg
  1067. * Added TFPDocEngine.HideProtected
  1068. }