dglobals.pp 36 KB

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