dglobals.pp 37 KB

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