dwriter.pp 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139
  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. end;
  748. class procedure TMultiFileDocWriter.Usage(List: TStrings);
  749. begin
  750. List.AddStrings(['--use-subpagenames', SUsageSubNames]);
  751. List.AddStrings(['--only-pages=LIST', SUsageOnlyPages]);
  752. end;
  753. function TMultiFileDocWriter.InterPretOption(const Cmd, Arg: String): boolean;
  754. Var
  755. I : Integer;
  756. FN : String;
  757. begin
  758. Writeln('Cmd : ',Cmd);
  759. Result := True;
  760. if Cmd = '--use-subpagenames' then
  761. FSubPageNames:= True
  762. else
  763. if Cmd = '--only-pages' then
  764. begin
  765. Result:=Arg<>'';
  766. if Result then
  767. begin
  768. if Arg[1]='@' then
  769. begin
  770. FN:=Copy(Arg,2,Length(Arg)-1);
  771. OutputPageNames.LoadFromFile(FN);
  772. end
  773. else
  774. begin
  775. For I:=1 to WordCount(Arg,[',']) do
  776. OutputPageNames.Add(ExtractWord(I,Arg,[',']));
  777. end;
  778. Writeln('OutputPagenames ',OutputPagenames.CommaText);
  779. end
  780. end
  781. else
  782. Result:=inherited InterPretOption(Cmd, Arg);
  783. end;
  784. { TWriterRecord }
  785. constructor TWriterRecord.Create(AClass: TFPDocWriterClass; const AName,
  786. ADescr: String);
  787. begin
  788. FClass:=AClass;
  789. FName:=AName;
  790. FDescription:=ADescr;
  791. end;
  792. Var
  793. Writers : TStringList;
  794. Procedure InitWriterList;
  795. begin
  796. Writers:=TStringList.Create;
  797. Writers.Sorted:=True;
  798. end;
  799. Procedure DoneWriterList;
  800. Var
  801. I : Integer;
  802. begin
  803. For I:=Writers.Count-1 downto 0 do
  804. Writers.Objects[i].Free;
  805. FreeAndNil(Writers);
  806. end;
  807. procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName, ADescr : String);
  808. begin
  809. If Writers.IndexOf(AName)<>-1 then
  810. Raise EFPDocWriterError.CreateFmt(SErralreadyRegistered,[ANAme]);
  811. Writers.AddObject(AName,TWriterRecord.Create(AClass,AName,ADescr));
  812. end;
  813. function FindWriterClass(AName : String) : Integer;
  814. begin
  815. Result:=Writers.IndexOf(AName);
  816. end;
  817. function GetWriterClass(AName : String) : TFPDocWriterClass;
  818. Var
  819. Index : Integer;
  820. begin
  821. Index:=FindWriterClass(AName);
  822. If Index=-1 then
  823. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  824. Result:=(Writers.Objects[Index] as TWriterRecord).FClass;
  825. end;
  826. // UnRegister backend
  827. Procedure UnRegisterWriter(Const AName : String);
  828. Var
  829. Index : Integer;
  830. begin
  831. Index:=Writers.IndexOf(AName);
  832. If Index=-1 then
  833. Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
  834. Writers.Objects[Index].Free;
  835. Writers.Delete(Index);
  836. end;
  837. Procedure EnumWriters(List : TStrings);
  838. Var
  839. I : Integer;
  840. begin
  841. List.Clear;
  842. For I:=0 to Writers.Count-1 do
  843. With (Writers.Objects[I] as TWriterRecord) do
  844. List.Add(FName+'='+FDescription);
  845. end;
  846. function IsWhitespaceNode(Node: TDOMText): Boolean;
  847. var
  848. I,L: Integer;
  849. S: DOMString;
  850. P : PWideChar;
  851. begin
  852. S := Node.Data;
  853. Result := True;
  854. I:=0;
  855. L:=Length(S);
  856. P:=PWideChar(S);
  857. While Result and (I<L) do
  858. begin
  859. Result:=P^ in [#32,#10,#9,#13];
  860. Inc(P);
  861. Inc(I);
  862. end;
  863. end;
  864. { ---------------------------------------------------------------------
  865. TFileAllocator
  866. ---------------------------------------------------------------------}
  867. function TFileAllocator.GetFilePostfix(ASubindex: Integer): String;
  868. begin
  869. if FSubPageNames then
  870. case ASubindex of
  871. IdentifierIndex: Result:='';
  872. ResstrSubindex: Result:='reestr';
  873. ConstsSubindex: Result:='consts';
  874. TypesSubindex: Result:='types';
  875. ClassesSubindex: Result:='classes';
  876. ProcsSubindex: Result:='procs';
  877. VarsSubindex: Result:='vars';
  878. TopicsSubIndex: Result:='topics';
  879. IndexSubIndex: Result:='indexes';
  880. ClassHierarchySubIndex: Result:='class-tree';
  881. InterfaceHierarchySubIndex: Result:='interface-tree';
  882. PropertiesByInheritanceSubindex: Result:='props';
  883. PropertiesByNameSubindex: Result:='props-n';
  884. MethodsByInheritanceSubindex: Result:='methods';
  885. MethodsByNameSubindex: Result:='methods-n';
  886. EventsByInheritanceSubindex: Result:='events';
  887. EventsByNameSubindex: Result:='events-n';
  888. end
  889. else
  890. Result:= IntToStr(ASubindex);
  891. end;
  892. procedure TFileAllocator.Create();
  893. begin
  894. FSubPageNames:= False;
  895. end;
  896. procedure TFileAllocator.AllocFilename(AElement: TPasElement;
  897. ASubindex: Integer);
  898. begin
  899. end;
  900. function TFileAllocator.GetRelativePathToTop(AElement: TPasElement): String;
  901. begin
  902. Result:='';
  903. end;
  904. function TFileAllocator.GetCSSFilename(ARelativeTo: TPasElement): DOMString;
  905. begin
  906. Result := Utf8Decode(GetRelativePathToTop(ARelativeTo)) + 'fpdoc.css';
  907. end;
  908. { ---------------------------------------------------------------------
  909. TLongNameFileAllocator
  910. ---------------------------------------------------------------------}
  911. constructor TLongNameFileAllocator.Create(const AExtension: String);
  912. begin
  913. inherited Create;
  914. FExtension := AExtension;
  915. end;
  916. function TLongNameFileAllocator.GetFilename(AElement: TPasElement; ASubindex: Integer): String;
  917. var
  918. n,s: String;
  919. i: Integer;
  920. MElement: TPasElement;
  921. begin
  922. Result:='';
  923. if AElement.ClassType = TPasPackage then
  924. Result := 'index'
  925. else if AElement.ClassType = TPasModule then
  926. Result := LowerCase(AElement.Name) + PathDelim + 'index'
  927. else
  928. begin
  929. if AElement is TPasOperator then
  930. begin
  931. if Assigned(AElement.Parent) then
  932. result:=LowerCase(AElement.Parent.PathName);
  933. With TPasOperator(aElement) do
  934. Result:= Result + 'op-'+OperatorTypeToOperatorName(OperatorType);
  935. s := '';
  936. N:=LowerCase(aElement.Name); // Should not contain any weird chars.
  937. Delete(N,1,Pos('(',N));
  938. i := 1;
  939. Repeat
  940. I:=Pos(',',N);
  941. if I=0 then
  942. I:=Pos(')',N);
  943. if I>1 then
  944. begin
  945. if (S<>'') then
  946. S:=S+'-';
  947. S:=S+Copy(N,1,I-1);
  948. end;
  949. Delete(N,1,I);
  950. until I=0;
  951. // First char is maybe :
  952. if (N<>'') and (N[1]=':') then
  953. Delete(N,1,1);
  954. Result:=Result + '-'+ s + '-' + N;
  955. end else
  956. Result := LowerCase(AElement.PathName);
  957. // cut off Package Name
  958. MElement:= AElement.GetModule;
  959. if Assigned(MElement) then
  960. AElement:= MElement;
  961. Result := Copy(Result, Length(AElement.Parent.Name) + 2, MaxInt);
  962. // to skip dots in unit name
  963. i := Length(AElement.Name);
  964. while (i <= Length(Result)) and (Result[i] <> '.') do
  965. Inc(i);
  966. if (i <= Length(Result)) and (i > 0) then
  967. Result[i] := PathDelim;
  968. end;
  969. if ASubindex > 0 then
  970. Result := Result + '-' + GetFilePostfix(ASubindex);
  971. Result := Result + Extension;
  972. end;
  973. function TLongNameFileAllocator.GetRelativePathToTop(AElement: TPasElement): String;
  974. begin
  975. if (AElement.ClassType=TPasPackage) then
  976. Result := ''
  977. else if (AElement.ClassType=TTopicElement) then
  978. begin
  979. If (AElement.Parent.ClassType=TTopicElement) then
  980. Result:='../'+GetRelativePathToTop(AElement.Parent)
  981. else if (AElement.Parent.ClassType=TPasPackage) then
  982. Result:=''
  983. else if (AElement.Parent.ClassType=TPasModule) then
  984. Result:='../';
  985. end
  986. else
  987. Result := '../';
  988. end;
  989. { ---------------------------------------------------------------------
  990. TFPDocWriter
  991. ---------------------------------------------------------------------}
  992. {
  993. fmtIPF:
  994. begin
  995. if Length(Engine.Output) = 0 then
  996. WriteLn(SCmdLineOutputOptionMissing)
  997. else
  998. CreateIPFDocForPackage(Engine.Package, Engine);
  999. end;
  1000. }
  1001. constructor TFPDocWriter.Create ( APackage: TPasPackage; AEngine: TFPDocEngine
  1002. ) ;
  1003. begin
  1004. inherited Create;
  1005. FEngine := AEngine;
  1006. FPackage := APackage;
  1007. FTopics:=Tlist.Create;
  1008. FImgExt:='.png';
  1009. TreeClass:= TClassTreeBuilder.Create(FEngine, FPackage, okWithFields);
  1010. TreeInterface:= TClassTreeBuilder.Create(FEngine, FPackage, [okInterface]);
  1011. CreateClassTree;
  1012. end;
  1013. destructor TFPDocWriter.Destroy;
  1014. Var
  1015. i : integer;
  1016. begin
  1017. For I:=0 to FTopics.Count-1 do
  1018. TTopicElement(FTopics[i]).Free;
  1019. FTopics.Free;
  1020. TreeClass.free;
  1021. TreeInterface.Free;
  1022. Inherited;
  1023. end;
  1024. procedure TFPDocWriter.AddModuleIdentifiers(AModule : TPasModule; L : TStrings);
  1025. begin
  1026. if assigned(AModule.InterfaceSection) Then
  1027. begin
  1028. AddElementsFromList(L,AModule.InterfaceSection.Consts);
  1029. AddElementsFromList(L,AModule.InterfaceSection.Types);
  1030. AddElementsFromList(L,AModule.InterfaceSection.Functions);
  1031. AddElementsFromList(L,AModule.InterfaceSection.Classes);
  1032. AddElementsFromList(L,AModule.InterfaceSection.Variables);
  1033. AddElementsFromList(L,AModule.InterfaceSection.ResStrings);
  1034. end;
  1035. end;
  1036. function TFPDocWriter.InterpretOption(const Cmd, Arg: String): Boolean;
  1037. begin
  1038. Result:=False;
  1039. end;
  1040. class function TFPDocWriter.FileNameExtension: String;
  1041. begin
  1042. //Override in linear writers with the expected extension.
  1043. Result := ''; //Output must not contain an extension.
  1044. end;
  1045. class procedure TFPDocWriter.Usage(List: TStrings);
  1046. begin
  1047. // Do nothing.
  1048. end;
  1049. class procedure TFPDocWriter.SplitImport(var AFilename, ALinkPrefix: String);
  1050. var
  1051. i: integer;
  1052. begin
  1053. //override in HTML and CHM writer
  1054. i := Pos(',', AFilename);
  1055. if i > 0 then
  1056. begin //split CSV into filename and prefix
  1057. ALinkPrefix := Copy(AFilename,i+1,Length(AFilename));
  1058. SetLength(AFilename, i-1);
  1059. end;
  1060. end;
  1061. procedure TFPDocWriter.WriteDocumentation;
  1062. begin
  1063. PrepareDocumentation();
  1064. DoWriteDocumentation();
  1065. OutputResults();
  1066. end;
  1067. function TFPDocWriter.FindTopicElement ( Node: TDocNode ) : TTopicElement;
  1068. Var
  1069. I : Integer;
  1070. begin
  1071. Result:=Nil;
  1072. I:=FTopics.Count-1;
  1073. While (I>=0) and (Result=Nil) do
  1074. begin
  1075. If (TTopicElement(FTopics[i]).TopicNode=Node) Then
  1076. Result:=TTopicElement(FTopics[i]);
  1077. Dec(I);
  1078. end;
  1079. end;
  1080. procedure TFPDocWriter.DescrWriteImageEl(const AFileName, ACaption,
  1081. ALinkName: DOMString);
  1082. begin
  1083. DoLog('%s : No support for images yet: %s (caption: "%s")',[ClassName,AFileName,ACaption]);
  1084. end;
  1085. procedure TFPDocWriter.PrepareDocumentation;
  1086. begin
  1087. // Ancestors can call AllocatePages();CreateAllocator(); into base class
  1088. end;
  1089. { ---------------------------------------------------------------------
  1090. Generic documentation node conversion
  1091. ---------------------------------------------------------------------}
  1092. function IsContentNodeType(Node: TDOMNode): Boolean;
  1093. begin
  1094. Result := (Node.NodeType = ELEMENT_NODE) or
  1095. ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or
  1096. (Node.NodeType = ENTITY_REFERENCE_NODE);
  1097. end;
  1098. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String);
  1099. begin
  1100. if (AContext<>nil) then
  1101. DoLog('[%s] %s',[AContext.PathName,AMsg])
  1102. else
  1103. DoLog('[<no context>] %s', [AMsg]);
  1104. end;
  1105. procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String;
  1106. const Args: array of const);
  1107. begin
  1108. Warning(AContext, Format(AMsg, Args));
  1109. end;
  1110. function TFPDocWriter.IsDescrNodeEmpty(Node: TDOMNode): Boolean;
  1111. var
  1112. Child: TDOMNode;
  1113. begin
  1114. if (not Assigned(Node)) or (not Assigned(Node.FirstChild)) then
  1115. Result := True
  1116. else
  1117. begin
  1118. Child := Node.FirstChild;
  1119. while Assigned(Child) do
  1120. begin
  1121. if (Child.NodeType = ELEMENT_NODE) or (Child.NodeType = TEXT_NODE) or
  1122. (Child.NodeType = ENTITY_REFERENCE_NODE) then
  1123. begin
  1124. Result := False;
  1125. exit;
  1126. end;
  1127. Child := Child.NextSibling;
  1128. end;
  1129. end;
  1130. Result := True;
  1131. end;
  1132. { Check wether the nodes starting with the node given as argument make up an
  1133. 'extshort' production. }
  1134. function TFPDocWriter.IsExtShort(Node: TDOMNode): Boolean;
  1135. begin
  1136. while Assigned(Node) do
  1137. begin
  1138. if Node.NodeType = ELEMENT_NODE then
  1139. if (Node.NodeName <> 'br') and
  1140. (Node.NodeName <> 'link') and
  1141. (Node.NodeName <> 'url') and
  1142. (Node.NodeName <> 'b') and
  1143. (Node.NodeName <> 'file') and
  1144. (Node.NodeName <> 'i') and
  1145. (Node.NodeName <> 'kw') and
  1146. (Node.NodeName <> 'printshort') and
  1147. (Node.NodeName <> 'var') then
  1148. begin
  1149. Result := False;
  1150. exit;
  1151. end;
  1152. Node := Node.NextSibling;
  1153. end;
  1154. Result := True;
  1155. end;
  1156. function TFPDocWriter.ConvertShort(AContext: TPasElement;
  1157. El: TDOMElement): Boolean;
  1158. var
  1159. Node: TDOMNode;
  1160. begin
  1161. Result := False;
  1162. if not Assigned(El) then
  1163. exit;
  1164. FContext:=AContext;
  1165. try
  1166. Node := El.FirstChild;
  1167. while Assigned(Node) do
  1168. begin
  1169. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  1170. ConvertLink(AContext, TDOMElement(Node))
  1171. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'url') then
  1172. ConvertURL(AContext, TDOMElement(Node))
  1173. else
  1174. if not ConvertBaseShort(AContext, Node) then
  1175. exit;
  1176. Node := Node.NextSibling;
  1177. end;
  1178. Result := True;
  1179. finally
  1180. FContext:=Nil;
  1181. end;
  1182. end;
  1183. function TFPDocWriter.ConvertNotes(AContext: TPasElement; El: TDOMElement
  1184. ): Boolean;
  1185. Var
  1186. L : TFPList;
  1187. N : TDomNode;
  1188. I : Integer;
  1189. B : Boolean;
  1190. begin
  1191. Result:=Assigned(El) and EmitNotes;
  1192. If Not Result then
  1193. exit;
  1194. L:=TFPList.Create;
  1195. try
  1196. N:=El.FirstChild;
  1197. While Assigned(N) do
  1198. begin
  1199. If (N.NodeType=ELEMENT_NODE) and (N.NodeName='note') then
  1200. begin
  1201. B:=True;
  1202. if Assigned(FBeforeEmitNote) then
  1203. FBeforeEmitNote(Self,TDomElement(N),B);
  1204. If B then
  1205. L.Add(N);
  1206. end;
  1207. N:=N.NextSibling;
  1208. end;
  1209. Result:=L.Count>0;
  1210. If Not Result then
  1211. exit;
  1212. DescrEmitNotesHeader(AContext);
  1213. DescrBeginUnorderedList;
  1214. For i:=0 to L.Count-1 do
  1215. begin
  1216. DescrBeginListItem;
  1217. ConvertExtShortOrNonSectionBlocks(AContext, TDOMNode(L[i]).FirstChild);
  1218. DescrEndListItem;
  1219. end;
  1220. DescrEndUnorderedList;
  1221. DescrEmitNotesFooter(AContext);
  1222. finally
  1223. L.Free;
  1224. end;
  1225. end;
  1226. function TFPDocWriter.ConvertBaseShort(AContext: TPasElement;
  1227. Node: TDOMNode): Boolean;
  1228. function ConvertText: DOMString;
  1229. var
  1230. s: DOMString;
  1231. i: Integer;
  1232. begin
  1233. if Node.NodeType = TEXT_NODE then
  1234. begin
  1235. s := Node.NodeValue;
  1236. i := 1;
  1237. Result:='';
  1238. while i <= Length(s) do
  1239. if s[i] = #13 then
  1240. begin
  1241. Result := Result + ' ';
  1242. Inc(i);
  1243. if s[i] = #10 then
  1244. Inc(i);
  1245. end else if s[i] = #10 then
  1246. begin
  1247. Result := Result + ' ';
  1248. Inc(i);
  1249. end else
  1250. begin
  1251. Result := Result + s[i];
  1252. Inc(i);
  1253. end;
  1254. end else if Node.NodeType = ENTITY_REFERENCE_NODE then
  1255. if Node.NodeName = 'fpc' then
  1256. Result := 'Free Pascal'
  1257. else if Node.NodeName = 'delphi' then
  1258. Result := 'Delphi'
  1259. else
  1260. begin
  1261. Warning(AContext, Format(SErrUnknownEntityReference, [Node.NodeName]));
  1262. Result := Node.NodeName;
  1263. end
  1264. else if Node.NodeType = ELEMENT_NODE then
  1265. SetLength(Result, 0);
  1266. end;
  1267. function ConvertTextContent: DOMString;
  1268. begin
  1269. Result:='';
  1270. Node := Node.FirstChild;
  1271. while Assigned(Node) do
  1272. begin
  1273. Result := Result + ConvertText;
  1274. Node := Node.NextSibling;
  1275. end;
  1276. end;
  1277. var
  1278. El, DescrEl: TDOMElement;
  1279. hlp : TPasElement;
  1280. begin
  1281. Result := True;
  1282. if Node.NodeType = ELEMENT_NODE then
  1283. if Node.NodeName = 'b' then
  1284. begin
  1285. DescrBeginBold;
  1286. ConvertBaseShortList(AContext, Node, False);
  1287. DescrEndBold;
  1288. end else
  1289. if Node.NodeName = 'i' then
  1290. begin
  1291. DescrBeginItalic;
  1292. ConvertBaseShortList(AContext, Node, False);
  1293. DescrEndItalic;
  1294. end else
  1295. if Node.NodeName = 'em' then
  1296. begin
  1297. DescrBeginEmph;
  1298. ConvertBaseShortList(AContext, Node, False);
  1299. DescrEndEmph;
  1300. end else
  1301. if Node.NodeName = 'u' then
  1302. begin
  1303. DescrBeginUnderline;
  1304. ConvertBaseShortList(AContext, Node, False);
  1305. DescrEndUnderline;
  1306. end else
  1307. if Node.NodeName = 'file' then
  1308. DescrWriteFileEl(ConvertTextContent)
  1309. else if Node.NodeName = 'kw' then
  1310. DescrWriteKeywordEl(ConvertTextContent)
  1311. else if Node.NodeName = 'printshort' then
  1312. begin
  1313. El := TDOMElement(Node);
  1314. hlp:=AContext;
  1315. while assigned(hlp) and not (hlp is TPasModule) do
  1316. hlp:=hlp.parent;
  1317. if not (hlp is TPasModule) then
  1318. hlp:=nil;
  1319. DescrEl := Engine.FindShortDescr(TPasModule(hlp), UTF8Encode(El['id']));
  1320. if Assigned(DescrEl) then
  1321. ConvertShort(AContext, DescrEl)
  1322. else
  1323. begin
  1324. Warning(AContext, Format(SErrUnknownPrintShortID, [El['id']]));
  1325. DescrBeginBold;
  1326. DescrWriteText('#ShortDescr:' + El['id']);
  1327. DescrEndBold;
  1328. end;
  1329. end else if Node.NodeName = 'var' then
  1330. DescrWriteVarEl(ConvertTextContent)
  1331. else
  1332. Result := False
  1333. else
  1334. DescrWriteText(ConvertText);
  1335. end;
  1336. procedure TFPDocWriter.ConvertBaseShortList(AContext: TPasElement;
  1337. Node: TDOMNode; MayBeEmpty: Boolean);
  1338. var
  1339. Child: TDOMNode;
  1340. begin
  1341. Child := Node.FirstChild;
  1342. while Assigned(Child) do
  1343. begin
  1344. if not ConvertBaseShort(AContext, Child) then
  1345. Warning(AContext, SErrInvalidShortDescr)
  1346. else
  1347. MayBeEmpty := True;
  1348. Child := Child.NextSibling;
  1349. end;
  1350. if not MayBeEmpty then
  1351. Warning(AContext, SErrInvalidShortDescr)
  1352. end;
  1353. procedure TFPDocWriter.ConvertLink(AContext: TPasElement; El: TDOMElement);
  1354. begin
  1355. DescrBeginLink(El['id']);
  1356. if not IsDescrNodeEmpty(El) then
  1357. ConvertBaseShortList(AContext, El, True)
  1358. else
  1359. DescrWriteText(El['id']);
  1360. DescrEndLink;
  1361. end;
  1362. procedure TFPDocWriter.ConvertURL(AContext: TPasElement; El: TDOMElement);
  1363. begin
  1364. DescrBeginURL(El['href']);
  1365. if not IsDescrNodeEmpty(El) then
  1366. ConvertBaseShortList(AContext, El, True)
  1367. else
  1368. DescrWriteText(El['href']);
  1369. DescrEndURL;
  1370. end;
  1371. procedure TFPDocWriter.AddElementsFromList ( L: TStrings; List: TFPList;
  1372. UsePathName: Boolean ) ;
  1373. Var
  1374. I : Integer;
  1375. El : TPasElement;
  1376. N : TDocNode;
  1377. begin
  1378. For I:=0 to List.Count-1 do
  1379. begin
  1380. El:=TPasElement(List[I]);
  1381. N:=Engine.FindDocNode(El);
  1382. if (N=Nil) or (not N.IsSkipped) then
  1383. begin
  1384. if UsePathName then
  1385. L.AddObject(El.PathName,El)
  1386. else
  1387. L.AddObject(El.Name,El);
  1388. If el is TPasEnumType then
  1389. AddElementsFromList(L,TPasEnumType(el).Values);
  1390. end;
  1391. end;
  1392. end;
  1393. procedure TFPDocWriter.CreateClassTree;
  1394. var
  1395. L: TStringList;
  1396. M: TPasModule;
  1397. I:Integer;
  1398. begin
  1399. L:=TStringList.Create;
  1400. try
  1401. For I:=0 to Package.Modules.Count-1 do
  1402. begin
  1403. M:=TPasModule(Package.Modules[i]);
  1404. if Not (M is TPasExternalModule) and assigned(M.InterfaceSection) then
  1405. Self.AddElementsFromList(L,M.InterfaceSection.Classes,True)
  1406. end;
  1407. // You can see this tree by using --format=xml option
  1408. TreeClass.BuildTree(L);
  1409. TreeInterface.BuildTree(L);
  1410. Finally
  1411. L.Free;
  1412. end;
  1413. end;
  1414. procedure TFPDocWriter.DoLog(const Msg: String);
  1415. begin
  1416. If Assigned(FEngine.OnLog) then
  1417. FEngine.OnLog(Self,Msg);
  1418. end;
  1419. procedure TFPDocWriter.DoLog(const Fmt: String; Args: array of const);
  1420. begin
  1421. DoLog(Format(Fmt,Args));
  1422. end;
  1423. procedure TFPDocWriter.OutputResults();
  1424. begin
  1425. DoLog('Package: %s - Documentation process finished.', [FPackage.Name]);
  1426. end;
  1427. function TFPDocWriter.ConvertExtShort(AContext: TPasElement;
  1428. Node: TDOMNode): Boolean;
  1429. begin
  1430. Result := False;
  1431. while Assigned(Node) do
  1432. begin
  1433. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
  1434. ConvertLink(AContext, TDOMElement(Node))
  1435. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'url') then
  1436. ConvertURL(AContext, TDOMElement(Node))
  1437. else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'br') then
  1438. DescrWriteLinebreak
  1439. else
  1440. if not ConvertBaseShort(AContext, Node) then
  1441. exit;
  1442. Node := Node.NextSibling;
  1443. end;
  1444. Result := True;
  1445. end;
  1446. procedure TFPDocWriter.ConvertDescr(AContext: TPasElement; El: TDOMElement;
  1447. AutoInsertBlock: Boolean);
  1448. var
  1449. Node, Child: TDOMNode;
  1450. ParaCreated: Boolean;
  1451. begin
  1452. FContext:=AContext;
  1453. try
  1454. if AutoInsertBlock then
  1455. if IsExtShort(El.FirstChild) then
  1456. DescrBeginParagraph
  1457. else
  1458. AutoInsertBlock := False;
  1459. Node := El.FirstChild;
  1460. if not ConvertExtShort(AContext, Node) then
  1461. begin
  1462. while Assigned(Node) do
  1463. begin
  1464. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'section') then
  1465. begin
  1466. DescrBeginSectionTitle;
  1467. Child := Node.FirstChild;
  1468. while Assigned(Child) and (Child.NodeType <> ELEMENT_NODE) do
  1469. begin
  1470. if not IsDescrNodeEmpty(Child) then
  1471. Warning(AContext, SErrInvalidContentBeforeSectionTitle);
  1472. Child := Child.NextSibling;
  1473. end;
  1474. if not Assigned(Child) or (Child.NodeName <> 'title') then
  1475. Warning(AContext, SErrSectionTitleExpected)
  1476. else
  1477. ConvertShort(AContext, TDOMElement(Child));
  1478. DescrBeginSectionBody;
  1479. if IsExtShort(Child) then
  1480. begin
  1481. DescrBeginParagraph;
  1482. ParaCreated := True;
  1483. end else
  1484. ParaCreated := False;
  1485. ConvertExtShortOrNonSectionBlocks(AContext, Child.NextSibling);
  1486. if ParaCreated then
  1487. DescrEndParagraph;
  1488. DescrEndSection;
  1489. end else if not ConvertNonSectionBlock(AContext, Node) then
  1490. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  1491. Node := Node.NextSibling;
  1492. end;
  1493. end else
  1494. if AutoInsertBlock then
  1495. DescrEndParagraph;
  1496. finally
  1497. FContext:=Nil;
  1498. end;
  1499. end;
  1500. procedure TFPDocWriter.ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
  1501. Node: TDOMNode);
  1502. begin
  1503. if not ConvertExtShort(AContext, Node) then
  1504. while Assigned(Node) do
  1505. begin
  1506. if not ConvertNonSectionBlock(AContext, Node) then
  1507. Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
  1508. Node := Node.NextSibling;
  1509. end;
  1510. end;
  1511. function TFPDocWriter.ConvertNonSectionBlock(AContext: TPasElement;
  1512. Node: TDOMNode): Boolean;
  1513. procedure ConvertCells(Node: TDOMNode);
  1514. var
  1515. Child: TDOMNode;
  1516. IsEmpty: Boolean;
  1517. begin
  1518. Node := Node.FirstChild;
  1519. IsEmpty := True;
  1520. while Assigned(Node) do
  1521. begin
  1522. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  1523. begin
  1524. DescrBeginTableCell;
  1525. Child := Node.FirstChild;
  1526. if not ConvertExtShort(AContext, Child) then
  1527. while Assigned(Child) do
  1528. begin
  1529. if not ConvertSimpleBlock(AContext, Child) then
  1530. Warning(AContext, SErrInvalidTableContent);
  1531. Child := Child.NextSibling;
  1532. end;
  1533. DescrEndTableCell;
  1534. IsEmpty := False;
  1535. end else
  1536. if IsContentNodeType(Node) then
  1537. Warning(AContext, SErrInvalidTableContent);
  1538. Node := Node.NextSibling;
  1539. end;
  1540. if IsEmpty then
  1541. Warning(AContext, SErrTableRowEmpty);
  1542. end;
  1543. procedure ConvertTable;
  1544. function GetColCount(Node: TDOMNode): Integer;
  1545. begin
  1546. Result := 0;
  1547. Node := Node.FirstChild;
  1548. while Assigned(Node) do
  1549. begin
  1550. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
  1551. Inc(Result);
  1552. Node := Node.NextSibling;
  1553. end;
  1554. end;
  1555. var
  1556. s: DOMString;
  1557. HasBorder, CaptionPossible, HeadRowPossible: Boolean;
  1558. ColCount, ThisRowColCount: Integer;
  1559. Subnode: TDOMNode;
  1560. begin
  1561. s := TDOMElement(Node)['border'];
  1562. if s = '1' then
  1563. HasBorder := True
  1564. else
  1565. begin
  1566. HasBorder := False;
  1567. if (Length(s) <> 0) and (s <> '0') then
  1568. Warning(AContext, SErrInvalidBorderValue, ['<table>']);
  1569. end;
  1570. // Determine the number of columns
  1571. ColCount := 0;
  1572. Subnode := Node.FirstChild;
  1573. while Assigned(Subnode) do
  1574. begin
  1575. if Subnode.NodeType = ELEMENT_NODE then
  1576. if (Subnode.NodeName = 'caption') or (Subnode.NodeName = 'th') or
  1577. (Subnode.NodeName = 'tr') then
  1578. begin
  1579. ThisRowColCount := GetColCount(Subnode);
  1580. if ThisRowColCount > ColCount then
  1581. ColCount := ThisRowColCount;
  1582. end;
  1583. Subnode := Subnode.NextSibling;
  1584. end;
  1585. DescrBeginTable(ColCount, HasBorder);
  1586. Node := Node.FirstChild;
  1587. CaptionPossible := True;
  1588. HeadRowPossible := True;
  1589. while Assigned(Node) do
  1590. begin
  1591. if Node.NodeType = ELEMENT_NODE then
  1592. if CaptionPossible and (Node.NodeName = 'caption') then
  1593. begin
  1594. DescrBeginTableCaption;
  1595. if not ConvertExtShort(AContext, Node.FirstChild) then
  1596. Warning(AContext, SErrInvalidTableContent);
  1597. DescrEndTableCaption;
  1598. CaptionPossible := False;
  1599. end else if HeadRowPossible and (Node.NodeName = 'th') then
  1600. begin
  1601. DescrBeginTableHeadRow;
  1602. ConvertCells(Node);
  1603. DescrEndTableHeadRow;
  1604. CaptionPossible := False;
  1605. HeadRowPossible := False;
  1606. end else if Node.NodeName = 'tr' then
  1607. begin
  1608. DescrBeginTableRow;
  1609. ConvertCells(Node);
  1610. DescrEndTableRow;
  1611. end else
  1612. Warning(AContext, SErrInvalidTableContent)
  1613. else if IsContentNodeType(Node) then
  1614. Warning(AContext, SErrInvalidTableContent);
  1615. Node := Node.NextSibling;
  1616. end;
  1617. DescrEndTable;
  1618. end;
  1619. begin
  1620. if Node.NodeType <> ELEMENT_NODE then
  1621. begin
  1622. if Node.NodeType = TEXT_NODE then
  1623. Result := IsWhitespaceNode(TDOMText(Node))
  1624. else
  1625. Result := Node.NodeType = COMMENT_NODE;
  1626. exit;
  1627. end;
  1628. if Node.NodeName = 'remark' then
  1629. begin
  1630. DescrBeginRemark;
  1631. Node := Node.FirstChild;
  1632. if not ConvertExtShort(AContext, Node) then
  1633. while Assigned(Node) do
  1634. begin
  1635. if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'table') then
  1636. ConvertTable
  1637. else
  1638. if not ConvertSimpleBlock(AContext, Node) then
  1639. Warning(AContext, SErrInvalidRemarkContent, [Node.NodeName]);
  1640. Node := Node.NextSibling;
  1641. end;
  1642. DescrEndRemark;
  1643. Result := True;
  1644. end else if Node.NodeName = 'table' then
  1645. begin
  1646. ConvertTable;
  1647. Result := True;
  1648. end else
  1649. Result := ConvertSimpleBlock(AContext, Node);
  1650. end;
  1651. function TFPDocWriter.ConvertSimpleBlock(AContext: TPasElement;
  1652. Node: TDOMNode): Boolean;
  1653. procedure ConvertListItems;
  1654. var
  1655. Empty: Boolean;
  1656. begin
  1657. Node := Node.FirstChild;
  1658. Empty := True;
  1659. while Assigned(Node) do
  1660. begin
  1661. if ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  1662. then
  1663. Warning(AContext, SErrInvalidListContent)
  1664. else if Node.NodeType = ELEMENT_NODE then
  1665. if Node.NodeName = 'li' then
  1666. begin
  1667. DescrBeginListItem;
  1668. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  1669. DescrEndListItem;
  1670. Empty := False;
  1671. end else
  1672. Warning(AContext, SErrInvalidElementInList);
  1673. Node := Node.NextSibling;
  1674. end;
  1675. if Empty then
  1676. Warning(AContext, SErrListIsEmpty);
  1677. end;
  1678. procedure ConvertDefinitionList;
  1679. var
  1680. Empty, ExpectDTNext: Boolean;
  1681. begin
  1682. Node := Node.FirstChild;
  1683. Empty := True;
  1684. ExpectDTNext := True;
  1685. while Assigned(Node) do
  1686. begin
  1687. if ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or (Node.NodeType = ENTITY_REFERENCE_NODE)
  1688. then
  1689. Warning(AContext, SErrInvalidListContent)
  1690. else if Node.NodeType = ELEMENT_NODE then
  1691. if ExpectDTNext and (Node.NodeName = 'dt') then
  1692. begin
  1693. DescrBeginDefinitionTerm;
  1694. if not ConvertShort(AContext, TDOMElement(Node)) then
  1695. Warning(AContext, SErrInvalidDefinitionTermContent);
  1696. DescrEndDefinitionTerm;
  1697. Empty := False;
  1698. ExpectDTNext := False;
  1699. end else if not ExpectDTNext and (Node.NodeName = 'dd') then
  1700. begin
  1701. DescrBeginDefinitionEntry;
  1702. ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
  1703. DescrEndDefinitionEntry;
  1704. ExpectDTNext := True;
  1705. end else
  1706. Warning(AContext, SErrInvalidElementInList);
  1707. Node := Node.NextSibling;
  1708. end;
  1709. if Empty then
  1710. Warning(AContext, SErrListIsEmpty)
  1711. else if not ExpectDTNext then
  1712. Warning(AContext, SErrDefinitionEntryMissing);
  1713. end;
  1714. procedure ProcessCodeBody(Node: TDOMNode);
  1715. var
  1716. s: String;
  1717. i, j: Integer;
  1718. begin
  1719. Node := Node.FirstChild;
  1720. S:='';
  1721. while Assigned(Node) do
  1722. begin
  1723. if Node.NodeType = TEXT_NODE then
  1724. begin
  1725. s := s + UTF8Encode(Node.NodeValue);
  1726. j := 1;
  1727. for i := 1 to Length(s) do
  1728. // In XML, linefeeds are normalized to #10 by the parser!
  1729. if s[i] = #10 then
  1730. begin
  1731. DescrWriteCodeLine(Copy(s, j, i - j));
  1732. j := i + 1;
  1733. end;
  1734. if j > 1 then
  1735. s := Copy(s, j, Length(s));
  1736. end;
  1737. Node := Node.NextSibling;
  1738. end;
  1739. if Length(s) > 0 then
  1740. DescrWriteCodeLine(s);
  1741. end;
  1742. var
  1743. s: DOMString;
  1744. HasBorder: Boolean;
  1745. begin
  1746. if Node.NodeType <> ELEMENT_NODE then
  1747. begin
  1748. Result := (Node.NodeType = TEXT_NODE) and IsWhitespaceNode(TDOMText(Node));
  1749. exit;
  1750. end;
  1751. if Node.NodeName = 'p' then
  1752. begin
  1753. DescrBeginParagraph;
  1754. if not ConvertExtShort(AContext, Node.FirstChild) then
  1755. Warning(AContext, SErrInvalidParaContent);
  1756. DescrEndParagraph;
  1757. Result := True;
  1758. end else if Node.NodeName = 'code' then
  1759. begin
  1760. s := TDOMElement(Node)['border'];
  1761. if s = '1' then
  1762. HasBorder := True
  1763. else
  1764. begin
  1765. if (Length(s) > 0) and (s <> '0') then
  1766. Warning(AContext, SErrInvalidBorderValue, ['<code>']);
  1767. end;
  1768. DescrBeginCode(HasBorder, UTF8Encode(TDOMElement(Node)['highlighter']));
  1769. ProcessCodeBody(Node);
  1770. DescrEndCode;
  1771. Result := True;
  1772. end else if Node.NodeName = 'pre' then
  1773. begin
  1774. DescrBeginCode(False, 'none');
  1775. ProcessCodeBody(Node);
  1776. DescrEndCode;
  1777. Result := True;
  1778. end else if Node.NodeName = 'ul' then
  1779. begin
  1780. DescrBeginUnorderedList;
  1781. ConvertListItems;
  1782. DescrEndUnorderedList;
  1783. Result := True;
  1784. end else if Node.NodeName = 'ol' then
  1785. begin
  1786. DescrBeginOrderedList;
  1787. ConvertListItems;
  1788. DescrEndOrderedList;
  1789. Result := True;
  1790. end else if Node.NodeName = 'dl' then
  1791. begin
  1792. DescrBeginDefinitionList;
  1793. ConvertDefinitionList;
  1794. DescrEndDefinitionList;
  1795. Result := True;
  1796. end else if Node.NodeName = 'img' then
  1797. begin
  1798. begin
  1799. ConvertImage(Node as TDomElement);
  1800. Result:=True;
  1801. end;
  1802. end else
  1803. Result := False;
  1804. end;
  1805. procedure TFPDocWriter.ConvertImage ( El: TDomElement ) ;
  1806. Var
  1807. FN,Cap,LinkName : DOMString;
  1808. begin
  1809. FN:=El['file'];
  1810. Cap:=El['caption'];
  1811. LinkName:=El['name'];
  1812. FN:=UTF8decode(ChangeFileExt(UTF8Encode(FN),ImageExtension));
  1813. DescrWriteImageEl(FN,Cap,LinkName);
  1814. end;
  1815. procedure TFPDocWriter.DescrEmitNotesHeader(AContext: TPasElement);
  1816. begin
  1817. DescrWriteLinebreak;
  1818. DescrBeginBold;
  1819. DescrWriteText(UTF8Decode(SDocNotes));
  1820. DescrEndBold;
  1821. DescrWriteLinebreak;
  1822. end;
  1823. procedure TFPDocWriter.DescrEmitNotesFooter(AContext: TPasElement);
  1824. begin
  1825. DescrWriteLinebreak;
  1826. end;
  1827. Constructor TTopicElement.Create(const AName: String; AParent: TPasElement);
  1828. begin
  1829. Inherited Create(AName,AParent);
  1830. SubTopics:=TList.Create;
  1831. end;
  1832. Destructor TTopicElement.Destroy;
  1833. begin
  1834. // Actual subtopics are freed by TFPDocWriter Topics list.
  1835. SubTopics.Free;
  1836. Inherited;
  1837. end;
  1838. function TFPDocWriter.WriteDescr ( Element: TPasElement ) : TDocNode;
  1839. begin
  1840. Result:=Engine.FindDocNode(Element);
  1841. WriteDescr(ELement,Result);
  1842. end;
  1843. procedure TFPDocWriter.WriteDescr(Element: TPasElement; DocNode: TDocNode);
  1844. begin
  1845. if Assigned(DocNode) then
  1846. begin
  1847. if not IsDescrNodeEmpty(DocNode.Descr) then
  1848. WriteDescr(Element, DocNode.Descr)
  1849. else if not IsDescrNodeEmpty(DocNode.ShortDescr) then
  1850. WriteDescr(Element, DocNode.ShortDescr);
  1851. end;
  1852. end;
  1853. procedure TFPDocWriter.WriteDescr(AContext: TPasElement; DescrNode: TDOMElement);
  1854. begin
  1855. if Assigned(DescrNode) then
  1856. ConvertDescr(AContext, DescrNode, False);
  1857. end;
  1858. procedure TFPDocWriter.FPDocError(Msg: String);
  1859. begin
  1860. Raise EFPDocWriterError.Create(Msg);
  1861. end;
  1862. procedure TFPDocWriter.FPDocError(Fmt: String; Args: array of const);
  1863. begin
  1864. FPDocError(Format(Fmt,Args));
  1865. end;
  1866. function TFPDocWriter.ShowMember(M: TPasElement): boolean;
  1867. begin
  1868. Result:=not ((M.Visibility=visPrivate) and Engine.HidePrivate);
  1869. If Result then
  1870. Result:=Not ((M.Visibility=visProtected) and Engine.HideProtected)
  1871. end;
  1872. procedure TFPDocWriter.GetMethodList ( ClassDecl: TPasClassType;
  1873. List: TStringList ) ;
  1874. Var
  1875. I : Integer;
  1876. M : TPasElement;
  1877. begin
  1878. List.Clear;
  1879. List.Sorted:=False;
  1880. for i := 0 to ClassDecl.Members.Count - 1 do
  1881. begin
  1882. M:=TPasElement(ClassDecl.Members[i]);
  1883. if M.InheritsFrom(TPasProcedureBase) and ShowMember(M) then
  1884. List.AddObject(M.Name,M);
  1885. end;
  1886. List.Sorted:=False;
  1887. end;
  1888. initialization
  1889. InitWriterList;
  1890. finalization
  1891. DoneWriterList;
  1892. end.