dglobals.pp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243
  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. // returns true is link is an absolute URI
  243. Function IsLinkAbsolute(ALink: String): boolean;
  244. implementation
  245. uses SysUtils, Gettext, XMLRead;
  246. const
  247. AbsoluteLinkPrefixes : array[0..2] of string = ('/', 'http://', 'ms-its:');
  248. { TObjectList }
  249. destructor TObjectList.Destroy;
  250. var
  251. i: Integer;
  252. begin
  253. for i := 0 to Count - 1 do
  254. TObject(Items[i]).Free;
  255. inherited Destroy;
  256. end;
  257. { TLinkNode }
  258. constructor TLinkNode.Create(const AName, ALink: String);
  259. begin
  260. inherited Create;
  261. FName := AName;
  262. FLink := ALink;
  263. end;
  264. destructor TLinkNode.Destroy;
  265. begin
  266. if Assigned(FirstChild) then
  267. FirstChild.Free;
  268. if Assigned(NextSibling) then
  269. NextSibling.Free;
  270. inherited Destroy;
  271. end;
  272. function TLinkNode.FindChild(const APathName: String): TLinkNode;
  273. var
  274. DotPos: Integer;
  275. ChildName: String;
  276. Child: TLinkNode;
  277. begin
  278. if Length(APathName) = 0 then
  279. Result := Self
  280. else
  281. begin
  282. DotPos := Pos('.', APathName);
  283. if DotPos = 0 then
  284. ChildName := APathName
  285. else
  286. ChildName := Copy(APathName, 1, DotPos - 1);
  287. Child := FirstChild;
  288. while Assigned(Child) do
  289. begin
  290. if CompareText(Child.Name, ChildName) = 0 then
  291. begin
  292. if DotPos = 0 then
  293. Result := Child
  294. else
  295. Result := Child.FindChild(
  296. Copy(APathName, DotPos + 1, Length(APathName)));
  297. exit;
  298. end;
  299. Child := Child.NextSibling;
  300. end;
  301. Result := nil;
  302. end;
  303. end;
  304. function TLinkNode.CreateChildren(const APathName, ALinkTo: String): TLinkNode;
  305. var
  306. DotPos: Integer;
  307. ChildName: String;
  308. Child, LastChild: TLinkNode;
  309. begin
  310. if Length(APathName) = 0 then
  311. Result := Self
  312. else
  313. begin
  314. DotPos := Pos('.', APathName);
  315. if DotPos = 0 then
  316. ChildName := APathName
  317. else
  318. ChildName := Copy(APathName, 1, DotPos - 1);
  319. Child := FirstChild;
  320. LastChild := nil;
  321. while Assigned(Child) do
  322. begin
  323. if CompareText(Child.Name, ChildName) = 0 then
  324. begin
  325. if DotPos = 0 then
  326. Result := Child
  327. else
  328. Result := Child.CreateChildren(
  329. Copy(APathName, DotPos + 1, Length(APathName)), ALinkTo);
  330. exit;
  331. end;
  332. LastChild := Child;
  333. Child := Child.NextSibling;
  334. end;
  335. { No child found, let's create one if we are at the end of the path }
  336. if DotPos > 0 then
  337. // !!!: better throw an exception
  338. WriteLn('Link path does not exist: ', APathName);
  339. Result := TLinkNode.Create(ChildName, ALinkTo);
  340. if Assigned(LastChild) then
  341. LastChild.FNextSibling := Result
  342. else
  343. FFirstChild := Result;
  344. end;
  345. end;
  346. { TDocNode }
  347. constructor TDocNode.Create(const AName: String; ANode: TDOMElement);
  348. begin
  349. inherited Create;
  350. FName := AName;
  351. FNode := ANode;
  352. end;
  353. destructor TDocNode.Destroy;
  354. begin
  355. if Assigned(FirstChild) then
  356. FirstChild.Free;
  357. if Assigned(NextSibling) then
  358. NextSibling.Free;
  359. inherited Destroy;
  360. end;
  361. function TDocNode.FindChild(const APathName: String): TDocNode;
  362. var
  363. DotPos: Integer;
  364. ChildName: String;
  365. Child: TDocNode;
  366. begin
  367. if Length(APathName) = 0 then
  368. Result := Self
  369. else
  370. begin
  371. DotPos := Pos('.', APathName);
  372. if DotPos = 0 then
  373. ChildName := APathName
  374. else
  375. ChildName := Copy(APathName, 1, DotPos - 1);
  376. Child := FirstChild;
  377. while Assigned(Child) do
  378. begin
  379. if CompareText(Child.Name, ChildName) = 0 then
  380. begin
  381. if DotPos = 0 then
  382. Result := Child
  383. else
  384. Result := Child.FindChild(
  385. Copy(APathName, DotPos + 1, Length(APathName)));
  386. exit;
  387. end;
  388. Child := Child.NextSibling;
  389. end;
  390. Result := nil;
  391. end;
  392. end;
  393. function TDocNode.CreateChildren(const APathName: String): TDocNode;
  394. var
  395. DotPos: Integer;
  396. ChildName: String;
  397. Child: TDocNode;
  398. begin
  399. if Length(APathName) = 0 then
  400. Result := Self
  401. else
  402. begin
  403. DotPos := Pos('.', APathName);
  404. if DotPos = 0 then
  405. ChildName := APathName
  406. else
  407. ChildName := Copy(APathName, 1, DotPos - 1);
  408. Child := FirstChild;
  409. while Assigned(Child) do
  410. begin
  411. if CompareText(Child.Name, ChildName) = 0 then
  412. begin
  413. if DotPos = 0 then
  414. Result := Child
  415. else
  416. Result := Child.CreateChildren(
  417. Copy(APathName, DotPos + 1, Length(APathName)));
  418. exit;
  419. end;
  420. Child := Child.NextSibling;
  421. end;
  422. // No child found, let's create one
  423. Result := TDocNode.Create(ChildName, nil);
  424. if Assigned(FirstChild) then
  425. begin
  426. Result.FNextSibling := FirstChild;
  427. FFirstChild := Result;
  428. end else
  429. FFirstChild := Result;
  430. if DotPos > 0 then
  431. Result := Result.CreateChildren(
  432. Copy(APathName, DotPos + 1, Length(APathName)));
  433. end;
  434. end;
  435. { TFPDocEngine }
  436. constructor TFPDocEngine.Create;
  437. begin
  438. inherited Create;
  439. DescrDocs := TObjectList.Create;
  440. DescrDocNames := TStringList.Create;
  441. FRootLinkNode := TLinkNode.Create('', '');
  442. FRootDocNode := TDocNode.Create('', nil);
  443. HidePrivate := True;
  444. FPackages := TList.Create;
  445. end;
  446. destructor TFPDocEngine.Destroy;
  447. var
  448. i: Integer;
  449. begin
  450. for i := 0 to FPackages.Count - 1 do
  451. TPasPackage(FPackages[i]).Release;
  452. FPackages.Free;
  453. FRootDocNode.Free;
  454. FRootLinkNode.Free;
  455. DescrDocNames.Free;
  456. DescrDocs.Free;
  457. inherited Destroy;
  458. end;
  459. procedure TFPDocEngine.SetPackageName(const APackageName: String);
  460. begin
  461. ASSERT(not Assigned(Package));
  462. FPackage := TPasPackage(inherited CreateElement(TPasPackage,
  463. '#' + APackageName, nil, '', 0));
  464. FPackages.Add(FPackage);
  465. CurPackageDocNode := RootDocNode.FindChild('#' + APackageName);
  466. end;
  467. procedure TFPDocEngine.ReadContentFile(const AFilename, ALinkPrefix: String);
  468. var
  469. f: Text;
  470. procedure ReadLinkTree;
  471. var
  472. s: String;
  473. PrevSpaces, ThisSpaces, i, StackIndex: Integer;
  474. CurParent, PrevSibling, NewNode: TLinkNode;
  475. ParentStack, SiblingStack: array[0..7] of TLinkNode;
  476. begin
  477. PrevSpaces := 0;
  478. CurParent := RootLinkNode;
  479. PrevSibling := CurParent.FirstChild;
  480. if assigned(PrevSibling) then
  481. while assigned(PrevSibling.NextSibling) do
  482. PrevSibling := PrevSibling.NextSibling;
  483. StackIndex := 0;
  484. while True do
  485. begin
  486. ReadLn(f, s);
  487. if Length(s) = 0 then
  488. break;
  489. ThisSpaces := 0;
  490. while s[ThisSpaces + 1] = ' ' do
  491. Inc(ThisSpaces);
  492. if ThisSpaces <> PrevSpaces then
  493. begin
  494. if ThisSpaces > PrevSpaces then
  495. begin
  496. { Dive down one level }
  497. ParentStack[StackIndex] := CurParent;
  498. SiblingStack[StackIndex] := PrevSibling;
  499. Inc(StackIndex);
  500. CurParent := PrevSibling;
  501. PrevSibling := nil;
  502. end else
  503. while PrevSpaces > ThisSpaces do
  504. begin
  505. Dec(StackIndex);
  506. CurParent := ParentStack[StackIndex];
  507. PrevSibling := SiblingStack[StackIndex];
  508. Dec(PrevSpaces);
  509. end;
  510. PrevSpaces := ThisSpaces;
  511. end;
  512. i := ThisSpaces + 1;
  513. while s[i] <> ' ' do
  514. Inc(i);
  515. NewNode := TLinkNode.Create(Copy(s, ThisSpaces + 1, i - ThisSpaces - 1),
  516. ALinkPrefix + Copy(s, i + 1, Length(s)));
  517. if Assigned(PrevSibling) then
  518. PrevSibling.FNextSibling := NewNode
  519. else
  520. CurParent.FFirstChild := NewNode;
  521. PrevSibling := NewNode;
  522. end;
  523. end;
  524. procedure ReadClasses;
  525. function CreateClass(const AName: String): TPasClassType;
  526. var
  527. DotPos, DotPos2, i: Integer;
  528. s: String;
  529. HPackage: TPasPackage;
  530. Module: TPasModule;
  531. begin
  532. // Find or create package
  533. DotPos := Pos('.', AName);
  534. s := Copy(AName, 1, DotPos - 1);
  535. HPackage := nil;
  536. for i := 0 to FPackages.Count - 1 do
  537. if CompareText(TPasPackage(FPackages[i]).Name, s) = 0 then
  538. begin
  539. HPackage := TPasPackage(FPackages[i]);
  540. break;
  541. end;
  542. if not Assigned(HPackage) then
  543. begin
  544. HPackage := TPasPackage(inherited CreateElement(TPasPackage, s, nil,
  545. '', 0));
  546. FPackages.Add(HPackage);
  547. end;
  548. // Find or create module
  549. DotPos2 := DotPos;
  550. repeat
  551. Inc(DotPos2);
  552. until AName[DotPos2] = '.';
  553. s := Copy(AName, DotPos + 1, DotPos2 - DotPos - 1);
  554. Module := nil;
  555. for i := 0 to HPackage.Modules.Count - 1 do
  556. if CompareText(TPasModule(HPackage.Modules[i]).Name, s) = 0 then
  557. begin
  558. Module := TPasModule(HPackage.Modules[i]);
  559. break;
  560. end;
  561. if not Assigned(Module) then
  562. begin
  563. Module := TPasModule.Create(s, HPackage);
  564. Module.InterfaceSection := TPasSection.Create('', Module);
  565. HPackage.Modules.Add(Module);
  566. end;
  567. // Create node for class
  568. Result := TPasClassType.Create(Copy(AName, DotPos2 + 1, Length(AName)),
  569. Module.InterfaceSection);
  570. Result.ObjKind := okClass;
  571. Module.InterfaceSection.Declarations.Add(Result);
  572. Module.InterfaceSection.Classes.Add(Result);
  573. end;
  574. var
  575. s, Name: String;
  576. CurClass: TPasClassType;
  577. i: Integer;
  578. Member: TPasElement;
  579. begin
  580. CurClass := nil;
  581. while True do
  582. begin
  583. ReadLn(f, s);
  584. if Length(s) = 0 then
  585. break;
  586. if s[1] = '#' then
  587. begin
  588. // New class
  589. i := Pos(' ', s);
  590. CurClass := CreateClass(Copy(s, 1, i - 1));
  591. end else
  592. begin
  593. i := Pos(' ', s);
  594. if i = 0 then
  595. Name := Copy(s, 3, Length(s))
  596. else
  597. Name := Copy(s, 3, i - 3);
  598. case s[2] of
  599. 'M':
  600. Member := TPasProcedure.Create(Name, CurClass);
  601. 'P':
  602. begin
  603. Member := TPasProperty.Create(Name, CurClass);
  604. if i > 0 then
  605. while i <= Length(s) do
  606. begin
  607. case s[i] of
  608. 'r':
  609. TPasProperty(Member).ReadAccessorName := '<dummy>';
  610. 'w':
  611. TPasProperty(Member).WriteAccessorName := '<dummy>';
  612. 's':
  613. TPasProperty(Member).StoredAccessorName := '<dummy>';
  614. end;
  615. Inc(i);
  616. end;
  617. end;
  618. 'V':
  619. Member := TPasVariable.Create(Name, CurClass);
  620. else
  621. raise Exception.Create('Invalid member type: ' + s[2]);
  622. end;
  623. CurClass.Members.Add(Member);
  624. end;
  625. end;
  626. end;
  627. var
  628. s: String;
  629. begin
  630. if not FileExists(AFileName) then
  631. raise EInOutError.Create('File not found: ' + AFileName);
  632. Assign(f, AFilename);
  633. Reset(f);
  634. while not EOF(f) do
  635. begin
  636. ReadLn(f, s);
  637. if (Length(s) = 0) or (s[1] = '#') then
  638. continue;
  639. if s = ':link tree' then
  640. ReadLinkTree
  641. else if s = ':classes' then
  642. ReadClasses
  643. else
  644. repeat
  645. ReadLn(f, s);
  646. until EOF(f) or (Length(s) = 0);
  647. end;
  648. Close(f);
  649. end;
  650. procedure TFPDocEngine.WriteContentFile(const AFilename: String);
  651. var
  652. ContentFile: Text;
  653. procedure ProcessLinkNode(ALinkNode: TLinkNode; const AIdent: String);
  654. var
  655. ChildNode: TLinkNode;
  656. begin
  657. WriteLn(ContentFile, AIdent, ALinkNode.Name, ' ', ALinkNode.Link);
  658. ChildNode := ALinkNode.FirstChild;
  659. while Assigned(ChildNode) do
  660. begin
  661. ProcessLinkNode(ChildNode, AIdent + ' ');
  662. ChildNode := ChildNode.NextSibling;
  663. end;
  664. end;
  665. var
  666. LinkNode: TLinkNode;
  667. i, j, k: Integer;
  668. Module: TPasModule;
  669. ClassDecl: TPasClassType;
  670. Member: TPasElement;
  671. s: String;
  672. begin
  673. Assign(ContentFile, AFilename);
  674. Rewrite(ContentFile);
  675. try
  676. WriteLn(ContentFile, '# FPDoc Content File');
  677. WriteLn(ContentFile, ':link tree');
  678. LinkNode := RootLinkNode.FirstChild;
  679. while Assigned(LinkNode) do
  680. begin
  681. if LinkNode.Name = Package.Name then
  682. begin
  683. ProcessLinkNode(LinkNode, '');
  684. end;
  685. LinkNode := LinkNode.NextSibling;
  686. end;
  687. if Assigned(Package) then
  688. begin
  689. WriteLn(ContentFile);
  690. WriteLn(ContentFile, ':classes');
  691. for i := 0 to Package.Modules.Count - 1 do
  692. begin
  693. Module := TPasModule(Package.Modules[i]);
  694. for j := 0 to Module.InterfaceSection.Classes.Count - 1 do
  695. begin
  696. ClassDecl := TPasClassType(Module.InterfaceSection.Classes[j]);
  697. Write(ContentFile, ClassDecl.PathName, ' ');
  698. if Assigned(ClassDecl.AncestorType) then
  699. WriteLn(ContentFile, ClassDecl.AncestorType.PathName)
  700. else if ClassDecl.ObjKind = okClass then
  701. WriteLn(ContentFile, '.TObject');
  702. for k := 0 to ClassDecl.Members.Count - 1 do
  703. begin
  704. Member := TPasElement(ClassDecl.Members[k]);
  705. Write(ContentFile, Chr(Ord(Member.Visibility) + Ord('0')));
  706. SetLength(s, 0);
  707. if Member.ClassType = TPasVariable then
  708. Write(ContentFile, 'V')
  709. else if Member.ClassType = TPasProperty then
  710. begin
  711. Write(ContentFile, 'P');
  712. if Length(TPasProperty(Member).ReadAccessorName) > 0 then
  713. s := s + 'r';
  714. if Length(TPasProperty(Member).WriteAccessorName) > 0 then
  715. s := s + 'w';
  716. if Length(TPasProperty(Member).StoredAccessorName) > 0 then
  717. s := s + 's';
  718. end else
  719. Write(ContentFile, 'M'); // Member must be a method
  720. Write(ContentFile, Member.Name);
  721. if Length(s) > 0 then
  722. WriteLn(ContentFile, ' ', s)
  723. else
  724. WriteLn(ContentFile);
  725. end;
  726. end;
  727. end;
  728. end;
  729. finally
  730. Close(ContentFile);
  731. end;
  732. end;
  733. function TFPDocEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  734. AParent: TPasElement; AVisibility: TPasMemberVisibility;
  735. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  736. begin
  737. Result := AClass.Create(AName, AParent);
  738. Result.Visibility := AVisibility;
  739. if AClass.InheritsFrom(TPasModule) then
  740. CurModule := TPasModule(Result);
  741. Result.SourceFilename := ASourceFilename;
  742. Result.SourceLinenumber := ASourceLinenumber;
  743. end;
  744. function TFPDocEngine.FindElement(const AName: String): TPasElement;
  745. function FindInModule(AModule: TPasModule; const LocalName: String): TPasElement;
  746. var
  747. l: TList;
  748. i: Integer;
  749. begin
  750. l := AModule.InterfaceSection.Declarations;
  751. for i := 0 to l.Count - 1 do
  752. begin
  753. Result := TPasElement(l[i]);
  754. if CompareText(Result.Name, LocalName) = 0 then
  755. exit;
  756. end;
  757. Result := nil;
  758. end;
  759. var
  760. i: Integer;
  761. //ModuleName, LocalName: String;
  762. Module: TPasElement;
  763. begin
  764. {!!!: Don't know if we ever will have to use the following:
  765. i := Pos('.', AName);
  766. if i <> 0 then
  767. begin
  768. WriteLn('Dot found in name: ', AName);
  769. Result := nil;
  770. end else
  771. begin}
  772. Result := FindInModule(CurModule, AName);
  773. if not Assigned(Result) then
  774. for i := CurModule.InterfaceSection.UsesList.Count - 1 downto 0 do
  775. begin
  776. Module := TPasElement(CurModule.InterfaceSection.UsesList[i]);
  777. if Module.ClassType = TPasModule then
  778. begin
  779. Result := FindInModule(TPasModule(Module), AName);
  780. if Assigned(Result) then
  781. exit;
  782. end;
  783. end;
  784. {end;}
  785. end;
  786. function TFPDocEngine.FindModule(const AName: String): TPasModule;
  787. function FindInPackage(APackage: TPasPackage): TPasModule;
  788. var
  789. i: Integer;
  790. begin
  791. for i := 0 to APackage.Modules.Count - 1 do
  792. begin
  793. Result := TPasModule(APackage.Modules[i]);
  794. if CompareText(Result.Name, AName) = 0 then
  795. exit;
  796. end;
  797. Result := nil;
  798. end;
  799. var
  800. i: Integer;
  801. begin
  802. Result := FindInPackage(Package);
  803. if not Assigned(Result) then
  804. for i := FPackages.Count - 1 downto 0 do
  805. begin
  806. if TPasPackage(FPackages[i]) = Package then
  807. continue;
  808. Result := FindInPackage(TPasPackage(FPackages[i]));
  809. if Assigned(Result) then
  810. exit;
  811. end;
  812. end;
  813. procedure TFPDocEngine.AddLink(const APathName, ALinkTo: String);
  814. begin
  815. RootLinkNode.CreateChildren(APathName, ALinkTo);
  816. end;
  817. function TFPDocEngine.FindAbsoluteLink(const AName: String): String;
  818. var
  819. LinkNode: TLinkNode;
  820. begin
  821. LinkNode := RootLinkNode.FindChild(AName);
  822. if Assigned(LinkNode) then
  823. Result := LinkNode.Link
  824. else
  825. SetLength(Result, 0);
  826. end;
  827. function TFPDocEngine.ResolveLink(AModule: TPasModule;
  828. const ALinkDest: String): String;
  829. var
  830. i: Integer;
  831. ThisPackage: TLinkNode;
  832. UnitList: TList;
  833. begin
  834. //WriteLn('ResolveLink(', ALinkDest, ')... ');
  835. if Length(ALinkDest) = 0 then
  836. begin
  837. SetLength(Result, 0);
  838. exit;
  839. end;
  840. if (ALinkDest[1] = '#') or (not assigned(AModule)) then
  841. Result := FindAbsoluteLink(ALinkDest)
  842. else
  843. begin
  844. Result := ResolveLink(AModule, AModule.PathName + '.' + ALinkDest);
  845. if Length(Result) > 0 then
  846. exit;
  847. { Try all packages }
  848. SetLength(Result, 0);
  849. ThisPackage := RootLinkNode.FirstChild;
  850. while Assigned(ThisPackage) do
  851. begin
  852. Result := ResolveLink(AModule, ThisPackage.Name + '.' + ALinkDest);
  853. if Length(Result) > 0 then
  854. exit;
  855. ThisPackage := ThisPackage.NextSibling;
  856. end;
  857. if Length(Result) = 0 then
  858. begin
  859. { Okay, then we have to try all imported units of the current module }
  860. UnitList := AModule.InterfaceSection.UsesList;
  861. for i := UnitList.Count - 1 downto 0 do
  862. begin
  863. { Try all packages }
  864. ThisPackage := RootLinkNode.FirstChild;
  865. while Assigned(ThisPackage) do
  866. begin
  867. Result := ResolveLink(AModule, ThisPackage.Name + '.' +
  868. TPasType(UnitList[i]).Name + '.' + ALinkDest);
  869. if Length(Result) > 0 then
  870. exit;
  871. ThisPackage := ThisPackage.NextSibling;
  872. end;
  873. end;
  874. end;
  875. end;
  876. if Length(Result) = 0 then
  877. for i := Length(ALinkDest) downto 1 do
  878. if ALinkDest[i] = '.' then
  879. begin
  880. Result := ResolveLink(AModule, Copy(ALinkDest, 1, i - 1));
  881. exit;
  882. end;
  883. end;
  884. procedure TFPDocEngine.AddDocFile(const AFilename: String);
  885. function ReadNode(OwnerDocNode: TDocNode; Element: TDOMElement): TDocNode;
  886. var
  887. Subnode: TDOMNode;
  888. begin
  889. if OwnerDocNode = RootDocNode then
  890. Result := OwnerDocNode.CreateChildren('#' + Element['name'])
  891. else
  892. Result := OwnerDocNode.CreateChildren(Element['name']);
  893. Result.FNode := Element;
  894. Result.FLink := Element['link'];
  895. Result.FIsSkipped := Element['skip'] = '1';
  896. Subnode := Element.FirstChild;
  897. while Assigned(Subnode) do
  898. begin
  899. if Subnode.NodeType = ELEMENT_NODE then
  900. begin
  901. if Subnode.NodeName = 'short' then
  902. Result.FShortDescr := TDOMElement(Subnode)
  903. else if Subnode.NodeName = 'descr' then
  904. Result.FDescr := TDOMElement(Subnode)
  905. else if Subnode.NodeName = 'errors' then
  906. Result.FErrorsDoc := TDOMElement(Subnode)
  907. else if Subnode.NodeName = 'seealso' then
  908. Result.FSeeAlso := TDOMElement(Subnode)
  909. else if (Subnode.NodeName = 'example') and
  910. not Assigned(Result.FirstExample) then
  911. Result.FFirstExample := TDOMElement(Subnode);
  912. end;
  913. Subnode := Subnode.NextSibling;
  914. end;
  915. end;
  916. Procedure ReadTopics(TopicNode : TDocNode);
  917. Var
  918. SubNode : TDOMNode;
  919. begin
  920. SubNode:=TopicNode.FNode.FirstChilD;
  921. While Assigned(SubNode) do
  922. begin
  923. If (SubNode.NodeType=ELEMENT_NODE) and (SubNode.NodeName='topic') then
  924. With ReadNode(TopicNode,TDomElement(SubNode)) do
  925. // We could allow recursion here, but we won't, because it doesn't work on paper.
  926. FTopicNode:=True;
  927. SubNode:=Subnode.NextSibling;
  928. end;
  929. end;
  930. var
  931. i: Integer;
  932. Node, Subnode, Subsubnode: TDOMNode;
  933. Element: TDOMElement;
  934. Doc: TXMLDocument;
  935. PackageDocNode, TopicNode,ModuleDocNode: TDocNode;
  936. begin
  937. ReadXMLFile(Doc, AFilename);
  938. DescrDocs.Add(Doc);
  939. DescrDocNames.Add(AFilename);
  940. Node := Doc.DocumentElement.FirstChild;
  941. while Assigned(Node) do
  942. begin
  943. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'package') then
  944. begin
  945. PackageDocNode := ReadNode(RootDocNode, TDOMElement(Node));
  946. // Scan all 'module' elements within this package element
  947. Subnode := Node.FirstChild;
  948. while Assigned(Subnode) do
  949. begin
  950. if (Subnode.NodeType = ELEMENT_NODE) then
  951. begin
  952. If (Subnode.NodeName = 'module') then
  953. begin
  954. ModuleDocNode := ReadNode(PackageDocNode, TDOMElement(Subnode));
  955. // Scan all 'element' elements within this module element
  956. Subsubnode := Subnode.FirstChild;
  957. while Assigned(Subsubnode) do
  958. begin
  959. if (Subsubnode.NodeType = ELEMENT_NODE) then
  960. begin
  961. if (Subsubnode.NodeName = 'element') then
  962. ReadNode(ModuleDocNode, TDOMElement(Subsubnode))
  963. else if (SubSubNode.NodeName='topic') then
  964. begin
  965. TopicNode:=ReadNode(ModuleDocNode,TDomElement(SubSubNode));
  966. TopicNode.FTopicNode:=True;
  967. ReadTopics(TopicNode);
  968. end;
  969. end;
  970. Subsubnode := Subsubnode.NextSibling;
  971. end;
  972. end
  973. else if (SubNode.NodeName='topic') then
  974. begin
  975. TopicNode:=ReadNode(PackageDocNode,TDomElement(SubNode));
  976. TopicNode.FTopicNode:=True;
  977. ReadTopics(TopicNode);
  978. end;
  979. end;
  980. Subnode := Subnode.NextSibling;
  981. end;
  982. end;
  983. Node := Node.NextSibling;
  984. end;
  985. end;
  986. function TFPDocEngine.FindDocNode(AElement: TPasElement): TDocNode;
  987. begin
  988. Result:=Nil;
  989. If Assigned(AElement) then
  990. begin
  991. if AElement.InheritsFrom(TPasUnresolvedTypeRef) then
  992. Result := FindDocNode(AElement.GetModule, AElement.Name)
  993. else
  994. Result := RootDocNode.FindChild(AElement.PathName);
  995. if (Result=Nil) and
  996. WarnNoNode and
  997. (Length(AElement.PathName)>0) and
  998. (AElement.PathName[1]='#') then
  999. Writeln('No documentation node found for identifier : ',AElement.PathName);
  1000. end;
  1001. end;
  1002. function TFPDocEngine.FindDocNode(ARefModule: TPasModule;
  1003. const AName: String): TDocNode;
  1004. var
  1005. CurPackage: TDocNode;
  1006. UnitList: TList;
  1007. i: Integer;
  1008. begin
  1009. if Length(AName) = 0 then
  1010. Result := nil
  1011. else
  1012. begin
  1013. if AName[1] = '#' then
  1014. Result := RootDocNode.FindChild(AName)
  1015. else
  1016. Result := RootDocNode.FindChild(Package.Name + '.' + AName);
  1017. if (not Assigned(Result)) and Assigned(ARefModule) then
  1018. Result := RootDocNode.FindChild(ARefModule.PathName + '.' + AName);
  1019. if (not Assigned(Result)) and (AName[1] <> '#') then
  1020. begin
  1021. CurPackage := RootDocNode.FirstChild;
  1022. while Assigned(CurPackage) do
  1023. begin
  1024. Result := RootDocNode.FindChild(CurPackage.Name + '.' + AName);
  1025. if Assigned(Result) then
  1026. break;
  1027. CurPackage := CurPackage.NextSibling;
  1028. end;
  1029. if not Assigned(Result) then
  1030. begin
  1031. { Okay, then we have to try all imported units of the current module }
  1032. UnitList := CurModule.InterfaceSection.UsesList;
  1033. for i := UnitList.Count - 1 downto 0 do
  1034. begin
  1035. { Try all packages }
  1036. CurPackage := RootDocNode.FirstChild;
  1037. while Assigned(CurPackage) do
  1038. begin
  1039. Result := RootDocNode.FindChild(CurPackage.Name + '.' +
  1040. TPasType(UnitList[i]).Name + '.' + AName);
  1041. if Assigned(Result) then
  1042. break;
  1043. CurPackage := CurPackage.NextSibling;
  1044. end;
  1045. end;
  1046. end;
  1047. end;
  1048. end;
  1049. end;
  1050. function TFPDocEngine.FindShortDescr(AElement: TPasElement): TDOMElement;
  1051. var
  1052. DocNode: TDocNode;
  1053. begin
  1054. DocNode := FindDocNode(AElement);
  1055. if Assigned(DocNode) then
  1056. Result := DocNode.ShortDescr
  1057. else
  1058. Result := nil;
  1059. end;
  1060. function TFPDocEngine.FindShortDescr(ARefModule: TPasModule;
  1061. const AName: String): TDOMElement;
  1062. var
  1063. DocNode: TDocNode;
  1064. begin
  1065. DocNode := FindDocNode(ARefModule, AName);
  1066. if Assigned(DocNode) then
  1067. Result := DocNode.ShortDescr
  1068. else
  1069. Result := nil;
  1070. end;
  1071. function TFPDocEngine.GetExampleFilename(const ExElement: TDOMElement): String;
  1072. var
  1073. i: Integer;
  1074. fn : String;
  1075. begin
  1076. Result:='';
  1077. for i := 0 to DescrDocs.Count - 1 do
  1078. begin
  1079. Fn:=ExElement['file'];
  1080. if (FN<>'') and (TDOMDocument(DescrDocs[i]) = ExElement.OwnerDocument) then
  1081. begin
  1082. Result := ExtractFilePath(DescrDocNames[i]) + FN;
  1083. if (ExtractFileExt(Result)='') then
  1084. Result:=Result+'.pp';
  1085. end;
  1086. end;
  1087. end;
  1088. { Global helpers }
  1089. procedure TranslateDocStrings(const Lang: String);
  1090. var
  1091. mo: TMOFile;
  1092. begin
  1093. {$IFDEF Unix}
  1094. mo := TMOFile.Create(Format('/usr/local/share/locale/%s/LC_MESSAGES/dglobals.mo', [Lang]));
  1095. {$ELSE}
  1096. mo := TMOFile.Create(Format('intl/dglobals.%s.mo', [Lang]));
  1097. {$ENDIF}
  1098. try
  1099. TranslateResourceStrings(mo);
  1100. finally
  1101. mo.Free;
  1102. end;
  1103. end;
  1104. Function IsLinkNode(Node : TDomNode) : Boolean;
  1105. begin
  1106. Result:=Assigned(Node) and (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link');
  1107. end;
  1108. Function IsExampleNode(Example : TDomNode) : Boolean;
  1109. begin
  1110. Result:=Assigned(Example) and (Example.NodeType = ELEMENT_NODE) and (Example.NodeName = 'example')
  1111. end;
  1112. function IsLinkAbsolute(ALink: String): boolean;
  1113. var
  1114. i: integer;
  1115. begin
  1116. Result := false;
  1117. for i := low(AbsoluteLinkPrefixes) to high(AbsoluteLinkPrefixes) do
  1118. if CompareText(AbsoluteLinkPrefixes[i], copy(ALink,1,length(AbsoluteLinkPrefixes[i])))=0 then begin
  1119. Result := true;
  1120. break;
  1121. end;
  1122. end;
  1123. initialization
  1124. LEOL:=Length(LineEnding);
  1125. end.