dw_basemd.pp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918
  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. LinkUnresolvedInc();
  475. end
  476. end;
  477. procedure TBaseMarkdownWriter.DescrEndLink;
  478. begin
  479. AppendToLine(']('+FLink+') ',false);
  480. FLink:='';
  481. end;
  482. procedure TBaseMarkdownWriter.DescrBeginURL(const AURL: DOMString);
  483. begin
  484. FLink:=UTF8Encode(aURL);
  485. AppendToLine('[');
  486. end;
  487. procedure TBaseMarkdownWriter.DescrEndURL;
  488. begin
  489. AppendToLine(']('+FLink+') ',false);
  490. FLink:='';
  491. end;
  492. procedure TBaseMarkdownWriter.DescrWriteLinebreak;
  493. begin
  494. AppendToLine(' ');
  495. OutputCurrentLine;
  496. end;
  497. procedure TBaseMarkdownWriter.DescrBeginParagraph;
  498. begin
  499. EnsureEmptyLine;
  500. end;
  501. procedure TBaseMarkdownWriter.DescrEndParagraph;
  502. begin
  503. EnsureEmptyLine;
  504. end;
  505. procedure TBaseMarkdownWriter.DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String);
  506. Var
  507. hl : string;
  508. begin
  509. hl:=AHighlighterName;
  510. if SameText(hl,'Pascal') or (hl='') then
  511. hl:='delphi';
  512. OutputCurrentLine;
  513. AppendToLine('```'+hl,False);
  514. PushIndent(0);
  515. end;
  516. procedure TBaseMarkdownWriter.DescrWriteCodeLine(const ALine: String);
  517. begin
  518. EmitCodeLine(aLine);
  519. end;
  520. procedure TBaseMarkdownWriter.DescrEndCode;
  521. begin
  522. OutputCurrentLine;
  523. AppendToLine('```',False);
  524. OutputCurrentLine;
  525. PopIndent;
  526. end;
  527. procedure TBaseMarkdownWriter.DescrBeginOrderedList;
  528. begin
  529. StartList(ltOrdered);
  530. end;
  531. procedure TBaseMarkdownWriter.DescrEndOrderedList;
  532. begin
  533. StopList(ltOrdered);
  534. end;
  535. procedure TBaseMarkdownWriter.DescrBeginUnorderedList;
  536. begin
  537. StartList(ltUnordered);
  538. end;
  539. procedure TBaseMarkdownWriter.DescrEndUnorderedList;
  540. begin
  541. StopList(ltUnordered);
  542. end;
  543. procedure TBaseMarkdownWriter.DescrBeginDefinitionList;
  544. begin
  545. StartList(ltDefinition);
  546. end;
  547. procedure TBaseMarkdownWriter.DescrEndDefinitionList;
  548. begin
  549. StopList(ltDefinition);
  550. end;
  551. function TBaseMarkdownWriter.GetListPrefix : String;
  552. begin
  553. Case CurrentList of
  554. ltOrdered : Result:='1. ';
  555. ltUnordered : Result:='- ';
  556. ltDefinition : Result:=': ';
  557. end;
  558. end;
  559. procedure TBaseMarkdownWriter.DescrBeginListItem;
  560. Var
  561. Pref : String;
  562. begin
  563. Pref:=GetListPrefix;
  564. AppendToLine(Pref);
  565. end;
  566. procedure TBaseMarkdownWriter.DescrEndListItem;
  567. begin
  568. OutputCurrentLine;
  569. end;
  570. procedure TBaseMarkdownWriter.DescrBeginDefinitionTerm;
  571. begin
  572. if MarkDownEngine=meMkDocs then
  573. EnsureEmptyLine
  574. else
  575. begin
  576. AppendToLine('- ');
  577. StartSpan(DefinitionTermRender);
  578. end;
  579. end;
  580. procedure TBaseMarkdownWriter.DescrEndDefinitionTerm;
  581. begin
  582. if MarkDownEngine=meMkDocs then
  583. OutputCurrentLine
  584. else
  585. EndSpan(DefinitionTermRender);
  586. end;
  587. procedure TBaseMarkdownWriter.DescrBeginDefinitionEntry;
  588. begin
  589. if MarkDownEngine=meMkDocs then
  590. begin
  591. AppendToLine(': ');
  592. Indent;
  593. end
  594. else
  595. AppendToLine(DefinitionSeparator);
  596. end;
  597. procedure TBaseMarkdownWriter.DescrEndDefinitionEntry;
  598. begin
  599. OutputCurrentLine;
  600. if MarkDownEngine=meMkDocs then
  601. begin
  602. Undent;
  603. EnsureEmptyLine;
  604. end;
  605. end;
  606. procedure TBaseMarkdownWriter.DescrBeginSectionTitle;
  607. begin
  608. EnsureEmptyLine;
  609. end;
  610. procedure TBaseMarkdownWriter.DescrBeginSectionBody;
  611. begin
  612. EnsureEmptyLine;
  613. end;
  614. procedure TBaseMarkdownWriter.DescrEndSection;
  615. begin
  616. EnsureEmptyLine;
  617. end;
  618. procedure TBaseMarkdownWriter.DescrBeginRemark;
  619. begin
  620. if MarkDownEngine=meMkDocs then
  621. begin
  622. EnsureEmptyLine;
  623. AppendToLine('!!! Remark',False);
  624. OutputCurrentLine;
  625. end
  626. else
  627. FContentPrefix:='> ';
  628. end;
  629. procedure TBaseMarkdownWriter.DescrEndRemark;
  630. begin
  631. if MarkDownEngine=meMkDocs then
  632. begin
  633. OutputCurrentLine;
  634. AppendToLine('!!!',False);
  635. end;
  636. EnsureEmptyLine;
  637. FContentPrefix:='';
  638. end;
  639. procedure TBaseMarkdownWriter.DescrBeginTable(ColCount: Integer; HasBorder: Boolean);
  640. begin
  641. EnsureEmptyLine;
  642. FTableColCount:=ColCount;
  643. end;
  644. procedure TBaseMarkdownWriter.DescrEndTable;
  645. begin
  646. EnsureEmptyLine;
  647. end;
  648. procedure TBaseMarkdownWriter.DescrBeginTableCaption;
  649. begin
  650. BeginIgnore;
  651. end;
  652. procedure TBaseMarkdownWriter.DescrEndTableCaption;
  653. begin
  654. EndIgnore;
  655. end;
  656. procedure TBaseMarkdownWriter.DescrBeginTableHeadRow;
  657. begin
  658. end;
  659. procedure TBaseMarkdownWriter.DescrEndTableHeadRow;
  660. Var
  661. I : Integer;
  662. begin
  663. AppendToLine(' |',False);
  664. OutputCurrentLine;
  665. AppendToLine('|',False);
  666. For I:=1 to FTableColCount do
  667. AppendToLine('---|',False);
  668. OutputCurrentLine;
  669. end;
  670. procedure TBaseMarkdownWriter.DescrBeginTableRow;
  671. begin
  672. end;
  673. procedure TBaseMarkdownWriter.DescrEndTableRow;
  674. begin
  675. AppendToLine(' |',False);
  676. OutputCurrentLine;
  677. end;
  678. procedure TBaseMarkdownWriter.DescrBeginTableCell;
  679. begin
  680. AppendToLine('| ',False);
  681. end;
  682. procedure TBaseMarkdownWriter.DescrEndTableCell;
  683. begin
  684. AppendToLine(' ',False);
  685. end;
  686. function TBaseMarkdownWriter.OutputCurrentLine: Boolean;
  687. begin
  688. If FIgnoreCount>0 then
  689. exit(False);
  690. Result:=FCurrentLine<>'';
  691. if Result then
  692. begin
  693. FMarkDown.Add(Prefix+FCurrentLine);
  694. FCurrentLine:='';
  695. end;
  696. end;
  697. constructor TBaseMarkdownWriter.Create(APackage: TPasPackage;
  698. AEngine: TFPDocEngine);
  699. begin
  700. inherited Create(APackage, AEngine);
  701. FMarkDown:=TStringList.Create;
  702. FMetaData:=TStringList.Create;
  703. FMetaData.NameValueSeparator:=':';
  704. FIndents[FCurrentIndentIndex]:=0;
  705. CalcPrefix;
  706. Theme:='readthedocs';
  707. end;
  708. destructor TBaseMarkdownWriter.Destroy;
  709. begin
  710. FreeAndNil(FMarkDown);
  711. FreeAndNil(FMetadata);
  712. inherited Destroy;
  713. end;
  714. procedure TBaseMarkdownWriter.AppendHeader(aLevel: THeaderLevel;
  715. const AHeader: String; DoEscape : Boolean = true);
  716. begin
  717. EnsureEmptyLine;
  718. AppendToLine(StringOfChar('#',aLevel)+' ',False);
  719. AppendToLine(aHeader,DoEscape);
  720. end;
  721. procedure TBaseMarkdownWriter.Indent;
  722. begin
  723. Inc(FIndents[FCurrentIndentIndex],IndentSize);
  724. CalcPrefix;
  725. end;
  726. procedure TBaseMarkdownWriter.Undent;
  727. begin
  728. if IndentSize>CurrentIndent then
  729. FPDocError(SErrIndentMismatch);
  730. Dec(FIndents[FCurrentIndentIndex],IndentSize);
  731. CalcPrefix;
  732. end;
  733. procedure TBaseMarkdownWriter.ClearMarkDown;
  734. begin
  735. FMarkDown.Clear;
  736. FMetaData.Clear;
  737. FCurrentLine:='';
  738. FContentPrefix:='';
  739. FCurrentIndentIndex:=0;;
  740. FIndents[FCurrentIndentIndex]:=0;
  741. CalcPrefix;
  742. end;
  743. procedure TBaseMarkdownWriter.EnsureEmptyLine;
  744. begin
  745. if OutputCurrentLine then
  746. FMarkDown.Add('')
  747. else if (FmarkDown.Count>0) and (FMarkDown[FmarkDown.Count-1]<>'') then
  748. FMarkDown.Add('');
  749. end;
  750. procedure TBaseMarkdownWriter.SaveToFile(aFileName: string);
  751. Var
  752. Doc: TStrings;
  753. I : Integer;
  754. N,V : String;
  755. begin
  756. Doc:=TStringList.Create;
  757. try
  758. if FMetadata.Count>0 then
  759. begin
  760. Doc.Add('---');
  761. For I:=0 to FMetadata.Count-1 do
  762. begin
  763. FMetaData.GetNameValue(I,N,V);
  764. Doc.Add(Lowercase(N)+': "'+V+'"');
  765. end;
  766. Doc.Add('---');
  767. end;
  768. Doc.AddStrings(FMarkDown);
  769. Doc.SaveToFile(aFileName);
  770. finally
  771. Doc.Free;
  772. end;
  773. end;
  774. procedure TBaseMarkdownWriter.AddMetaData(const aName, aValue: string);
  775. begin
  776. FMetadata.Values[aName]:=aValue;
  777. end;
  778. end.