dwriter.pp 57 KB

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