dglobals.pp 37 KB

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