dglobals.pp 32 KB

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