dwriter.pp 58 KB

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