dw_basemd.pp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. {
  2. FPDoc - Free Pascal Documentation Tool
  3. Copyright (C) 2021 by Michael Van Canneyt
  4. * Basic Markdown output generator. No assumptions about document/documentation structure
  5. See the file COPYING, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. }
  11. unit dw_basemd;
  12. {$mode objfpc}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils, dwriter, DOM, pastree, dglobals;
  16. Const
  17. MaxIndents = 32;
  18. MaxLists = 32;
  19. Type
  20. THeaderLevel = 1..6;
  21. TRender = (rStrong,rEmphasis,rCode);
  22. TListType = (ltOrdered,ltUnordered, ltDefinition);
  23. TMarkdownEngine = (meMkDocs,meNone);
  24. { TBaseMarkdownWriter }
  25. TBaseMarkdownWriter = class(TMultifileDocWriter)
  26. private
  27. FIgnoreCount : Integer;
  28. FBaseImageURL: String;
  29. FContentPrefix: String;
  30. FCurrentIndentIndex: Word;
  31. FCurrentLine: UTF8String;
  32. FDefinitionSeparator: String;
  33. FDefinitionTermRender: TRender;
  34. FFileRendering: TRender;
  35. FIndentSize: Byte;
  36. FKeywordRendering: TRender;
  37. FPrefix : string;
  38. FMetadata,
  39. FMarkDown: TStrings;
  40. FSymbolRendering: TRender;
  41. FTheme: String;
  42. FUnderLineRendering: TRender;
  43. FVarRendering: TRender;
  44. FLink : String;
  45. FListStack : Integer;
  46. FIndents : Array[0..MaxIndents] of Word;
  47. FListTypes : Array[1..MaxLists] of TListType;
  48. FTableColCount : Integer;
  49. FMarkDownEngine: TMarkdownEngine;
  50. function GetCurrentIndent: Word;
  51. procedure SetIndentSize(AValue: Byte);
  52. procedure clearIndent;
  53. procedure CalcPrefix;
  54. Protected
  55. function CreateAllocator: TFileAllocator; override;
  56. Procedure DescrEmitNotesHeader(AContext : TPasElement); override;
  57. Procedure DescrEmitNotesFooter(AContext : TPasElement); override;
  58. procedure DescrWriteText(const AText: DOMString); override;
  59. procedure DescrBeginBold; override;
  60. procedure DescrEndBold; override;
  61. procedure DescrBeginItalic; override;
  62. procedure DescrEndItalic; override;
  63. procedure DescrBeginEmph; override;
  64. procedure DescrEndEmph; override;
  65. procedure DescrBeginUnderline; override;
  66. procedure DescrEndUnderline; override;
  67. procedure DescrWriteImageEl(const AFileName, ACaption, ALinkName : DOMString); override;
  68. procedure DescrWriteFileEl(const AText: DOMString); override;
  69. procedure DescrWriteKeywordEl(const AText: DOMString); override;
  70. procedure DescrWriteVarEl(const AText: DOMString); override;
  71. procedure DescrBeginLink(const AId: DOMString); override;
  72. procedure DescrEndLink; override;
  73. procedure DescrBeginURL(const AURL: DOMString); override;
  74. procedure DescrEndURL; override;
  75. procedure DescrWriteLinebreak; override;
  76. procedure DescrBeginParagraph; override;
  77. procedure DescrEndParagraph; override;
  78. procedure DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String); override;
  79. procedure DescrWriteCodeLine(const ALine: String); override;
  80. procedure DescrEndCode; override;
  81. procedure DescrBeginOrderedList; override;
  82. procedure DescrEndOrderedList; override;
  83. procedure DescrBeginUnorderedList; override;
  84. procedure DescrEndUnorderedList; override;
  85. procedure DescrBeginDefinitionList; override;
  86. procedure DescrEndDefinitionList; override;
  87. procedure DescrBeginListItem; override;
  88. procedure DescrEndListItem; override;
  89. procedure DescrBeginDefinitionTerm; override;
  90. procedure DescrEndDefinitionTerm; override;
  91. procedure DescrBeginDefinitionEntry; override;
  92. procedure DescrEndDefinitionEntry; override;
  93. procedure DescrBeginSectionTitle; override;
  94. procedure DescrBeginSectionBody; override;
  95. procedure DescrEndSection; override;
  96. procedure DescrBeginRemark; override;
  97. procedure DescrEndRemark; override;
  98. procedure DescrBeginTable(ColCount: Integer; HasBorder: Boolean); override;
  99. procedure DescrEndTable; override;
  100. procedure DescrBeginTableCaption; override;
  101. procedure DescrEndTableCaption; override;
  102. procedure DescrBeginTableHeadRow; override;
  103. procedure DescrEndTableHeadRow; override;
  104. procedure DescrBeginTableRow; override;
  105. procedure DescrEndTableRow; override;
  106. procedure DescrBeginTableCell; override;
  107. procedure DescrEndTableCell; override;
  108. Protected
  109. // Emit current line, if any
  110. Function OutputCurrentLine : Boolean;
  111. function EscapeMarkDown(aText: Domstring): string;
  112. function EscapeMarkDown(aText: String): string;
  113. function CreateLink(Const aText,aLink : String) : String;
  114. function GetListPrefix: String;virtual;
  115. // Append to current line
  116. procedure AppendToLine(aText: DomString; DoEscape: boolean = true);
  117. procedure AppendToLine(aText: UTF8String; DoEscape: boolean = true); virtual;
  118. procedure AppendToLine(aFmt: UTF8String; aArgs : Array of const; DoEscape: boolean = true);
  119. // Write current line and append new line
  120. procedure EmitLine(aText: UTF8String; DoEscape: boolean = true); virtual;
  121. procedure EmitLine(aFmt: UTF8String; aArgs : Array of const; DoEscape: boolean = true);
  122. // Append spans to current line
  123. procedure AppendLink(Const aText,aLink : String);
  124. Procedure AppendRendered(aText : String; aRender : TRender);
  125. Procedure AppendKeyWord(aText : String); inline;
  126. Procedure AppendSymbol(aText : String); inline;
  127. Procedure AppendTableHeader(aHeaders: Array of String);
  128. Procedure EmitCode(aCodeBlock : String; aIndent : Integer = 0);
  129. Procedure EmitCode(aCodeBlock : TStrings; aIndent : Integer = 0);
  130. Procedure EmitCodeLine(aCodeLine : string);
  131. procedure EndSpan(aRender: TRender);
  132. procedure StartSpan(aRender: TRender);
  133. Procedure PushIndent(aNewIndent : Byte);
  134. Procedure PopIndent;
  135. Procedure StartList(aType : TListType);
  136. Procedure StopList(aType : TListType);
  137. Procedure BeginIgnore;
  138. Procedure EndIgnore;
  139. Procedure DoLineBreak;
  140. Function InList : Boolean;
  141. Function CurrentList : TListType;
  142. Property ContentPrefix : String Read FContentPrefix Write FContentPrefix;
  143. Public
  144. Constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); override;
  145. destructor Destroy; override;
  146. Procedure AppendHeader(aLevel : THeaderLevel; const AHeader : String; DoEscape : Boolean = true);
  147. Procedure Indent;
  148. Procedure Undent;
  149. Procedure ClearMarkDown;
  150. Procedure EnsureEmptyLine;
  151. procedure SaveToFile(aFileName : string);
  152. procedure AddMetaData(Const aName,aValue : string);
  153. Property CurrentLine : UTF8String Read FCurrentLine Write FCurrentLine;
  154. Property MarkDown : TStrings Read FMarkDown;
  155. Property CurrentIndent : Word Read GetCurrentIndent;
  156. Property Prefix : String Read FPrefix;
  157. Property IndentSize : Byte Read FIndentSize Write SetIndentSize;
  158. Property UnderLineRendering : TRender Read FUnderLineRendering Write FUnderLineRendering;
  159. Property FileRendering : TRender Read FFileRendering Write FFileRendering;
  160. Property VarRendering : TRender Read FVarRendering Write FVarRendering;
  161. Property KeywordRendering : TRender Read FKeywordRendering Write FKeyWordRendering;
  162. Property SymbolRendering : TRender Read FSymbolRendering Write FSymbolRendering;
  163. Property DefinitionSeparator : String Read FDefinitionSeparator Write FDefinitionSeparator;
  164. Property DefinitionTermRender : TRender Read FDefinitionTermRender Write FDefinitionTermRender;
  165. Property BaseImageURL : String Read FBaseImageURL Write FBaseIMageURL;
  166. Property MetaData : TStrings Read FMetaData;
  167. Property MarkDownEngine : TMarkdownEngine Read FMarkDownEngine Write FMarkDownEngine;
  168. Property Theme : String Read FTheme Write FTheme;
  169. end;
  170. implementation
  171. uses fpdocstrs;
  172. procedure TBaseMarkdownWriter.SetIndentSize(AValue: Byte);
  173. begin
  174. if FIndentSize=AValue then Exit;
  175. if CurrentIndent>0 then
  176. FPDocError(SErrCannotChangeIndentSizeWhenIndented);
  177. FIndentSize:=AValue;
  178. end;
  179. function TBaseMarkdownWriter.GetCurrentIndent: Word;
  180. begin
  181. Result:=FIndents[FCurrentIndentIndex];
  182. end;
  183. procedure TBaseMarkdownWriter.clearIndent;
  184. begin
  185. FIndents[FCurrentIndentIndex]:=0;
  186. CalcPrefix;
  187. end;
  188. procedure TBaseMarkdownWriter.CalcPrefix;
  189. begin
  190. FPrefix:=StringOfChar(' ',CurrentIndent);
  191. end;
  192. function TBaseMarkdownWriter.CreateAllocator: TFileAllocator;
  193. begin
  194. Result:=TLongNameFileAllocator.Create('.md');
  195. end;
  196. procedure TBaseMarkdownWriter.DescrEmitNotesHeader(AContext: TPasElement);
  197. begin
  198. AppendHeader(2, SDocNotes);
  199. end;
  200. procedure TBaseMarkdownWriter.DescrEmitNotesFooter(AContext: TPasElement);
  201. begin
  202. EnsureEmptyLine;
  203. end;
  204. function TBaseMarkdownWriter.EscapeMarkDown(aText: Domstring): string;
  205. begin
  206. Result:=EscapeMarkDown(UTF8Encode(aText))
  207. end;
  208. function TBaseMarkdownWriter.EscapeMarkDown(aText: String): string;
  209. begin
  210. Result:=StringReplace(aText,'*','\*',[rfReplaceAll]);
  211. Result:=StringReplace(Result,'_','\_',[rfReplaceAll]);
  212. Result:=StringReplace(Result,'`','\`',[rfReplaceAll]);
  213. end;
  214. function TBaseMarkdownWriter.CreateLink(const aText, aLink: String): String;
  215. begin
  216. Result:=Format('[%s](%s)',[EscapeMarkDown(aText),aLink])
  217. end;
  218. procedure TBaseMarkdownWriter.AppendToLine(aText: DomString; DoEscape: boolean);
  219. begin
  220. If FIgnoreCount>0 then
  221. exit;
  222. AppendToLine(UTF8Encode(aText),DoEscape);
  223. end;
  224. procedure TBaseMarkdownWriter.AppendToLine(aText: UTF8String; DoEscape: boolean
  225. );
  226. begin
  227. if DoEscape then
  228. aText:=EscapeMarkDown(aText);
  229. if (FCurrentLine='') and (FContentPrefix<>'') then
  230. FCurrentLine:=FContentPrefix;
  231. FCurrentLine:=FCurrentLine+aText;
  232. end;
  233. procedure TBaseMarkdownWriter.AppendToLine(aFmt: UTF8String;
  234. aArgs: array of const; DoEscape: boolean);
  235. begin
  236. AppendToLine(Format(aFmt,aArgs),DoEscape);
  237. end;
  238. procedure TBaseMarkdownWriter.EmitLine(aText: UTF8String; DoEscape: boolean);
  239. begin
  240. OutputCurrentLine;
  241. AppendToLine(aText,DoEscape);
  242. OutputCurrentLine;
  243. end;
  244. procedure TBaseMarkdownWriter.EmitLine(aFmt: UTF8String; aArgs: array of const;
  245. DoEscape: boolean);
  246. begin
  247. EmitLine(Format(aFmt,aArgs),DoEscape);
  248. end;
  249. procedure TBaseMarkdownWriter.AppendLink(const aText, aLink: String);
  250. begin
  251. AppendToLine(CreateLink(aText,aLink),False);
  252. end;
  253. procedure TBaseMarkdownWriter.AppendRendered(aText: String; aRender: TRender);
  254. begin
  255. StartSpan(aRender);
  256. AppendToLine(aText);
  257. EndSpan(aRender);
  258. end;
  259. procedure TBaseMarkdownWriter.AppendKeyWord(aText: String);
  260. begin
  261. AppendRendered(aText,KeywordRendering);
  262. end;
  263. procedure TBaseMarkdownWriter.AppendSymbol(aText: String);
  264. begin
  265. AppendRendered(aText,SymbolRendering);
  266. end;
  267. procedure TBaseMarkdownWriter.AppendTableHeader(aHeaders: array of String);
  268. Var
  269. S : String;
  270. begin
  271. DescrBeginTable(Length(aHeaders),False);
  272. DescrBeginTableHeadRow;
  273. for S in aHeaders do
  274. begin
  275. DescrBeginTableCell;
  276. AppendToLine(S);
  277. DescrEndTableCell;
  278. end;
  279. DescrEndTableHeadRow;
  280. end;
  281. procedure TBaseMarkdownWriter.EmitCode(aCodeBlock: String; aIndent : Integer = 0);
  282. Var
  283. L : TStringList;
  284. begin
  285. L:=TStringList.Create;
  286. try
  287. L.Text:=aCodeBlock;
  288. EmitCode(L,aIndent);
  289. finally
  290. L.Free;
  291. end;
  292. end;
  293. procedure TBaseMarkdownWriter.EmitCode(aCodeBlock: TStrings; aIndent : Integer = 0);
  294. var
  295. S,aPrefix : string;
  296. begin
  297. aPrefix:=StringOfChar(' ',aIndent);
  298. For S in aCodeBlock do
  299. EmitCodeLine(aPrefix+S);
  300. end;
  301. procedure TBaseMarkdownWriter.EmitCodeLine(aCodeLine: string);
  302. begin
  303. EmitLine(aCodeLine,False);
  304. end;
  305. procedure TBaseMarkdownWriter.DescrWriteText(const AText: DOMString);
  306. begin
  307. AppendToLine(aText);
  308. end;
  309. procedure TBaseMarkdownWriter.DescrBeginBold;
  310. begin
  311. AppendToLine('**',False);
  312. end;
  313. procedure TBaseMarkdownWriter.DescrEndBold;
  314. begin
  315. AppendToLine('**',False);
  316. end;
  317. procedure TBaseMarkdownWriter.DescrBeginItalic;
  318. begin
  319. AppendToLine('*',False);
  320. end;
  321. procedure TBaseMarkdownWriter.DescrEndItalic;
  322. begin
  323. AppendToLine('*',False);
  324. end;
  325. procedure TBaseMarkdownWriter.DescrBeginEmph;
  326. begin
  327. AppendToLine('*',False);
  328. end;
  329. procedure TBaseMarkdownWriter.DescrEndEmph;
  330. begin
  331. AppendToLine('*',False);
  332. end;
  333. procedure TBaseMarkdownWriter.StartSpan(aRender : TRender);
  334. begin
  335. case aRender of
  336. rStrong : AppendToLine('**',False);
  337. rEmphasis : AppendToLine('*',False);
  338. rCode : AppendToLine('`',False);
  339. end;
  340. end;
  341. procedure TBaseMarkdownWriter.PushIndent(aNewIndent: Byte);
  342. begin
  343. if FCurrentIndentIndex>=MaxIndents then
  344. FPDocError(SErrMaxIndentStack);
  345. Inc(FCurrentIndentIndex);
  346. Findents[FCurrentIndentIndex]:=aNewIndent;
  347. CalcPrefix;
  348. end;
  349. procedure TBaseMarkdownWriter.PopIndent;
  350. begin
  351. if FCurrentIndentIndex<=0 then
  352. FPDocError(SErrMinIndentStack);
  353. Dec(FCurrentIndentIndex);
  354. CalcPrefix;
  355. end;
  356. procedure TBaseMarkdownWriter.StartList(aType: TListType);
  357. begin
  358. If FListStack>=MaxLists then
  359. FPDocError(SErrMaxListStack);
  360. OutputCurrentLine;
  361. Inc(FListStack);
  362. FListTypes[FListStack]:=aType;
  363. if FListStack>1 then
  364. PushIndent(CurrentIndent+Length(GetListprefix));
  365. end;
  366. procedure TBaseMarkdownWriter.StopList(aType: TListType);
  367. begin
  368. OutputCurrentLine;
  369. If FListStack<=0 then
  370. FPDocError(SErrMinListStack);
  371. if FListTypes[FListStack]<>aType then
  372. FPDocError(SErrPopListStack);
  373. if FListStack>1 then
  374. PopIndent;
  375. Dec(FListStack);
  376. end;
  377. procedure TBaseMarkdownWriter.BeginIgnore;
  378. begin
  379. Inc(FIgnoreCount);
  380. end;
  381. procedure TBaseMarkdownWriter.EndIgnore;
  382. begin
  383. If FignoreCount>0 then
  384. Dec(FIgnoreCount);
  385. end;
  386. procedure TBaseMarkdownWriter.DoLineBreak;
  387. begin
  388. if FCurrentLine<>'' then
  389. begin
  390. FCurrentLine:=FCurrentLine+' ';
  391. OutputCurrentLine;
  392. end;
  393. end;
  394. function TBaseMarkdownWriter.InList: Boolean;
  395. begin
  396. Result:=FlistStack>0;
  397. end;
  398. function TBaseMarkdownWriter.CurrentList: TListType;
  399. begin
  400. if FListStack=0 then
  401. FPDOcError(SErrNotInList);
  402. Result:=FListTypes[FListStack];
  403. end;
  404. procedure TBaseMarkdownWriter.EndSpan(aRender : TRender);
  405. begin
  406. case aRender of
  407. rStrong : AppendToLine('**',False);
  408. rEmphasis : AppendToLine('*',False);
  409. rCode : AppendToLine('`',False);
  410. end;
  411. end;
  412. procedure TBaseMarkdownWriter.DescrBeginUnderline;
  413. begin
  414. StartSpan(UnderlineRendering);
  415. end;
  416. procedure TBaseMarkdownWriter.DescrEndUnderline;
  417. begin
  418. EndSpan(UnderlineRendering);
  419. end;
  420. procedure TBaseMarkdownWriter.DescrWriteImageEl(const AFileName, ACaption, ALinkName : DOMString);
  421. Var
  422. aLink,D,FN : String;
  423. L : integer;
  424. begin
  425. // Determine URL for image.
  426. If (Module=Nil) then
  427. D:=Allocator.GetRelativePathToTop(Package)
  428. else
  429. D:=Allocator.GetRelativePathToTop(Module);
  430. L:=Length(D);
  431. If (L>0) and (D[L]<>'/') then
  432. D:=D+'/';
  433. FN:=D + BaseImageURL+ Utf8Encode(AFileName);
  434. EnsureEmptyLine;
  435. aLink:='!['+UTF8Encode(aCaption)+']('+FN+')';
  436. AppendToLine(aLink,False);
  437. end;
  438. procedure TBaseMarkdownWriter.DescrWriteFileEl(const AText: DOMString);
  439. begin
  440. AppendRendered(UTF8Encode(aText),FileRendering);
  441. end;
  442. procedure TBaseMarkdownWriter.DescrWriteKeywordEl(const AText: DOMString);
  443. begin
  444. AppendKeyWord(UTF8ENcode(aText));
  445. end;
  446. procedure TBaseMarkdownWriter.DescrWriteVarEl(const AText: DOMString);
  447. begin
  448. AppendRendered(UTF8Encode(aText),VarRendering);
  449. end;
  450. procedure TBaseMarkdownWriter.DescrBeginLink(const AId: DOMString);
  451. var
  452. a,s,n : String;
  453. begin
  454. a:=UTF8Encode(AId);
  455. s := UTF8Encode(ResolveLinkID(a));
  456. if Length(s) > 0 then
  457. begin
  458. FLink:=S;
  459. AppendToLine('[');
  460. end
  461. else
  462. begin
  463. FLink:='';
  464. if assigned(module) then
  465. s:=module.name
  466. else
  467. s:='?';
  468. if a='' then a:='<empty>';
  469. if Assigned(CurrentContext) then
  470. N:=CurrentContext.Name
  471. else
  472. N:='?';
  473. DoLog(SErrUnknownLinkID, [s,n,a]);
  474. end
  475. end;
  476. procedure TBaseMarkdownWriter.DescrEndLink;
  477. begin
  478. AppendToLine(']('+FLink+') ',false);
  479. FLink:='';
  480. end;
  481. procedure TBaseMarkdownWriter.DescrBeginURL(const AURL: DOMString);
  482. begin
  483. FLink:=UTF8Encode(aURL);
  484. AppendToLine('[');
  485. end;
  486. procedure TBaseMarkdownWriter.DescrEndURL;
  487. begin
  488. AppendToLine(']('+FLink+') ',false);
  489. FLink:='';
  490. end;
  491. procedure TBaseMarkdownWriter.DescrWriteLinebreak;
  492. begin
  493. AppendToLine(' ');
  494. OutputCurrentLine;
  495. end;
  496. procedure TBaseMarkdownWriter.DescrBeginParagraph;
  497. begin
  498. EnsureEmptyLine;
  499. end;
  500. procedure TBaseMarkdownWriter.DescrEndParagraph;
  501. begin
  502. EnsureEmptyLine;
  503. end;
  504. procedure TBaseMarkdownWriter.DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String);
  505. Var
  506. hl : string;
  507. begin
  508. hl:=AHighlighterName;
  509. if SameText(hl,'Pascal') or (hl='') then
  510. hl:='delphi';
  511. OutputCurrentLine;
  512. AppendToLine('```'+hl,False);
  513. PushIndent(0);
  514. end;
  515. procedure TBaseMarkdownWriter.DescrWriteCodeLine(const ALine: String);
  516. begin
  517. EmitCodeLine(aLine);
  518. end;
  519. procedure TBaseMarkdownWriter.DescrEndCode;
  520. begin
  521. OutputCurrentLine;
  522. AppendToLine('```',False);
  523. OutputCurrentLine;
  524. PopIndent;
  525. end;
  526. procedure TBaseMarkdownWriter.DescrBeginOrderedList;
  527. begin
  528. StartList(ltOrdered);
  529. end;
  530. procedure TBaseMarkdownWriter.DescrEndOrderedList;
  531. begin
  532. StopList(ltOrdered);
  533. end;
  534. procedure TBaseMarkdownWriter.DescrBeginUnorderedList;
  535. begin
  536. StartList(ltUnordered);
  537. end;
  538. procedure TBaseMarkdownWriter.DescrEndUnorderedList;
  539. begin
  540. StopList(ltUnordered);
  541. end;
  542. procedure TBaseMarkdownWriter.DescrBeginDefinitionList;
  543. begin
  544. StartList(ltDefinition);
  545. end;
  546. procedure TBaseMarkdownWriter.DescrEndDefinitionList;
  547. begin
  548. StopList(ltDefinition);
  549. end;
  550. function TBaseMarkdownWriter.GetListPrefix : String;
  551. begin
  552. Case CurrentList of
  553. ltOrdered : Result:='1. ';
  554. ltUnordered : Result:='- ';
  555. ltDefinition : Result:=': ';
  556. end;
  557. end;
  558. procedure TBaseMarkdownWriter.DescrBeginListItem;
  559. Var
  560. Pref : String;
  561. begin
  562. Pref:=GetListPrefix;
  563. AppendToLine(Pref);
  564. end;
  565. procedure TBaseMarkdownWriter.DescrEndListItem;
  566. begin
  567. OutputCurrentLine;
  568. end;
  569. procedure TBaseMarkdownWriter.DescrBeginDefinitionTerm;
  570. begin
  571. if MarkDownEngine=meMkDocs then
  572. EnsureEmptyLine
  573. else
  574. begin
  575. AppendToLine('- ');
  576. StartSpan(DefinitionTermRender);
  577. end;
  578. end;
  579. procedure TBaseMarkdownWriter.DescrEndDefinitionTerm;
  580. begin
  581. if MarkDownEngine=meMkDocs then
  582. OutputCurrentLine
  583. else
  584. EndSpan(DefinitionTermRender);
  585. end;
  586. procedure TBaseMarkdownWriter.DescrBeginDefinitionEntry;
  587. begin
  588. if MarkDownEngine=meMkDocs then
  589. begin
  590. AppendToLine(': ');
  591. Indent;
  592. end
  593. else
  594. AppendToLine(DefinitionSeparator);
  595. end;
  596. procedure TBaseMarkdownWriter.DescrEndDefinitionEntry;
  597. begin
  598. OutputCurrentLine;
  599. if MarkDownEngine=meMkDocs then
  600. begin
  601. Undent;
  602. EnsureEmptyLine;
  603. end;
  604. end;
  605. procedure TBaseMarkdownWriter.DescrBeginSectionTitle;
  606. begin
  607. EnsureEmptyLine;
  608. end;
  609. procedure TBaseMarkdownWriter.DescrBeginSectionBody;
  610. begin
  611. EnsureEmptyLine;
  612. end;
  613. procedure TBaseMarkdownWriter.DescrEndSection;
  614. begin
  615. EnsureEmptyLine;
  616. end;
  617. procedure TBaseMarkdownWriter.DescrBeginRemark;
  618. begin
  619. if MarkDownEngine=meMkDocs then
  620. begin
  621. EnsureEmptyLine;
  622. AppendToLine('!!! Remark',False);
  623. OutputCurrentLine;
  624. end
  625. else
  626. FContentPrefix:='> ';
  627. end;
  628. procedure TBaseMarkdownWriter.DescrEndRemark;
  629. begin
  630. if MarkDownEngine=meMkDocs then
  631. begin
  632. OutputCurrentLine;
  633. AppendToLine('!!!',False);
  634. end;
  635. EnsureEmptyLine;
  636. FContentPrefix:='';
  637. end;
  638. procedure TBaseMarkdownWriter.DescrBeginTable(ColCount: Integer; HasBorder: Boolean);
  639. begin
  640. EnsureEmptyLine;
  641. FTableColCount:=ColCount;
  642. end;
  643. procedure TBaseMarkdownWriter.DescrEndTable;
  644. begin
  645. EnsureEmptyLine;
  646. end;
  647. procedure TBaseMarkdownWriter.DescrBeginTableCaption;
  648. begin
  649. BeginIgnore;
  650. end;
  651. procedure TBaseMarkdownWriter.DescrEndTableCaption;
  652. begin
  653. EndIgnore;
  654. end;
  655. procedure TBaseMarkdownWriter.DescrBeginTableHeadRow;
  656. begin
  657. end;
  658. procedure TBaseMarkdownWriter.DescrEndTableHeadRow;
  659. Var
  660. I : Integer;
  661. begin
  662. AppendToLine(' |',False);
  663. OutputCurrentLine;
  664. AppendToLine('|',False);
  665. For I:=1 to FTableColCount do
  666. AppendToLine('---|',False);
  667. OutputCurrentLine;
  668. end;
  669. procedure TBaseMarkdownWriter.DescrBeginTableRow;
  670. begin
  671. end;
  672. procedure TBaseMarkdownWriter.DescrEndTableRow;
  673. begin
  674. AppendToLine(' |',False);
  675. OutputCurrentLine;
  676. end;
  677. procedure TBaseMarkdownWriter.DescrBeginTableCell;
  678. begin
  679. AppendToLine('| ',False);
  680. end;
  681. procedure TBaseMarkdownWriter.DescrEndTableCell;
  682. begin
  683. AppendToLine(' ',False);
  684. end;
  685. function TBaseMarkdownWriter.OutputCurrentLine: Boolean;
  686. begin
  687. If FIgnoreCount>0 then
  688. exit(False);
  689. Result:=FCurrentLine<>'';
  690. if Result then
  691. begin
  692. FMarkDown.Add(Prefix+FCurrentLine);
  693. FCurrentLine:='';
  694. end;
  695. end;
  696. constructor TBaseMarkdownWriter.Create(APackage: TPasPackage;
  697. AEngine: TFPDocEngine);
  698. begin
  699. inherited Create(APackage, AEngine);
  700. FMarkDown:=TStringList.Create;
  701. FMetaData:=TStringList.Create;
  702. FMetaData.NameValueSeparator:=':';
  703. FIndents[FCurrentIndentIndex]:=0;
  704. CalcPrefix;
  705. Theme:='readthedocs';
  706. end;
  707. destructor TBaseMarkdownWriter.Destroy;
  708. begin
  709. FreeAndNil(FMarkDown);
  710. FreeAndNil(FMetadata);
  711. inherited Destroy;
  712. end;
  713. procedure TBaseMarkdownWriter.AppendHeader(aLevel: THeaderLevel;
  714. const AHeader: String; DoEscape : Boolean = true);
  715. begin
  716. EnsureEmptyLine;
  717. AppendToLine(StringOfChar('#',aLevel)+' ',False);
  718. AppendToLine(aHeader,DoEscape);
  719. end;
  720. procedure TBaseMarkdownWriter.Indent;
  721. begin
  722. Inc(FIndents[FCurrentIndentIndex],IndentSize);
  723. CalcPrefix;
  724. end;
  725. procedure TBaseMarkdownWriter.Undent;
  726. begin
  727. if IndentSize>CurrentIndent then
  728. FPDocError(SErrIndentMismatch);
  729. Dec(FIndents[FCurrentIndentIndex],IndentSize);
  730. CalcPrefix;
  731. end;
  732. procedure TBaseMarkdownWriter.ClearMarkDown;
  733. begin
  734. FMarkDown.Clear;
  735. FMetaData.Clear;
  736. FCurrentLine:='';
  737. FContentPrefix:='';
  738. FCurrentIndentIndex:=0;;
  739. FIndents[FCurrentIndentIndex]:=0;
  740. CalcPrefix;
  741. end;
  742. procedure TBaseMarkdownWriter.EnsureEmptyLine;
  743. begin
  744. if OutputCurrentLine then
  745. FMarkDown.Add('')
  746. else if (FmarkDown.Count>0) and (FMarkDown[FmarkDown.Count-1]<>'') then
  747. FMarkDown.Add('');
  748. end;
  749. procedure TBaseMarkdownWriter.SaveToFile(aFileName: string);
  750. Var
  751. Doc: TStrings;
  752. I : Integer;
  753. N,V : String;
  754. begin
  755. Doc:=TStringList.Create;
  756. try
  757. if FMetadata.Count>0 then
  758. begin
  759. Doc.Add('---');
  760. For I:=0 to FMetadata.Count-1 do
  761. begin
  762. FMetaData.GetNameValue(I,N,V);
  763. Doc.Add(Lowercase(N)+': "'+V+'"');
  764. end;
  765. Doc.Add('---');
  766. end;
  767. Doc.AddStrings(FMarkDown);
  768. Doc.SaveToFile(aFileName);
  769. finally
  770. Doc.Free;
  771. end;
  772. end;
  773. procedure TBaseMarkdownWriter.AddMetaData(const aName, aValue: string);
  774. begin
  775. FMetadata.Values[aName]:=aValue;
  776. end;
  777. end.