dglobals.pp 35 KB

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