dglobals.pp 36 KB

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