dglobals.pp 35 KB

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