dwriter.pp 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140
  1. {
  2. FPDoc - Free Pascal Documentation Tool
  3. Copyright (C) 2000 - 2003 by
  4. Areca Systems GmbH / Sebastian Guenther, [email protected]
  5. 2005-2012 by
  6. various FPC contributors
  7. * Output string definitions
  8. * Basic writer (output generator) class
  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. unit dWriter;
  16. {$MODE objfpc}
  17. {$H+}
  18. {$WARN 5024 off : Parameter "$1" not used}
  19. interface
  20. uses Classes, DOM, contnrs, dGlobals, PasTree, SysUtils, fpdocclasstree;
  21. type
  22. // Phony element for pas pages.
  23. TTopicElement = Class(TPaselement)
  24. TopicNode : TDocNode;
  25. Previous,
  26. Next : TPasElement;
  27. Subtopics : TList;
  28. Constructor Create(const AName: String; AParent: TPasElement); override;
  29. Destructor Destroy; override;
  30. end;
  31. { TFileAllocator }
  32. TFileAllocator = class
  33. private
  34. FSubPageNames: Boolean;
  35. protected
  36. function GetFilePostfix(ASubindex: Integer):String;
  37. public
  38. procedure Create(); overload;
  39. procedure AllocFilename(AElement: TPasElement; ASubindex: Integer); virtual;
  40. function GetFilename(AElement: TPasElement;
  41. ASubindex: Integer): String; virtual; abstract;
  42. function GetRelativePathToTop(AElement: TPasElement): String; virtual;
  43. function GetCSSFilename(ARelativeTo: TPasElement): DOMString; virtual;
  44. property SubPageNames: Boolean read FSubPageNames write FSubPageNames;
  45. end;
  46. TLongNameFileAllocator = class(TFileAllocator)
  47. private
  48. FExtension: String;
  49. public
  50. constructor Create(const AExtension: String);
  51. function GetFilename(AElement: TPasElement; ASubindex: Integer): String; override;
  52. function GetRelativePathToTop(AElement: TPasElement): String; override;
  53. property Extension: String read FExtension;
  54. end;
  55. TWriterLogEvent = Procedure(Sender : TObject; Const Msg : String) of object;
  56. TWriterNoteEvent = Procedure(Sender : TObject; Note : TDomElement; Var EmitNote : Boolean) of object;
  57. { TFPDocWriter }
  58. TFPDocWriter = class
  59. private
  60. FEmitNotes: Boolean;
  61. FEngine : TFPDocEngine;
  62. FPackage : TPasPackage;
  63. FContext : TPasElement;
  64. FTopics : TList;
  65. FImgExt : String;
  66. FBeforeEmitNote : TWriterNoteEvent;
  67. procedure ConvertURL(AContext: TPasElement; El: TDOMElement);
  68. procedure CreateClassTree;
  69. protected
  70. TreeClass: TClassTreeBuilder; // Global class tree
  71. TreeInterface: TClassTreeBuilder; // Global interface tree
  72. procedure AddElementsFromList(L: TStrings; List: TFPList; UsePathName : Boolean = False);
  73. Procedure DoLog(Const Msg : String);
  74. Procedure DoLog(Const Fmt : String; Args : Array of const);
  75. Procedure OutputResults(); virtual;
  76. procedure Warning(AContext: TPasElement; const AMsg: String);
  77. procedure Warning(AContext: TPasElement; const AMsg: String;
  78. const Args: array of const);
  79. // function FindShortDescr(const Name: String): TDOMElement;
  80. // Description conversion
  81. function IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  82. function IsExtShort(Node: TDOMNode): Boolean;
  83. function ConvertShort(AContext: TPasElement; El: TDOMElement): Boolean;
  84. function ConvertNotes(AContext: TPasElement; El: TDOMElement): Boolean; virtual;
  85. function ConvertBaseShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  86. procedure ConvertBaseShortList(AContext: TPasElement; Node: TDOMNode;
  87. MayBeEmpty: Boolean);
  88. procedure ConvertLink(AContext: TPasElement; El: TDOMElement);
  89. function ConvertExtShort(AContext: TPasElement; Node: TDOMNode): Boolean;
  90. procedure ConvertDescr(AContext: TPasElement; El: TDOMElement;
  91. AutoInsertBlock: Boolean);
  92. function ConvertNonSectionBlock(AContext: TPasElement;
  93. Node: TDOMNode): Boolean;
  94. procedure ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  95. Node: TDOMNode);
  96. function ConvertSimpleBlock(AContext: TPasElement; Node: TDOMNode): Boolean;
  97. Function FindTopicElement(Node : TDocNode): TTopicElement;
  98. Procedure ConvertImage(El : TDomElement);
  99. Procedure DescrEmitNotesHeader(AContext : TPasElement); virtual;
  100. Procedure DescrEmitNotesFooter(AContext : TPasElement); virtual;
  101. procedure DescrWriteText(const AText: DOMString); virtual; abstract;
  102. procedure DescrBeginBold; virtual; abstract;
  103. procedure DescrEndBold; virtual; abstract;
  104. procedure DescrBeginItalic; virtual; abstract;
  105. procedure DescrEndItalic; virtual; abstract;
  106. procedure DescrBeginUnderline; virtual; abstract;
  107. procedure DescrEndUnderline; virtual; abstract;
  108. procedure DescrBeginEmph; virtual; abstract;
  109. procedure DescrEndEmph; virtual; abstract;
  110. procedure DescrWriteImageEl(const AFileName, ACaption,ALinkName : DOMString); virtual;
  111. procedure DescrWriteFileEl(const AText: DOMString); virtual; abstract;
  112. procedure DescrWriteKeywordEl(const AText: DOMString); virtual; abstract;
  113. procedure DescrWriteVarEl(const AText: DOMString); virtual; abstract;
  114. procedure DescrBeginLink(const AId: DOMString); virtual; abstract;
  115. procedure DescrEndLink; virtual; abstract;
  116. procedure DescrBeginURL(const AURL: DOMString); virtual; abstract;
  117. procedure DescrEndURL; virtual; abstract;
  118. procedure DescrWriteLinebreak; virtual; abstract;
  119. procedure DescrBeginParagraph; virtual; abstract;
  120. procedure DescrEndParagraph; virtual; abstract;
  121. procedure DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String); virtual; abstract;
  122. procedure DescrWriteCodeLine(const ALine: String); virtual; abstract;
  123. procedure DescrEndCode; virtual; abstract;
  124. procedure DescrBeginOrderedList; virtual; abstract;
  125. procedure DescrEndOrderedList; virtual; abstract;
  126. procedure DescrBeginUnorderedList; virtual; abstract;
  127. procedure DescrEndUnorderedList; virtual; abstract;
  128. procedure DescrBeginDefinitionList; virtual; abstract;
  129. procedure DescrEndDefinitionList; virtual; abstract;
  130. procedure DescrBeginListItem; virtual; abstract;
  131. procedure DescrEndListItem; virtual; abstract;
  132. procedure DescrBeginDefinitionTerm; virtual; abstract;
  133. procedure DescrEndDefinitionTerm; virtual; abstract;
  134. procedure DescrBeginDefinitionEntry; virtual; abstract;
  135. procedure DescrEndDefinitionEntry; virtual; abstract;
  136. procedure DescrBeginSectionTitle; virtual; abstract;
  137. procedure DescrBeginSectionBody; virtual; abstract;
  138. procedure DescrEndSection; virtual; abstract;
  139. procedure DescrBeginRemark; virtual; abstract;
  140. procedure DescrEndRemark; virtual; abstract;
  141. procedure DescrBeginTable(ColCount: Integer; HasBorder: Boolean); virtual; abstract;
  142. procedure DescrEndTable; virtual; abstract;
  143. procedure DescrBeginTableCaption; virtual; abstract;
  144. procedure DescrEndTableCaption; virtual; abstract;
  145. procedure DescrBeginTableHeadRow; virtual; abstract;
  146. procedure DescrEndTableHeadRow; virtual; abstract;
  147. procedure DescrBeginTableRow; virtual; abstract;
  148. procedure DescrEndTableRow; virtual; abstract;
  149. procedure DescrBeginTableCell; virtual; abstract;
  150. procedure DescrEndTableCell; virtual; abstract;
  151. procedure PrepareDocumentation; virtual;
  152. // Descendents must override this.
  153. procedure DoWriteDocumentation; virtual; Abstract;
  154. Property CurrentContext : TPasElement Read FContext ;
  155. public
  156. Constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); virtual;
  157. destructor Destroy; override;
  158. procedure AddModuleIdentifiers(AModule: TPasModule; L: TStrings);
  159. property Engine : TFPDocEngine read FEngine;
  160. Property Package : TPasPackage read FPackage;
  161. Property Topics : TList Read FTopics;
  162. Property ImageExtension : String Read FImgExt Write FImgExt;
  163. // Should return True if option was succesfully interpreted.
  164. Function InterpretOption(Const Cmd,Arg : String) : Boolean; Virtual;
  165. Class Function FileNameExtension : String; virtual;
  166. Class Procedure Usage(List : TStrings); virtual;
  167. Class procedure SplitImport(var AFilename, ALinkPrefix: String); virtual;
  168. // Here we start the generation of documentation
  169. procedure WriteDocumentation;
  170. Function WriteDescr(Element: TPasElement) : TDocNode;
  171. procedure WriteDescr(Element: TPasElement; DocNode: TDocNode);
  172. procedure WriteDescr(AContext: TPasElement; DescrNode: TDOMElement); virtual;
  173. Procedure FPDocError(Msg : String);
  174. Procedure FPDocError(Fmt : String; Args : Array of Const);
  175. Function ShowMember(M : TPasElement) : boolean;
  176. Procedure GetMethodList(ClassDecl: TPasClassType; List : TStringList);
  177. Property EmitNotes : Boolean Read FEmitNotes Write FEmitNotes;
  178. Property BeforeEmitNote : TWriterNoteEvent Read FBeforeEmitNote Write FBeforeEmitNote;
  179. end;
  180. const
  181. // The Multi-Page doc writer identifies each page by it's index.
  182. IdentifierIndex = 0;
  183. // Subpage indices for modules
  184. ResstrSubindex = 1;
  185. ConstsSubindex = 2;
  186. TypesSubindex = 3;
  187. ClassesSubindex = 4;
  188. ProcsSubindex = 5;
  189. VarsSubindex = 6;
  190. // Maybe needed later for topic overview ??
  191. TopicsSubIndex = 7;
  192. IndexSubIndex = 8;
  193. ClassHierarchySubIndex = 9;
  194. InterfaceHierarchySubIndex = 10;
  195. // Subpage indices for classes
  196. PropertiesByInheritanceSubindex = 11;
  197. PropertiesByNameSubindex = 12;
  198. MethodsByInheritanceSubindex = 13;
  199. MethodsByNameSubindex = 14;
  200. EventsByInheritanceSubindex = 15;
  201. EventsByNameSubindex = 16;
  202. Type
  203. { TMultiFileDocWriter }
  204. { TPageInfo }
  205. TPageInfo = class
  206. Public
  207. Element: TPasElement;
  208. SubpageIndex: Integer;
  209. Constructor Create(aElement : TPasElement; aIndex : Integer);
  210. end;
  211. { TLinkData }
  212. TLinkData = Class(TObject)
  213. FPathName,
  214. FLink,
  215. FModuleName : String;
  216. Constructor Create(Const APathName,ALink,AModuleName : string);
  217. end;
  218. TMultiFileDocWriter = Class(TFPDocWriter)
  219. Private
  220. FSubPageNames: Boolean;
  221. FBaseDirectory: String;
  222. FCurDirectory: String;
  223. FModule: TPasModule;
  224. FPageInfos: TFPObjectList; // list of TPageInfo objects
  225. FLinkUnresolvedCnt: Integer;
  226. FOutputPageNames: TStringList;
  227. function GetOutputPageNames: TStrings;
  228. function GetPageCount: Integer;
  229. function LinkFix(ALink:String):String;
  230. Protected
  231. FAllocator: TFileAllocator;
  232. Procedure LinkUnresolvedInc();
  233. // General resolving routine
  234. function ResolveLinkID(const Name: String): DOMString;
  235. // Simplified resolving routine. Excluded last path after dot
  236. function ResolveLinkIDUnStrict(const Name: String): DOMString;
  237. function ResolveLinkIDInUnit(const Name,AUnitName: String): DOMString;
  238. function ResolveLinkWithinPackage(AElement: TPasElement; ASubpageIndex: Integer): String;
  239. procedure PrepareDocumentation; override;
  240. function CreateAllocator() : TFileAllocator; virtual; abstract;
  241. Procedure OutputResults(); override;
  242. // aFileName is the filename allocated by the Allocator, nothing prefixed.
  243. procedure WriteDocPage(const aFileName: String; aElement: TPasElement; aSubPageIndex: Integer); virtual; abstract;
  244. procedure AllocatePages; virtual;
  245. // Default page allocation mechanism.
  246. function AddPage(AElement: TPasElement; ASubpageIndex: Integer): TPageInfo; virtual;
  247. procedure AddPages(AElement: TPasElement; ASubpageIndex: Integer; AList: TFPList); virtual;
  248. procedure AddTopicPages(AElement: TPasElement); virtual;
  249. procedure AllocateClassMemberPages(AModule: TPasModule; LinkList: TObjectList); virtual;
  250. procedure AllocateModulePages(AModule: TPasModule; LinkList: TObjectList); virtual;
  251. procedure AllocatePackagePages; virtual;
  252. // Prefix every filename generated with the result of this.
  253. function GetFileBaseDir(aOutput: String): String; virtual;
  254. function InterPretOption(const Cmd, Arg: String): boolean; override;
  255. function ModuleHasClasses(AModule: TPasModule): Boolean;
  256. // Allocate pages etc.
  257. Procedure DoWriteDocumentation; override;
  258. Function MustGeneratePage(aFileName : String) : Boolean; virtual;
  259. Property PageInfos : TFPObjectList Read FPageInfos;
  260. Property SubPageNames: Boolean Read FSubPageNames;
  261. Public
  262. constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); override;
  263. Destructor Destroy; override;
  264. class procedure Usage(List: TStrings); override;
  265. property PageCount: Integer read GetPageCount;
  266. Property Allocator : TFileAllocator Read FAllocator;
  267. Property Module: TPasModule Read FModule Write FModule;
  268. Property CurDirectory: String Read FCurDirectory Write FCurDirectory; // relative to curdir of process
  269. property BaseDirectory: String read FBaseDirectory Write FBaseDirectory; // relative path to package base directory
  270. Property OutputPageNames : TStrings Read GetOutputPageNames;
  271. end;
  272. TFPDocWriterClass = Class of TFPDocWriter;
  273. EFPDocWriterError = Class(Exception);
  274. // Member Filter Callback type
  275. TMemberFilter = function(AMember: TPasElement): Boolean;
  276. // Filter Callbacks
  277. function PropertyFilter(AMember: TPasElement): Boolean;
  278. function MethodFilter(AMember: TPasElement): Boolean;
  279. function EventFilter(AMember: TPasElement): Boolean;
  280. // Register backend
  281. Procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName,ADescr : String);
  282. // UnRegister backend
  283. Procedure UnRegisterWriter(Const AName : String);
  284. // Return back end class. Exception if not found.
  285. Function GetWriterClass(AName : String) : TFPDocWriterClass;
  286. // Return index of back end class.
  287. Function FindWriterClass(AName : String) : Integer;
  288. // List of backend in name=descr form.
  289. Procedure EnumWriters(List : TStrings);
  290. // Sort elements on name
  291. function SortPasElements(Item1, Item2: Pointer): Integer;
  292. implementation
  293. uses strutils, fpdocstrs;
  294. function SortPasElements(Item1, Item2: Pointer): Integer;
  295. begin
  296. Result:=CompareText(TPasElement(Item1).Name,TPasElement(Item2).Name)
  297. end;
  298. { ---------------------------------------------------------------------
  299. Filter callbacks
  300. ---------------------------------------------------------------------}
  301. function PropertyFilter(AMember: TPasElement): Boolean;
  302. begin
  303. Result := (AMember.ClassType = TPasProperty) and
  304. (Copy(AMember.Name, 1, 2) <> 'On');
  305. end;
  306. function MethodFilter(AMember: TPasElement): Boolean;
  307. begin
  308. Result := AMember.InheritsFrom(TPasProcedureBase);
  309. // Writeln(aMember.Name,' (',aMember.ClassName,') is Method ',Result);
  310. end;
  311. function EventFilter(AMember: TPasElement): Boolean;
  312. begin
  313. Result := (AMember.ClassType = TPasProperty) and
  314. (Copy(AMember.Name, 1, 2) = 'On');
  315. end;
  316. { ---------------------------------------------------------------------
  317. Writer registration
  318. ---------------------------------------------------------------------}
  319. Type
  320. { TWriterRecord }
  321. TWriterRecord = Class(TObject)
  322. Private
  323. FClass : TFPDocWriterClass;
  324. FName : String;
  325. FDescription : String;
  326. Public
  327. Constructor Create (AClass : TFPDocWriterClass; Const AName,ADescr : String);
  328. end;
  329. { TPageInfo }
  330. constructor TPageInfo.Create(aElement: TPasElement; aIndex: Integer);
  331. begin
  332. Element:=aELement;
  333. SubpageIndex:=aIndex;
  334. end;
  335. { TLinkData }
  336. constructor TLinkData.Create(Const APathName, ALink, AModuleName: string);
  337. begin
  338. FPathName:=APathName;
  339. FLink:=ALink;
  340. FModuleName:=AModuleName;
  341. end;
  342. { TMultiFileDocWriter }
  343. constructor TMultiFileDocWriter.Create(APackage: TPasPackage;
  344. AEngine: TFPDocEngine);
  345. begin
  346. inherited Create(APackage, AEngine);
  347. FPageInfos:=TFPObjectList.Create;
  348. FSubPageNames:= False;
  349. FLinkUnresolvedCnt:=0;
  350. end;
  351. destructor TMultiFileDocWriter.Destroy;
  352. begin
  353. FreeAndNil(FPageInfos);
  354. FreeAndNil(FAllocator);
  355. inherited Destroy;
  356. end;
  357. function TMultiFileDocWriter.GetPageCount: Integer;
  358. begin
  359. Result := PageInfos.Count;
  360. end;
  361. function TMultiFileDocWriter.GetOutputPageNames: TStrings;
  362. begin
  363. If (FoutputPageNames=Nil) then
  364. begin
  365. FOutputPageNames:=TStringList.Create;
  366. FOutputPageNames.Sorted:=True;
  367. end;
  368. Result:=FOutputPageNames;
  369. end;
  370. procedure TMultiFileDocWriter.OutputResults();
  371. begin
  372. DoLog('Unresolved links: %d', [FLinkUnresolvedCnt]);
  373. inherited OutputResults();
  374. end;
  375. procedure TMultiFileDocWriter.LinkUnresolvedInc();
  376. begin
  377. Inc(FLinkUnresolvedCnt);
  378. end;
  379. function TMultiFileDocWriter.ResolveLinkID(const Name: String): DOMString;
  380. var
  381. res: String;
  382. begin
  383. res:=Engine.ResolveLink(Module,Name, True);
  384. // engine can return backslashes on Windows
  385. res:= LinkFix(res);
  386. Result:=UTF8Decode(res);
  387. end;
  388. function TMultiFileDocWriter.ResolveLinkIDUnStrict(const Name: String
  389. ): DOMString;
  390. var
  391. idDot, idLast: Integer;
  392. res: String;
  393. begin
  394. res:=Engine.ResolveLink(Module,Name, True);
  395. if res = '' then
  396. begin
  397. // do simplify on one level from end.
  398. // TOCO: I want to move that code to last check of Engine.ResolveLink() for not Strict
  399. IdDot:= Pos('.', Name);
  400. IdLast:= 0;
  401. // search last dot
  402. while idDot > 0 do
  403. begin
  404. IdLast:= idDot;
  405. IdDot:= Pos('.', Name, IdLast+1);
  406. end;
  407. if idLast > 0 then
  408. // have cut last element
  409. res:= Engine.ResolveLink(Module, Copy(Name, 1, IdLast-1), True);
  410. end;
  411. res:= LinkFix(res);
  412. Result:=UTF8Decode(res);
  413. end;
  414. function TMultiFileDocWriter.LinkFix(ALink: String): String;
  415. var
  416. res, s:String;
  417. begin
  418. res:= ALink;
  419. if Length(res) > 0 then
  420. begin
  421. // If the link is in the same directory as current dir, then remove the directory part.
  422. s:=Copy(res, 1, Length(CurDirectory) + 1);
  423. if (S= CurDirectory + '/') or (s= CurDirectory + '\') then
  424. res := Copy(res, Length(CurDirectory) + 2, Length(res))
  425. else if not IsLinkAbsolute(res) then
  426. res := BaseDirectory + res;
  427. end;
  428. Result:= res;
  429. end;
  430. { Used for:
  431. - <link> elements in descriptions
  432. - "see also" entries
  433. - AppendHyperlink (for unresolved parse tree element links)
  434. }
  435. function TMultiFileDocWriter.ResolveLinkIDInUnit(const Name,AUnitName: String): DOMString;
  436. begin
  437. Result:=ResolveLinkID(Name);
  438. If (Result='') and (AUnitName<>'') and (length(Name)>0) and (Name[1]<>'#') then
  439. Result:=ResolveLinkID(AUnitName+'.'+Name);
  440. end;
  441. function TMultiFileDocWriter.ResolveLinkWithinPackage(AElement: TPasElement;
  442. ASubpageIndex: Integer): String;
  443. var
  444. ParentEl: TPasElement;
  445. s : String;
  446. begin
  447. ParentEl := AElement;
  448. while Assigned(ParentEl) and not (ParentEl.ClassType = TPasPackage) do
  449. ParentEl := ParentEl.Parent;
  450. if Assigned(ParentEl) and (TPasPackage(ParentEl) = Engine.Package) then
  451. begin
  452. Result := Allocator.GetFilename(AElement, ASubpageIndex);
  453. // engine/allocator can return backslashes on Windows
  454. s:=Copy(Result, 1, Length(CurDirectory) + 1);
  455. if (S= CurDirectory + '/') or (s= CurDirectory + '\') then
  456. Result := Copy(Result, Length(CurDirectory) + 2, Length(Result))
  457. else
  458. Result := BaseDirectory + Result;
  459. end else
  460. SetLength(Result, 0);
  461. end;
  462. procedure TMultiFileDocWriter.PrepareDocumentation;
  463. begin
  464. inherited PrepareDocumentation;
  465. FAllocator:= CreateAllocator();
  466. FAllocator.SubPageNames:= SubPageNames;
  467. end;
  468. function TMultiFileDocWriter.AddPage(AElement: TPasElement;
  469. ASubpageIndex: Integer): TPageInfo;
  470. begin
  471. Result:= TPageInfo.Create(aElement,aSubPageIndex);
  472. PageInfos.Add(Result);
  473. Allocator.AllocFilename(AElement, ASubpageIndex);
  474. if ASubpageIndex = 0 then
  475. Engine.AddLink(AElement.PathName,Allocator.GetFilename(AElement, ASubpageIndex));
  476. end;
  477. procedure TMultiFileDocWriter.AddTopicPages(AElement: TPasElement);
  478. var
  479. PreviousTopic,
  480. TopicElement : TTopicElement;
  481. DocNode,
  482. TopicNode : TDocNode;
  483. begin
  484. DocNode:=Engine.FindDocNode(AElement);
  485. If not Assigned(DocNode) then
  486. exit;
  487. TopicNode:=DocNode.FirstChild;
  488. PreviousTopic:=Nil;
  489. While Assigned(TopicNode) do
  490. begin
  491. If TopicNode.TopicNode then
  492. begin
  493. TopicElement:=TTopicElement.Create(TopicNode.Name,AElement);
  494. Topics.Add(TopicElement);
  495. TopicElement.TopicNode:=TopicNode;
  496. TopicElement.Previous:=PreviousTopic;
  497. If Assigned(PreviousTopic) then
  498. PreviousTopic.Next:=TopicElement;
  499. PreviousTopic:=TopicElement;
  500. if AElement is TTopicElement then
  501. TTopicElement(AElement).SubTopics.Add(TopicElement);
  502. AddPage(TopicElement,IdentifierIndex);
  503. if AElement is TTopicElement then
  504. TTopicElement(AElement).SubTopics.Add(TopicElement)
  505. else // Only one level of recursion.
  506. AddTopicPages(TopicElement);
  507. end;
  508. TopicNode:=TopicNode.NextSibling;
  509. end;
  510. end;
  511. function TMultiFileDocWriter.ModuleHasClasses(AModule: TPasModule): Boolean;
  512. begin
  513. result:=assigned(AModule)
  514. and assigned(AModule.InterfaceSection)
  515. and assigned(AModule.InterfaceSection.Classes)
  516. and (AModule.InterfaceSection.Classes.Count>0);
  517. end;
  518. procedure TMultiFileDocWriter.AddPages(AElement: TPasElement; ASubpageIndex: Integer;
  519. AList: TFPList);
  520. var
  521. i,j: Integer;
  522. R : TPasRecordtype;
  523. FPEl : TPasElement;
  524. DocNode: TDocNode;
  525. begin
  526. if AList.Count > 0 then
  527. begin
  528. AddPage(AElement, ASubpageIndex);
  529. for i := 0 to AList.Count - 1 do
  530. begin
  531. AddPage(TPasElement(AList[i]), 0);
  532. if (TObject(AList[i]) is TPasRecordType) then
  533. begin
  534. R:=TObject(AList[I]) as TPasRecordType;
  535. For J:=0 to R.Members.Count-1 do
  536. begin
  537. FPEl:=TPasElement(R.Members[J]);
  538. if ((FPEL is TPasProperty) or (FPEL is TPasProcedureBase))
  539. and Engine.ShowElement(FPEl) then
  540. begin
  541. DocNode := Engine.FindDocNode(FPEl);
  542. if Assigned(DocNode) then
  543. AddPage(FPEl, 0);
  544. end;
  545. end;
  546. end;
  547. end;
  548. end;
  549. end;
  550. procedure TMultiFileDocWriter.AllocateClassMemberPages(AModule: TPasModule;
  551. LinkList: TObjectList);
  552. var
  553. i, j, k: Integer;
  554. ClassEl: TPasClassType;
  555. FPEl, AncestorMemberEl: TPasElement;
  556. DocNode: TDocNode;
  557. ALink : DOMString;
  558. DidAutolink: Boolean;
  559. begin
  560. for i := 0 to AModule.InterfaceSection.Classes.Count - 1 do
  561. begin
  562. ClassEl := TPasClassType(AModule.InterfaceSection.Classes[i]);
  563. AddPage(ClassEl, 0);
  564. // !!!: Only add when there are items
  565. AddPage(ClassEl, PropertiesByInheritanceSubindex);
  566. AddPage(ClassEl, PropertiesByNameSubindex);
  567. AddPage(ClassEl, MethodsByInheritanceSubindex);
  568. AddPage(ClassEl, MethodsByNameSubindex);
  569. AddPage(ClassEl, EventsByInheritanceSubindex);
  570. AddPage(ClassEl, EventsByNameSubindex);
  571. for j := 0 to ClassEl.Members.Count - 1 do
  572. begin
  573. FPEl := TPasElement(ClassEl.Members[j]);
  574. if Not Engine.ShowElement(FPEl) then
  575. continue;
  576. DocNode := Engine.FindDocNode(FPEl);
  577. if Assigned(DocNode) then
  578. begin
  579. if Assigned(DocNode.Node) then
  580. ALink:=DocNode.Node['link']
  581. else
  582. ALink:='';
  583. If (ALink<>'') then
  584. LinkList.Add(TLinkData.Create(FPEl.PathName,UTF8Encode(ALink),AModule.name))
  585. else
  586. AddPage(FPEl, 0);
  587. end
  588. else
  589. begin
  590. DidAutolink := False;
  591. if Assigned(ClassEl.AncestorType) and
  592. (ClassEl.AncestorType.ClassType.inheritsfrom(TPasClassType)) then
  593. begin
  594. for k := 0 to TPasClassType(ClassEl.AncestorType).Members.Count - 1 do
  595. begin
  596. AncestorMemberEl :=
  597. TPasElement(TPasClassType(ClassEl.AncestorType).Members[k]);
  598. if AncestorMemberEl.Name = FPEl.Name then
  599. begin
  600. DocNode := Engine.FindDocNode(AncestorMemberEl);
  601. if Assigned(DocNode) then
  602. begin
  603. DidAutolink := True;
  604. Engine.AddLink(FPEl.PathName,
  605. Engine.FindAbsoluteLink(AncestorMemberEl.PathName));
  606. break;
  607. end;
  608. end;
  609. end;
  610. end;
  611. if not DidAutolink then
  612. AddPage(FPEl, 0);
  613. end;
  614. end;
  615. end;
  616. end;
  617. procedure TMultiFileDocWriter.AllocateModulePages(AModule: TPasModule; LinkList : TObjectList);
  618. var
  619. i: Integer;
  620. s: String;
  621. begin
  622. if not assigned(Amodule.Interfacesection) then
  623. exit;
  624. AddPage(AModule, 0);
  625. AddPage(AModule,IndexSubIndex);
  626. AddTopicPages(AModule);
  627. with AModule do
  628. begin
  629. if InterfaceSection.ResStrings.Count > 0 then
  630. begin
  631. AddPage(AModule, ResstrSubindex);
  632. s := Allocator.GetFilename(AModule, ResstrSubindex);
  633. for i := 0 to InterfaceSection.ResStrings.Count - 1 do
  634. with TPasResString(InterfaceSection.ResStrings[i]) do
  635. Engine.AddLink(PathName, s + '#' + LowerCase(Name));
  636. end;
  637. AddPages(AModule, ConstsSubindex, InterfaceSection.Consts);
  638. AddPages(AModule, TypesSubindex, InterfaceSection.Types);
  639. if InterfaceSection.Classes.Count > 0 then
  640. begin
  641. AddPage(AModule, ClassesSubindex);
  642. AllocateClassMemberPages(AModule,LinkList);
  643. end;
  644. AddPages(AModule, ProcsSubindex, InterfaceSection.Functions);
  645. AddPages(AModule, VarsSubindex, InterfaceSection.Variables);
  646. end;
  647. end;
  648. procedure TMultiFileDocWriter.AllocatePackagePages;
  649. Var
  650. I : Integer;
  651. H : Boolean;
  652. begin
  653. if Length(Package.Name) <= 1 then
  654. exit;
  655. AddPage(Package, 0);
  656. AddPage(Package,IndexSubIndex);
  657. I:=0;
  658. H:=False;
  659. While (I<Package.Modules.Count) and Not H do
  660. begin
  661. H:=ModuleHasClasses(TPasModule(Package.Modules[i]));
  662. Inc(I);
  663. end;
  664. if H then
  665. AddPage(Package,ClassHierarchySubIndex);
  666. AddTopicPages(Package);
  667. end;
  668. procedure TMultiFileDocWriter.AllocatePages;
  669. Var
  670. L : TObjectList;
  671. ML : TFPList;
  672. I : Integer;
  673. begin
  674. // Allocate page for the package itself, if a name is given (i.e. <> '#')
  675. AllocatePackagePages;
  676. ML:=Nil;
  677. L:=TObjectList.Create;
  678. try
  679. ML:=TFPList.Create;
  680. ML.AddList(Package.Modules);
  681. ML.Sort(@SortPasElements);
  682. for i := 0 to ML.Count - 1 do
  683. AllocateModulePages(TPasModule(ML[i]),L);
  684. // Resolve links
  685. For I:=0 to L.Count-1 do
  686. With TLinkData(L[i]) do
  687. Engine.AddLink(FPathName,UTF8Encode(ResolveLinkIDInUnit(FLink,FModuleName)));
  688. finally
  689. L.Free;
  690. ML.Free;
  691. end;
  692. end;
  693. function TMultiFileDocWriter.GetFileBaseDir(aOutput: String) : String;
  694. begin
  695. Result:=aOutput;
  696. if Result<>'' then
  697. Result:=IncludeTrailingPathDelimiter(Result);
  698. end;
  699. procedure TMultiFileDocWriter.DoWriteDocumentation;
  700. procedure CreatePath(const AFilename: String);
  701. var
  702. EndIndex: Integer;
  703. Path: String;
  704. begin
  705. EndIndex := Length(AFilename);
  706. if EndIndex = 0 then
  707. exit;
  708. while not (AFilename[EndIndex] in AllowDirectorySeparators) do
  709. begin
  710. Dec(EndIndex);
  711. if EndIndex = 0 then
  712. exit;
  713. end;
  714. Path := Copy(AFilename, 1, EndIndex - 1);
  715. if not DirectoryExists(Path) then
  716. begin
  717. CreatePath(Path);
  718. MkDir(Path);
  719. end;
  720. end;
  721. var
  722. i: Integer;
  723. FileName : String;
  724. FinalFilename: String;
  725. begin
  726. AllocatePages;
  727. DoLog(SWritingPages, [PageCount]);
  728. if Engine.Output <> '' then
  729. Engine.Output := IncludeTrailingBackSlash(Engine.Output);
  730. for i := 0 to PageInfos.Count - 1 do
  731. with TPageInfo(PageInfos[i]) do
  732. begin
  733. FileName:= Allocator.GetFilename(Element, SubpageIndex);
  734. if MustGeneratePage(FileName) then
  735. begin
  736. FinalFilename := GetFileBaseDir(Engine.Output) + FileName;
  737. CreatePath(FinalFilename);
  738. WriteDocPage(FileName,ELement,SubPageIndex);
  739. end;
  740. end;
  741. end;
  742. function TMultiFileDocWriter.MustGeneratePage(aFileName: String): Boolean;
  743. begin
  744. Result:=Not Assigned(FOutputPageNames);
  745. if Not Result then
  746. Result:=FOutputPageNames.IndexOf(aFileName)<>-1;
  747. Writeln(afilename ,': ',result);
  748. end;
  749. class procedure TMultiFileDocWriter.Usage(List: TStrings);
  750. begin
  751. List.AddStrings(['--use-subpagenames', SUsageSubNames]);
  752. List.AddStrings(['--only-pages=LIST', SUsageOnlyPages]);
  753. end;
  754. function TMultiFileDocWriter.InterPretOption(const Cmd, Arg: String): boolean;
  755. Var
  756. I : Integer;
  757. FN : String;
  758. begin
  759. Writeln('Cmd : ',Cmd);
  760. Result := True;
  761. if Cmd = '--use-subpagenames' then
  762. FSubPageNames:= True
  763. else
  764. if Cmd = '--only-pages' then
  765. begin
  766. Result:=Arg<>'';
  767. if Result then
  768. begin
  769. if Arg[1]='@' then
  770. begin
  771. FN:=Copy(Arg,2,Length(Arg)-1);
  772. OutputPageNames.LoadFromFile(FN);
  773. end
  774. else
  775. begin
  776. For I:=1 to WordCount(Arg,[',']) do
  777. OutputPageNames.Add(ExtractWord(I,Arg,[',']));
  778. end;
  779. Writeln('OutputPagenames ',OutputPagenames.CommaText);
  780. end
  781. end
  782. else
  783. Result:=inherited InterPretOption(Cmd, Arg);
  784. end;
  785. { TWriterRecord }
  786. constructor TWriterRecord.Create(AClass: TFPDocWriterClass; const AName,
  787. ADescr: String);
  788. begin
  789. FClass:=AClass;
  790. FName:=AName;
  791. FDescription:=ADescr;
  792. end;
  793. Var
  794. Writers : TStringList;
  795. Procedure InitWriterList;
  796. begin
  797. Writers:=TStringList.Create;
  798. Writers.Sorted:=True;
  799. end;
  800. Procedure DoneWriterList;
  801. Var
  802. I : Integer;
  803. begin
  804. For I:=Writers.Count-1 downto 0 do
  805. Writers.Objects[i].Free;
  806. FreeAndNil(Writers);
  807. end;
  808. procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName, ADescr : String);
  809. begin
  810. If Writers.IndexOf(AName)<>-1 then
  811. Raise EFPDocWriterError.CreateFmt(SErralreadyRegistered,[ANAme]);
  812. Writers.AddObject(AName,TWriterRecord.Create(AClass,AName,ADescr));
  813. end;
  814. function FindWriterClass(AName : String) : Integer;
  815. begin
  816. Result:=Writers.IndexOf(AName);
  817. end;
  818. function GetWriterClass(AName : String) : TFPDocWriterClass;
  819. Var
  820. Index : Integer;
  821. begin
  822. Index:=FindWriterClass(AName);
  823. If Index=-1 then
  824. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  825. Result:=(Writers.Objects[Index] as TWriterRecord).FClass;
  826. end;
  827. // UnRegister backend
  828. Procedure UnRegisterWriter(Const AName : String);
  829. Var
  830. Index : Integer;
  831. begin
  832. Index:=Writers.IndexOf(AName);
  833. If Index=-1 then
  834. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  835. Writers.Objects[Index].Free;
  836. Writers.Delete(Index);
  837. end;
  838. Procedure EnumWriters(List : TStrings);
  839. Var
  840. I : Integer;
  841. begin
  842. List.Clear;
  843. For I:=0 to Writers.Count-1 do
  844. With (Writers.Objects[I] as TWriterRecord) do
  845. List.Add(FName+'='+FDescription);
  846. end;
  847. function IsWhitespaceNode(Node: TDOMText): Boolean;
  848. var
  849. I,L: Integer;
  850. S: DOMString;
  851. P : PWideChar;
  852. begin
  853. S := Node.Data;
  854. Result := True;
  855. I:=0;
  856. L:=Length(S);
  857. P:=PWideChar(S);
  858. While Result and (I<L) do
  859. begin
  860. Result:=P^ in [#32,#10,#9,#13];
  861. Inc(P);
  862. Inc(I);
  863. end;
  864. end;
  865. { ---------------------------------------------------------------------
  866. TFileAllocator
  867. ---------------------------------------------------------------------}
  868. function TFileAllocator.GetFilePostfix(ASubindex: Integer): String;
  869. begin
  870. if FSubPageNames then
  871. case ASubindex of
  872. IdentifierIndex: Result:='';
  873. ResstrSubindex: Result:='reestr';
  874. ConstsSubindex: Result:='consts';
  875. TypesSubindex: Result:='types';
  876. ClassesSubindex: Result:='classes';
  877. ProcsSubindex: Result:='procs';
  878. VarsSubindex: Result:='vars';
  879. TopicsSubIndex: Result:='topics';
  880. IndexSubIndex: Result:='indexes';
  881. ClassHierarchySubIndex: Result:='class-tree';
  882. InterfaceHierarchySubIndex: Result:='interface-tree';
  883. PropertiesByInheritanceSubindex: Result:='props';
  884. PropertiesByNameSubindex: Result:='props-n';
  885. MethodsByInheritanceSubindex: Result:='methods';
  886. MethodsByNameSubindex: Result:='methods-n';
  887. EventsByInheritanceSubindex: Result:='events';
  888. EventsByNameSubindex: Result:='events-n';
  889. end
  890. else
  891. Result:= IntToStr(ASubindex);
  892. end;
  893. procedure TFileAllocator.Create();
  894. begin
  895. FSubPageNames:= False;
  896. end;
  897. procedure TFileAllocator.AllocFilename(AElement: TPasElement;
  898. ASubindex: Integer);
  899. begin
  900. end;
  901. function TFileAllocator.GetRelativePathToTop(AElement: TPasElement): String;
  902. begin
  903. Result:='';
  904. end;
  905. function TFileAllocator.GetCSSFilename(ARelativeTo: TPasElement): DOMString;
  906. begin
  907. Result := Utf8Decode(GetRelativePathToTop(ARelativeTo)) + 'fpdoc.css';
  908. end;
  909. { ---------------------------------------------------------------------
  910. TLongNameFileAllocator
  911. ---------------------------------------------------------------------}
  912. constructor TLongNameFileAllocator.Create(const AExtension: String);
  913. begin
  914. inherited Create;
  915. FExtension := AExtension;
  916. end;
  917. function TLongNameFileAllocator.GetFilename(AElement: TPasElement; ASubindex: Integer): String;
  918. var
  919. n,s: String;
  920. i: Integer;
  921. MElement: TPasElement;
  922. begin
  923. Result:='';
  924. if AElement.ClassType = TPasPackage then
  925. Result := 'index'
  926. else if AElement.ClassType = TPasModule then
  927. Result := LowerCase(AElement.Name) + PathDelim + 'index'
  928. else
  929. begin
  930. if AElement is TPasOperator then
  931. begin
  932. if Assigned(AElement.Parent) then
  933. result:=LowerCase(AElement.Parent.PathName);
  934. With TPasOperator(aElement) do
  935. Result:= Result + 'op-'+OperatorTypeToOperatorName(OperatorType);
  936. s := '';
  937. N:=LowerCase(aElement.Name); // Should not contain any weird chars.
  938. Delete(N,1,Pos('(',N));
  939. i := 1;
  940. Repeat
  941. I:=Pos(',',N);
  942. if I=0 then
  943. I:=Pos(')',N);
  944. if I>1 then
  945. begin
  946. if (S<>'') then
  947. S:=S+'-';
  948. S:=S+Copy(N,1,I-1);
  949. end;
  950. Delete(N,1,I);
  951. until I=0;
  952. // First char is maybe :
  953. if (N<>'') and (N[1]=':') then
  954. Delete(N,1,1);
  955. Result:=Result + '-'+ s + '-' + N;
  956. end else
  957. Result := LowerCase(AElement.PathName);
  958. // cut off Package Name
  959. MElement:= AElement.GetModule;
  960. if Assigned(MElement) then
  961. AElement:= MElement;
  962. Result := Copy(Result, Length(AElement.Parent.Name) + 2, MaxInt);
  963. // to skip dots in unit name
  964. i := Length(AElement.Name);
  965. while (i <= Length(Result)) and (Result[i] <> '.') do
  966. Inc(i);
  967. if (i <= Length(Result)) and (i > 0) then
  968. Result[i] := PathDelim;
  969. end;
  970. if ASubindex > 0 then
  971. Result := Result + '-' + GetFilePostfix(ASubindex);
  972. Result := Result + Extension;
  973. end;
  974. function TLongNameFileAllocator.GetRelativePathToTop(AElement: TPasElement): String;
  975. begin
  976. if (AElement.ClassType=TPasPackage) then
  977. Result := ''
  978. else if (AElement.ClassType=TTopicElement) then
  979. begin
  980. If (AElement.Parent.ClassType=TTopicElement) then
  981. Result:='../'+GetRelativePathToTop(AElement.Parent)
  982. else if (AElement.Parent.ClassType=TPasPackage) then
  983. Result:=''
  984. else if (AElement.Parent.ClassType=TPasModule) then
  985. Result:='../';
  986. end
  987. else
  988. Result := '../';
  989. end;
  990. { ---------------------------------------------------------------------
  991. TFPDocWriter
  992. ---------------------------------------------------------------------}
  993. {
  994. fmtIPF:
  995. begin
  996. if Length(Engine.Output) = 0 then
  997. WriteLn(SCmdLineOutputOptionMissing)
  998. else
  999. CreateIPFDocForPackage(Engine.Package, Engine);
  1000. end;
  1001. }
  1002. constructor TFPDocWriter.Create ( APackage: TPasPackage; AEngine: TFPDocEngine
  1003. ) ;
  1004. begin
  1005. inherited Create;
  1006. FEngine := AEngine;
  1007. FPackage := APackage;
  1008. FTopics:=Tlist.Create;
  1009. FImgExt:='.png';
  1010. TreeClass:= TClassTreeBuilder.Create(FEngine, FPackage, okWithFields);
  1011. TreeInterface:= TClassTreeBuilder.Create(FEngine, FPackage, [okInterface]);
  1012. CreateClassTree;
  1013. end;
  1014. destructor TFPDocWriter.Destroy;
  1015. Var
  1016. i : integer;
  1017. begin
  1018. For I:=0 to FTopics.Count-1 do
  1019. TTopicElement(FTopics[i]).Free;
  1020. FTopics.Free;
  1021. TreeClass.free;
  1022. TreeInterface.Free;
  1023. Inherited;
  1024. end;
  1025. procedure TFPDocWriter.AddModuleIdentifiers(AModule : TPasModule; L : TStrings);
  1026. begin
  1027. if assigned(AModule.InterfaceSection) Then
  1028. begin
  1029. AddElementsFromList(L,AModule.InterfaceSection.Consts);
  1030. AddElementsFromList(L,AModule.InterfaceSection.Types);
  1031. AddElementsFromList(L,AModule.InterfaceSection.Functions);
  1032. AddElementsFromList(L,AModule.InterfaceSection.Classes);
  1033. AddElementsFromList(L,AModule.InterfaceSection.Variables);
  1034. AddElementsFromList(L,AModule.InterfaceSection.ResStrings);
  1035. end;
  1036. end;
  1037. function TFPDocWriter.InterpretOption(const Cmd, Arg: String): Boolean;
  1038. begin
  1039. Result:=False;
  1040. end;
  1041. class function TFPDocWriter.FileNameExtension: String;
  1042. begin
  1043. //Override in linear writers with the expected extension.
  1044. Result := ''; //Output must not contain an extension.
  1045. end;
  1046. class procedure TFPDocWriter.Usage(List: TStrings);
  1047. begin
  1048. // Do nothing.
  1049. end;
  1050. class procedure TFPDocWriter.SplitImport(var AFilename, ALinkPrefix: String);
  1051. var
  1052. i: integer;
  1053. begin
  1054. //override in HTML and CHM writer
  1055. i := Pos(',', AFilename);
  1056. if i > 0 then
  1057. begin //split CSV into filename and prefix
  1058. ALinkPrefix := Copy(AFilename,i+1,Length(AFilename));
  1059. SetLength(AFilename, i-1);
  1060. end;
  1061. end;
  1062. procedure TFPDocWriter.WriteDocumentation;
  1063. begin
  1064. PrepareDocumentation();
  1065. DoWriteDocumentation();
  1066. OutputResults();
  1067. end;
  1068. function TFPDocWriter.FindTopicElement ( Node: TDocNode ) : TTopicElement;
  1069. Var
  1070. I : Integer;
  1071. begin
  1072. Result:=Nil;
  1073. I:=FTopics.Count-1;
  1074. While (I>=0) and (Result=Nil) do
  1075. begin
  1076. If (TTopicElement(FTopics[i]).TopicNode=Node) Then
  1077. Result:=TTopicElement(FTopics[i]);
  1078. Dec(I);
  1079. end;
  1080. end;
  1081. procedure TFPDocWriter.DescrWriteImageEl(const AFileName, ACaption,
  1082. ALinkName: DOMString);
  1083. begin
  1084. DoLog('%s : No support for images yet: %s (caption: "%s")',[ClassName,AFileName,ACaption]);
  1085. end;
  1086. procedure TFPDocWriter.PrepareDocumentation;
  1087. begin
  1088. // Ancestors can call AllocatePages();CreateAllocator(); into base class
  1089. end;
  1090. { ---------------------------------------------------------------------
  1091. Generic documentation node conversion
  1092. ---------------------------------------------------------------------}
  1093. function IsContentNodeType(Node: TDOMNode): Boolean;
  1094. begin
  1095. Result := (Node.NodeType = ELEMENT_NODE) or
  1096. ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or
  1097. (Node.NodeType = ENTITY_REFERENCE_NODE);
  1098. end;
  1099. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String);
  1100. begin
  1101. if (AContext<>nil) then
  1102. DoLog('[%s] %s',[AContext.PathName,AMsg])
  1103. else
  1104. DoLog('[<no context>] %s', [AMsg]);
  1105. end;
  1106. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String;
  1107. const Args: array of const);
  1108. begin
  1109. Warning(AContext, Format(AMsg, Args));
  1110. end;
  1111. function TFPDocWriter.IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  1112. var
  1113. Child: TDOMNode;
  1114. begin
  1115. if (not Assigned(Node)) or (not Assigned(Node.FirstChild)) then
  1116. Result := True
  1117. else
  1118. begin
  1119. Child := Node.FirstChild;
  1120. while Assigned(Child) do
  1121. begin
  1122. if (Child.NodeType = ELEMENT_NODE) or (Child.NodeType = TEXT_NODE) or
  1123. (Child.NodeType = ENTITY_REFERENCE_NODE) then
  1124. begin
  1125. Result := False;
  1126. exit;
  1127. end;
  1128. Child := Child.NextSibling;
  1129. end;
  1130. end;
  1131. Result := True;
  1132. end;
  1133. { Check wether the nodes starting with the node given as argument make up an
  1134. 'extshort' production. }
  1135. function TFPDocWriter.IsExtShort(Node: TDOMNode): Boolean;
  1136. begin
  1137. while Assigned(Node) do
  1138. begin
  1139. if Node.NodeType = ELEMENT_NODE then
  1140. if (Node.NodeName <> 'br') and
  1141. (Node.NodeName <> 'link') and
  1142. (Node.NodeName <> 'url') and
  1143. (Node.NodeName <> 'b') and
  1144. (Node.NodeName <> 'file') and
  1145. (Node.NodeName <> 'i') and
  1146. (Node.NodeName <> 'kw') and
  1147. (Node.NodeName <> 'printshort') and
  1148. (Node.NodeName <> 'var') then
  1149. begin
  1150. Result := False;
  1151. exit;
  1152. end;
  1153. Node := Node.NextSibling;
  1154. end;
  1155. Result := True;
  1156. end;
  1157. function TFPDocWriter.ConvertShort(AContext: TPasElement;
  1158. El: TDOMElement): Boolean;
  1159. var
  1160. Node: TDOMNode;
  1161. begin
  1162. Result := False;
  1163. if not Assigned(El) then
  1164. exit;
  1165. FContext:=AContext;
  1166. try
  1167. Node := El.FirstChild;
  1168. while Assigned(Node) do
  1169. begin
  1170. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  1171. ConvertLink(AContext, TDOMElement(Node))
  1172. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'url') then
  1173. ConvertURL(AContext, TDOMElement(Node))
  1174. else
  1175. if not ConvertBaseShort(AContext, Node) then
  1176. exit;
  1177. Node := Node.NextSibling;
  1178. end;
  1179. Result := True;
  1180. finally
  1181. FContext:=Nil;
  1182. end;
  1183. end;
  1184. function TFPDocWriter.ConvertNotes(AContext: TPasElement; El: TDOMElement
  1185. ): Boolean;
  1186. Var
  1187. L : TFPList;
  1188. N : TDomNode;
  1189. I : Integer;
  1190. B : Boolean;
  1191. begin
  1192. Result:=Assigned(El) and EmitNotes;
  1193. If Not Result then
  1194. exit;
  1195. L:=TFPList.Create;
  1196. try
  1197. N:=El.FirstChild;
  1198. While Assigned(N) do
  1199. begin
  1200. If (N.NodeType=ELEMENT_NODE) and (N.NodeName='note') then
  1201. begin
  1202. B:=True;
  1203. if Assigned(FBeforeEmitNote) then
  1204. FBeforeEmitNote(Self,TDomElement(N),B);
  1205. If B then
  1206. L.Add(N);
  1207. end;
  1208. N:=N.NextSibling;
  1209. end;
  1210. Result:=L.Count>0;
  1211. If Not Result then
  1212. exit;
  1213. DescrEmitNotesHeader(AContext);
  1214. DescrBeginUnorderedList;
  1215. For i:=0 to L.Count-1 do
  1216. begin
  1217. DescrBeginListItem;
  1218. ConvertExtShortOrNonSectionBlocks(AContext, TDOMNode(L[i]).FirstChild);
  1219. DescrEndListItem;
  1220. end;
  1221. DescrEndUnorderedList;
  1222. DescrEmitNotesFooter(AContext);
  1223. finally
  1224. L.Free;
  1225. end;
  1226. end;
  1227. function TFPDocWriter.ConvertBaseShort(AContext: TPasElement;
  1228. Node: TDOMNode): Boolean;
  1229. function ConvertText: DOMString;
  1230. var
  1231. s: DOMString;
  1232. i: Integer;
  1233. begin
  1234. if Node.NodeType = TEXT_NODE then
  1235. begin
  1236. s := Node.NodeValue;
  1237. i := 1;
  1238. Result:='';
  1239. while i <= Length(s) do
  1240. if s[i] = #13 then
  1241. begin
  1242. Result := Result + ' ';
  1243. Inc(i);
  1244. if s[i] = #10 then
  1245. Inc(i);
  1246. end else if s[i] = #10 then
  1247. begin
  1248. Result := Result + ' ';
  1249. Inc(i);
  1250. end else
  1251. begin
  1252. Result := Result + s[i];
  1253. Inc(i);
  1254. end;
  1255. end else if Node.NodeType = ENTITY_REFERENCE_NODE then
  1256. if Node.NodeName = 'fpc' then
  1257. Result := 'Free Pascal'
  1258. else if Node.NodeName = 'delphi' then
  1259. Result := 'Delphi'
  1260. else
  1261. begin
  1262. Warning(AContext, Format(SErrUnknownEntityReference, [Node.NodeName]));
  1263. Result := Node.NodeName;
  1264. end
  1265. else if Node.NodeType = ELEMENT_NODE then
  1266. SetLength(Result, 0);
  1267. end;
  1268. function ConvertTextContent: DOMString;
  1269. begin
  1270. Result:='';
  1271. Node := Node.FirstChild;
  1272. while Assigned(Node) do
  1273. begin
  1274. Result := Result + ConvertText;
  1275. Node := Node.NextSibling;
  1276. end;
  1277. end;
  1278. var
  1279. El, DescrEl: TDOMElement;
  1280. hlp : TPasElement;
  1281. begin
  1282. Result := True;
  1283. if Node.NodeType = ELEMENT_NODE then
  1284. if Node.NodeName = 'b' then
  1285. begin
  1286. DescrBeginBold;
  1287. ConvertBaseShortList(AContext, Node, False);
  1288. DescrEndBold;
  1289. end else
  1290. if Node.NodeName = 'i' then
  1291. begin
  1292. DescrBeginItalic;
  1293. ConvertBaseShortList(AContext, Node, False);
  1294. DescrEndItalic;
  1295. end else
  1296. if Node.NodeName = 'em' then
  1297. begin
  1298. DescrBeginEmph;
  1299. ConvertBaseShortList(AContext, Node, False);
  1300. DescrEndEmph;
  1301. end else
  1302. if Node.NodeName = 'u' then
  1303. begin
  1304. DescrBeginUnderline;
  1305. ConvertBaseShortList(AContext, Node, False);
  1306. DescrEndUnderline;
  1307. end else
  1308. if Node.NodeName = 'file' then
  1309. DescrWriteFileEl(ConvertTextContent)
  1310. else if Node.NodeName = 'kw' then
  1311. DescrWriteKeywordEl(ConvertTextContent)
  1312. else if Node.NodeName = 'printshort' then
  1313. begin
  1314. El := TDOMElement(Node);
  1315. hlp:=AContext;
  1316. while assigned(hlp) and not (hlp is TPasModule) do
  1317. hlp:=hlp.parent;
  1318. if not (hlp is TPasModule) then
  1319. hlp:=nil;
  1320. DescrEl := Engine.FindShortDescr(TPasModule(hlp), UTF8Encode(El['id']));
  1321. if Assigned(DescrEl) then
  1322. ConvertShort(AContext, DescrEl)
  1323. else
  1324. begin
  1325. Warning(AContext, Format(SErrUnknownPrintShortID, [El['id']]));
  1326. DescrBeginBold;
  1327. DescrWriteText('#ShortDescr:' + El['id']);
  1328. DescrEndBold;
  1329. end;
  1330. end else if Node.NodeName = 'var' then
  1331. DescrWriteVarEl(ConvertTextContent)
  1332. else
  1333. Result := False
  1334. else
  1335. DescrWriteText(ConvertText);
  1336. end;
  1337. procedure TFPDocWriter.ConvertBaseShortList(AContext: TPasElement;
  1338. Node: TDOMNode; MayBeEmpty: Boolean);
  1339. var
  1340. Child: TDOMNode;
  1341. begin
  1342. Child := Node.FirstChild;
  1343. while Assigned(Child) do
  1344. begin
  1345. if not ConvertBaseShort(AContext, Child) then
  1346. Warning(AContext, SErrInvalidShortDescr)
  1347. else
  1348. MayBeEmpty := True;
  1349. Child := Child.NextSibling;
  1350. end;
  1351. if not MayBeEmpty then
  1352. Warning(AContext, SErrInvalidShortDescr)
  1353. end;
  1354. procedure TFPDocWriter.ConvertLink(AContext: TPasElement; El: TDOMElement);
  1355. begin
  1356. DescrBeginLink(El['id']);
  1357. if not IsDescrNodeEmpty(El) then
  1358. ConvertBaseShortList(AContext, El, True)
  1359. else
  1360. DescrWriteText(El['id']);
  1361. DescrEndLink;
  1362. end;
  1363. procedure TFPDocWriter.ConvertURL(AContext: TPasElement; El: TDOMElement);
  1364. begin
  1365. DescrBeginURL(El['href']);
  1366. if not IsDescrNodeEmpty(El) then
  1367. ConvertBaseShortList(AContext, El, True)
  1368. else
  1369. DescrWriteText(El['href']);
  1370. DescrEndURL;
  1371. end;
  1372. procedure TFPDocWriter.AddElementsFromList ( L: TStrings; List: TFPList;
  1373. UsePathName: Boolean ) ;
  1374. Var
  1375. I : Integer;
  1376. El : TPasElement;
  1377. N : TDocNode;
  1378. begin
  1379. For I:=0 to List.Count-1 do
  1380. begin
  1381. El:=TPasElement(List[I]);
  1382. N:=Engine.FindDocNode(El);
  1383. if (N=Nil) or (not N.IsSkipped) then
  1384. begin
  1385. if UsePathName then
  1386. L.AddObject(El.PathName,El)
  1387. else
  1388. L.AddObject(El.Name,El);
  1389. If el is TPasEnumType then
  1390. AddElementsFromList(L,TPasEnumType(el).Values);
  1391. end;
  1392. end;
  1393. end;
  1394. procedure TFPDocWriter.CreateClassTree;
  1395. var
  1396. L: TStringList;
  1397. M: TPasModule;
  1398. I:Integer;
  1399. begin
  1400. L:=TStringList.Create;
  1401. try
  1402. For I:=0 to Package.Modules.Count-1 do
  1403. begin
  1404. M:=TPasModule(Package.Modules[i]);
  1405. if Not (M is TPasExternalModule) and assigned(M.InterfaceSection) then
  1406. Self.AddElementsFromList(L,M.InterfaceSection.Classes,True)
  1407. end;
  1408. // You can see this tree by using --format=xml option
  1409. TreeClass.BuildTree(L);
  1410. TreeInterface.BuildTree(L);
  1411. Finally
  1412. L.Free;
  1413. end;
  1414. end;
  1415. procedure TFPDocWriter.DoLog(const Msg: String);
  1416. begin
  1417. If Assigned(FEngine.OnLog) then
  1418. FEngine.OnLog(Self,Msg);
  1419. end;
  1420. procedure TFPDocWriter.DoLog(const Fmt: String; Args: array of const);
  1421. begin
  1422. DoLog(Format(Fmt,Args));
  1423. end;
  1424. procedure TFPDocWriter.OutputResults();
  1425. begin
  1426. DoLog('Package: %s - Documentation process finished.', [FPackage.Name]);
  1427. end;
  1428. function TFPDocWriter.ConvertExtShort(AContext: TPasElement;
  1429. Node: TDOMNode): Boolean;
  1430. begin
  1431. Result := False;
  1432. while Assigned(Node) do
  1433. begin
  1434. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  1435. ConvertLink(AContext, TDOMElement(Node))
  1436. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'url') then
  1437. ConvertURL(AContext, TDOMElement(Node))
  1438. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'br') then
  1439. DescrWriteLinebreak
  1440. else
  1441. if not ConvertBaseShort(AContext, Node) then
  1442. exit;
  1443. Node := Node.NextSibling;
  1444. end;
  1445. Result := True;
  1446. end;
  1447. procedure TFPDocWriter.ConvertDescr(AContext: TPasElement; El: TDOMElement;
  1448. AutoInsertBlock: Boolean);
  1449. var
  1450. Node, Child: TDOMNode;
  1451. ParaCreated: Boolean;
  1452. begin
  1453. FContext:=AContext;
  1454. try
  1455. if AutoInsertBlock then
  1456. if IsExtShort(El.FirstChild) then
  1457. DescrBeginParagraph
  1458. else
  1459. AutoInsertBlock := False;
  1460. Node := El.FirstChild;
  1461. if not ConvertExtShort(AContext, Node) then
  1462. begin
  1463. while Assigned(Node) do
  1464. begin
  1465. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'section') then
  1466. begin
  1467. DescrBeginSectionTitle;
  1468. Child := Node.FirstChild;
  1469. while Assigned(Child) and (Child.NodeType <> ELEMENT_NODE) do
  1470. begin
  1471. if not IsDescrNodeEmpty(Child) then
  1472. Warning(AContext, SErrInvalidContentBeforeSectionTitle);
  1473. Child := Child.NextSibling;
  1474. end;
  1475. if not Assigned(Child) or (Child.NodeName <> 'title') then
  1476. Warning(AContext, SErrSectionTitleExpected)
  1477. else
  1478. ConvertShort(AContext, TDOMElement(Child));
  1479. DescrBeginSectionBody;
  1480. if IsExtShort(Child) then
  1481. begin
  1482. DescrBeginParagraph;
  1483. ParaCreated := True;
  1484. end else
  1485. ParaCreated := False;
  1486. ConvertExtShortOrNonSectionBlocks(AContext, Child.NextSibling);
  1487. if ParaCreated then
  1488. DescrEndParagraph;
  1489. DescrEndSection;
  1490. end else if not ConvertNonSectionBlock(AContext, Node) then
  1491. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  1492. Node := Node.NextSibling;
  1493. end;
  1494. end else
  1495. if AutoInsertBlock then
  1496. DescrEndParagraph;
  1497. finally
  1498. FContext:=Nil;
  1499. end;
  1500. end;
  1501. procedure TFPDocWriter.ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  1502. Node: TDOMNode);
  1503. begin
  1504. if not ConvertExtShort(AContext, Node) then
  1505. while Assigned(Node) do
  1506. begin
  1507. if not ConvertNonSectionBlock(AContext, Node) then
  1508. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  1509. Node := Node.NextSibling;
  1510. end;
  1511. end;
  1512. function TFPDocWriter.ConvertNonSectionBlock(AContext: TPasElement;
  1513. Node: TDOMNode): Boolean;
  1514. procedure ConvertCells(Node: TDOMNode);
  1515. var
  1516. Child: TDOMNode;
  1517. IsEmpty: Boolean;
  1518. begin
  1519. Node := Node.FirstChild;
  1520. IsEmpty := True;
  1521. while Assigned(Node) do
  1522. begin
  1523. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  1524. begin
  1525. DescrBeginTableCell;
  1526. Child := Node.FirstChild;
  1527. if not ConvertExtShort(AContext, Child) then
  1528. while Assigned(Child) do
  1529. begin
  1530. if not ConvertSimpleBlock(AContext, Child) then
  1531. Warning(AContext, SErrInvalidTableContent);
  1532. Child := Child.NextSibling;
  1533. end;
  1534. DescrEndTableCell;
  1535. IsEmpty := False;
  1536. end else
  1537. if IsContentNodeType(Node) then
  1538. Warning(AContext, SErrInvalidTableContent);
  1539. Node := Node.NextSibling;
  1540. end;
  1541. if IsEmpty then
  1542. Warning(AContext, SErrTableRowEmpty);
  1543. end;
  1544. procedure ConvertTable;
  1545. function GetColCount(Node: TDOMNode): Integer;
  1546. begin
  1547. Result := 0;
  1548. Node := Node.FirstChild;
  1549. while Assigned(Node) do
  1550. begin
  1551. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  1552. Inc(Result);
  1553. Node := Node.NextSibling;
  1554. end;
  1555. end;
  1556. var
  1557. s: DOMString;
  1558. HasBorder, CaptionPossible, HeadRowPossible: Boolean;
  1559. ColCount, ThisRowColCount: Integer;
  1560. Subnode: TDOMNode;
  1561. begin
  1562. s := TDOMElement(Node)['border'];
  1563. if s = '1' then
  1564. HasBorder := True
  1565. else
  1566. begin
  1567. HasBorder := False;
  1568. if (Length(s) <> 0) and (s <> '0') then
  1569. Warning(AContext, SErrInvalidBorderValue, ['<table>']);
  1570. end;
  1571. // Determine the number of columns
  1572. ColCount := 0;
  1573. Subnode := Node.FirstChild;
  1574. while Assigned(Subnode) do
  1575. begin
  1576. if Subnode.NodeType = ELEMENT_NODE then
  1577. if (Subnode.NodeName = 'caption') or (Subnode.NodeName = 'th') or
  1578. (Subnode.NodeName = 'tr') then
  1579. begin
  1580. ThisRowColCount := GetColCount(Subnode);
  1581. if ThisRowColCount > ColCount then
  1582. ColCount := ThisRowColCount;
  1583. end;
  1584. Subnode := Subnode.NextSibling;
  1585. end;
  1586. DescrBeginTable(ColCount, HasBorder);
  1587. Node := Node.FirstChild;
  1588. CaptionPossible := True;
  1589. HeadRowPossible := True;
  1590. while Assigned(Node) do
  1591. begin
  1592. if Node.NodeType = ELEMENT_NODE then
  1593. if CaptionPossible and (Node.NodeName = 'caption') then
  1594. begin
  1595. DescrBeginTableCaption;
  1596. if not ConvertExtShort(AContext, Node.FirstChild) then
  1597. Warning(AContext, SErrInvalidTableContent);
  1598. DescrEndTableCaption;
  1599. CaptionPossible := False;
  1600. end else if HeadRowPossible and (Node.NodeName = 'th') then
  1601. begin
  1602. DescrBeginTableHeadRow;
  1603. ConvertCells(Node);
  1604. DescrEndTableHeadRow;
  1605. CaptionPossible := False;
  1606. HeadRowPossible := False;
  1607. end else if Node.NodeName = 'tr' then
  1608. begin
  1609. DescrBeginTableRow;
  1610. ConvertCells(Node);
  1611. DescrEndTableRow;
  1612. end else
  1613. Warning(AContext, SErrInvalidTableContent)
  1614. else if IsContentNodeType(Node) then
  1615. Warning(AContext, SErrInvalidTableContent);
  1616. Node := Node.NextSibling;
  1617. end;
  1618. DescrEndTable;
  1619. end;
  1620. begin
  1621. if Node.NodeType <> ELEMENT_NODE then
  1622. begin
  1623. if Node.NodeType = TEXT_NODE then
  1624. Result := IsWhitespaceNode(TDOMText(Node))
  1625. else
  1626. Result := Node.NodeType = COMMENT_NODE;
  1627. exit;
  1628. end;
  1629. if Node.NodeName = 'remark' then
  1630. begin
  1631. DescrBeginRemark;
  1632. Node := Node.FirstChild;
  1633. if not ConvertExtShort(AContext, Node) then
  1634. while Assigned(Node) do
  1635. begin
  1636. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'table') then
  1637. ConvertTable
  1638. else
  1639. if not ConvertSimpleBlock(AContext, Node) then
  1640. Warning(AContext, SErrInvalidRemarkContent, [Node.NodeName]);
  1641. Node := Node.NextSibling;
  1642. end;
  1643. DescrEndRemark;
  1644. Result := True;
  1645. end else if Node.NodeName = 'table' then
  1646. begin
  1647. ConvertTable;
  1648. Result := True;
  1649. end else
  1650. Result := ConvertSimpleBlock(AContext, Node);
  1651. end;
  1652. function TFPDocWriter.ConvertSimpleBlock(AContext: TPasElement;
  1653. Node: TDOMNode): Boolean;
  1654. procedure ConvertListItems;
  1655. var
  1656. Empty: Boolean;
  1657. begin
  1658. Node := Node.FirstChild;
  1659. Empty := True;
  1660. while Assigned(Node) do
  1661. begin
  1662. if ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  1663. then
  1664. Warning(AContext, SErrInvalidListContent)
  1665. else if Node.NodeType = ELEMENT_NODE then
  1666. if Node.NodeName = 'li' then
  1667. begin
  1668. DescrBeginListItem;
  1669. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  1670. DescrEndListItem;
  1671. Empty := False;
  1672. end else
  1673. Warning(AContext, SErrInvalidElementInList);
  1674. Node := Node.NextSibling;
  1675. end;
  1676. if Empty then
  1677. Warning(AContext, SErrListIsEmpty);
  1678. end;
  1679. procedure ConvertDefinitionList;
  1680. var
  1681. Empty, ExpectDTNext: Boolean;
  1682. begin
  1683. Node := Node.FirstChild;
  1684. Empty := True;
  1685. ExpectDTNext := True;
  1686. while Assigned(Node) do
  1687. begin
  1688. if ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  1689. then
  1690. Warning(AContext, SErrInvalidListContent)
  1691. else if Node.NodeType = ELEMENT_NODE then
  1692. if ExpectDTNext and (Node.NodeName = 'dt') then
  1693. begin
  1694. DescrBeginDefinitionTerm;
  1695. if not ConvertShort(AContext, TDOMElement(Node)) then
  1696. Warning(AContext, SErrInvalidDefinitionTermContent);
  1697. DescrEndDefinitionTerm;
  1698. Empty := False;
  1699. ExpectDTNext := False;
  1700. end else if not ExpectDTNext and (Node.NodeName = 'dd') then
  1701. begin
  1702. DescrBeginDefinitionEntry;
  1703. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  1704. DescrEndDefinitionEntry;
  1705. ExpectDTNext := True;
  1706. end else
  1707. Warning(AContext, SErrInvalidElementInList);
  1708. Node := Node.NextSibling;
  1709. end;
  1710. if Empty then
  1711. Warning(AContext, SErrListIsEmpty)
  1712. else if not ExpectDTNext then
  1713. Warning(AContext, SErrDefinitionEntryMissing);
  1714. end;
  1715. procedure ProcessCodeBody(Node: TDOMNode);
  1716. var
  1717. s: String;
  1718. i, j: Integer;
  1719. begin
  1720. Node := Node.FirstChild;
  1721. S:='';
  1722. while Assigned(Node) do
  1723. begin
  1724. if Node.NodeType = TEXT_NODE then
  1725. begin
  1726. s := s + UTF8Encode(Node.NodeValue);
  1727. j := 1;
  1728. for i := 1 to Length(s) do
  1729. // In XML, linefeeds are normalized to #10 by the parser!
  1730. if s[i] = #10 then
  1731. begin
  1732. DescrWriteCodeLine(Copy(s, j, i - j));
  1733. j := i + 1;
  1734. end;
  1735. if j > 1 then
  1736. s := Copy(s, j, Length(s));
  1737. end;
  1738. Node := Node.NextSibling;
  1739. end;
  1740. if Length(s) > 0 then
  1741. DescrWriteCodeLine(s);
  1742. end;
  1743. var
  1744. s: DOMString;
  1745. HasBorder: Boolean;
  1746. begin
  1747. if Node.NodeType <> ELEMENT_NODE then
  1748. begin
  1749. Result := (Node.NodeType = TEXT_NODE) and IsWhitespaceNode(TDOMText(Node));
  1750. exit;
  1751. end;
  1752. if Node.NodeName = 'p' then
  1753. begin
  1754. DescrBeginParagraph;
  1755. if not ConvertExtShort(AContext, Node.FirstChild) then
  1756. Warning(AContext, SErrInvalidParaContent);
  1757. DescrEndParagraph;
  1758. Result := True;
  1759. end else if Node.NodeName = 'code' then
  1760. begin
  1761. s := TDOMElement(Node)['border'];
  1762. if s = '1' then
  1763. HasBorder := True
  1764. else
  1765. begin
  1766. if (Length(s) > 0) and (s <> '0') then
  1767. Warning(AContext, SErrInvalidBorderValue, ['<code>']);
  1768. end;
  1769. DescrBeginCode(HasBorder, UTF8Encode(TDOMElement(Node)['highlighter']));
  1770. ProcessCodeBody(Node);
  1771. DescrEndCode;
  1772. Result := True;
  1773. end else if Node.NodeName = 'pre' then
  1774. begin
  1775. DescrBeginCode(False, 'none');
  1776. ProcessCodeBody(Node);
  1777. DescrEndCode;
  1778. Result := True;
  1779. end else if Node.NodeName = 'ul' then
  1780. begin
  1781. DescrBeginUnorderedList;
  1782. ConvertListItems;
  1783. DescrEndUnorderedList;
  1784. Result := True;
  1785. end else if Node.NodeName = 'ol' then
  1786. begin
  1787. DescrBeginOrderedList;
  1788. ConvertListItems;
  1789. DescrEndOrderedList;
  1790. Result := True;
  1791. end else if Node.NodeName = 'dl' then
  1792. begin
  1793. DescrBeginDefinitionList;
  1794. ConvertDefinitionList;
  1795. DescrEndDefinitionList;
  1796. Result := True;
  1797. end else if Node.NodeName = 'img' then
  1798. begin
  1799. begin
  1800. ConvertImage(Node as TDomElement);
  1801. Result:=True;
  1802. end;
  1803. end else
  1804. Result := False;
  1805. end;
  1806. procedure TFPDocWriter.ConvertImage ( El: TDomElement ) ;
  1807. Var
  1808. FN,Cap,LinkName : DOMString;
  1809. begin
  1810. FN:=El['file'];
  1811. Cap:=El['caption'];
  1812. LinkName:=El['name'];
  1813. FN:=UTF8decode(ChangeFileExt(UTF8Encode(FN),ImageExtension));
  1814. DescrWriteImageEl(FN,Cap,LinkName);
  1815. end;
  1816. procedure TFPDocWriter.DescrEmitNotesHeader(AContext: TPasElement);
  1817. begin
  1818. DescrWriteLinebreak;
  1819. DescrBeginBold;
  1820. DescrWriteText(UTF8Decode(SDocNotes));
  1821. DescrEndBold;
  1822. DescrWriteLinebreak;
  1823. end;
  1824. procedure TFPDocWriter.DescrEmitNotesFooter(AContext: TPasElement);
  1825. begin
  1826. DescrWriteLinebreak;
  1827. end;
  1828. Constructor TTopicElement.Create(const AName: String; AParent: TPasElement);
  1829. begin
  1830. Inherited Create(AName,AParent);
  1831. SubTopics:=TList.Create;
  1832. end;
  1833. Destructor TTopicElement.Destroy;
  1834. begin
  1835. // Actual subtopics are freed by TFPDocWriter Topics list.
  1836. SubTopics.Free;
  1837. Inherited;
  1838. end;
  1839. function TFPDocWriter.WriteDescr ( Element: TPasElement ) : TDocNode;
  1840. begin
  1841. Result:=Engine.FindDocNode(Element);
  1842. WriteDescr(ELement,Result);
  1843. end;
  1844. procedure TFPDocWriter.WriteDescr(Element: TPasElement; DocNode: TDocNode);
  1845. begin
  1846. if Assigned(DocNode) then
  1847. begin
  1848. if not IsDescrNodeEmpty(DocNode.Descr) then
  1849. WriteDescr(Element, DocNode.Descr)
  1850. else if not IsDescrNodeEmpty(DocNode.ShortDescr) then
  1851. WriteDescr(Element, DocNode.ShortDescr);
  1852. end;
  1853. end;
  1854. procedure TFPDocWriter.WriteDescr(AContext: TPasElement; DescrNode: TDOMElement);
  1855. begin
  1856. if Assigned(DescrNode) then
  1857. ConvertDescr(AContext, DescrNode, False);
  1858. end;
  1859. procedure TFPDocWriter.FPDocError(Msg: String);
  1860. begin
  1861. Raise EFPDocWriterError.Create(Msg);
  1862. end;
  1863. procedure TFPDocWriter.FPDocError(Fmt: String; Args: array of const);
  1864. begin
  1865. FPDocError(Format(Fmt,Args));
  1866. end;
  1867. function TFPDocWriter.ShowMember(M: TPasElement): boolean;
  1868. begin
  1869. Result:=not ((M.Visibility=visPrivate) and Engine.HidePrivate);
  1870. If Result then
  1871. Result:=Not ((M.Visibility=visProtected) and Engine.HideProtected)
  1872. end;
  1873. procedure TFPDocWriter.GetMethodList ( ClassDecl: TPasClassType;
  1874. List: TStringList ) ;
  1875. Var
  1876. I : Integer;
  1877. M : TPasElement;
  1878. begin
  1879. List.Clear;
  1880. List.Sorted:=False;
  1881. for i := 0 to ClassDecl.Members.Count - 1 do
  1882. begin
  1883. M:=TPasElement(ClassDecl.Members[i]);
  1884. if M.InheritsFrom(TPasProcedureBase) and ShowMember(M) then
  1885. List.AddObject(M.Name,M);
  1886. end;
  1887. List.Sorted:=False;
  1888. end;
  1889. initialization
  1890. InitWriterList;
  1891. finalization
  1892. DoneWriterList;
  1893. end.