dglobals.pp 37 KB

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