dw_basemd.pp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  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. resourcestring
  172. SErrCannotChangeIndentSizeWhenIndented = 'Cannot change indent size while text is indented.';
  173. SErrIndentMismatch = 'Indent mismatch: trying to undent when current indent too small';
  174. SErrNotInList = 'Not in list';
  175. SErrPopListStack = 'Pop list stack list type mismatch';
  176. SErrMinListStack = 'Min list stack reached';
  177. SErrMaxListStack = 'Max list stack reached';
  178. SErrMinIndentStack = 'Min indent stack reached';
  179. SErrMaxIndentStack = 'Max indent stack reached';
  180. procedure TBaseMarkdownWriter.SetIndentSize(AValue: Byte);
  181. begin
  182. if FIndentSize=AValue then Exit;
  183. if CurrentIndent>0 then
  184. FPDocError(SErrCannotChangeIndentSizeWhenIndented);
  185. FIndentSize:=AValue;
  186. end;
  187. function TBaseMarkdownWriter.GetCurrentIndent: Word;
  188. begin
  189. Result:=FIndents[FCurrentIndentIndex];
  190. end;
  191. procedure TBaseMarkdownWriter.clearIndent;
  192. begin
  193. FIndents[FCurrentIndentIndex]:=0;
  194. CalcPrefix;
  195. end;
  196. procedure TBaseMarkdownWriter.CalcPrefix;
  197. begin
  198. FPrefix:=StringOfChar(' ',CurrentIndent);
  199. end;
  200. function TBaseMarkdownWriter.CreateAllocator: TFileAllocator;
  201. begin
  202. Result:=TLongNameFileAllocator.Create('.md');
  203. end;
  204. procedure TBaseMarkdownWriter.DescrEmitNotesHeader(AContext: TPasElement);
  205. begin
  206. AppendHeader(2, SDocNotes);
  207. end;
  208. procedure TBaseMarkdownWriter.DescrEmitNotesFooter(AContext: TPasElement);
  209. begin
  210. EnsureEmptyLine;
  211. end;
  212. function TBaseMarkdownWriter.EscapeMarkDown(aText: Domstring): string;
  213. begin
  214. Result:=EscapeMarkDown(UTF8Encode(aText))
  215. end;
  216. function TBaseMarkdownWriter.EscapeMarkDown(aText: String): string;
  217. begin
  218. Result:=StringReplace(aText,'*','\*',[rfReplaceAll]);
  219. Result:=StringReplace(Result,'_','\_',[rfReplaceAll]);
  220. Result:=StringReplace(Result,'`','\`',[rfReplaceAll]);
  221. end;
  222. function TBaseMarkdownWriter.CreateLink(const aText, aLink: String): String;
  223. begin
  224. Result:=Format('[%s](%s)',[EscapeMarkDown(aText),aLink])
  225. end;
  226. procedure TBaseMarkdownWriter.AppendToLine(aText: DomString; DoEscape: boolean);
  227. begin
  228. If FIgnoreCount>0 then
  229. exit;
  230. AppendToLine(UTF8Encode(aText),DoEscape);
  231. end;
  232. procedure TBaseMarkdownWriter.AppendToLine(aText: UTF8String; DoEscape: boolean
  233. );
  234. begin
  235. if DoEscape then
  236. aText:=EscapeMarkDown(aText);
  237. if (FCurrentLine='') and (FContentPrefix<>'') then
  238. FCurrentLine:=FContentPrefix;
  239. FCurrentLine:=FCurrentLine+aText;
  240. end;
  241. procedure TBaseMarkdownWriter.AppendToLine(aFmt: UTF8String;
  242. aArgs: array of const; DoEscape: boolean);
  243. begin
  244. AppendToLine(Format(aFmt,aArgs),DoEscape);
  245. end;
  246. procedure TBaseMarkdownWriter.EmitLine(aText: UTF8String; DoEscape: boolean);
  247. begin
  248. OutputCurrentLine;
  249. AppendToLine(aText,DoEscape);
  250. OutputCurrentLine;
  251. end;
  252. procedure TBaseMarkdownWriter.EmitLine(aFmt: UTF8String; aArgs: array of const;
  253. DoEscape: boolean);
  254. begin
  255. EmitLine(Format(aFmt,aArgs),DoEscape);
  256. end;
  257. procedure TBaseMarkdownWriter.AppendLink(const aText, aLink: String);
  258. begin
  259. AppendToLine(CreateLink(aText,aLink),False);
  260. end;
  261. procedure TBaseMarkdownWriter.AppendRendered(aText: String; aRender: TRender);
  262. begin
  263. StartSpan(aRender);
  264. AppendToLine(aText);
  265. EndSpan(aRender);
  266. end;
  267. procedure TBaseMarkdownWriter.AppendKeyWord(aText: String);
  268. begin
  269. AppendRendered(aText,KeywordRendering);
  270. end;
  271. procedure TBaseMarkdownWriter.AppendSymbol(aText: String);
  272. begin
  273. AppendRendered(aText,SymbolRendering);
  274. end;
  275. procedure TBaseMarkdownWriter.AppendTableHeader(aHeaders: array of String);
  276. Var
  277. S : String;
  278. begin
  279. DescrBeginTable(Length(aHeaders),False);
  280. DescrBeginTableHeadRow;
  281. for S in aHeaders do
  282. begin
  283. DescrBeginTableCell;
  284. AppendToLine(S);
  285. DescrEndTableCell;
  286. end;
  287. DescrEndTableHeadRow;
  288. end;
  289. procedure TBaseMarkdownWriter.EmitCode(aCodeBlock: String; aIndent : Integer = 0);
  290. Var
  291. L : TStringList;
  292. begin
  293. L:=TStringList.Create;
  294. try
  295. L.Text:=aCodeBlock;
  296. EmitCode(L,aIndent);
  297. finally
  298. L.Free;
  299. end;
  300. end;
  301. procedure TBaseMarkdownWriter.EmitCode(aCodeBlock: TStrings; aIndent : Integer = 0);
  302. var
  303. S,aPrefix : string;
  304. begin
  305. aPrefix:=StringOfChar(' ',aIndent);
  306. For S in aCodeBlock do
  307. EmitCodeLine(aPrefix+S);
  308. end;
  309. procedure TBaseMarkdownWriter.EmitCodeLine(aCodeLine: string);
  310. begin
  311. EmitLine(aCodeLine,False);
  312. end;
  313. procedure TBaseMarkdownWriter.DescrWriteText(const AText: DOMString);
  314. begin
  315. AppendToLine(aText);
  316. end;
  317. procedure TBaseMarkdownWriter.DescrBeginBold;
  318. begin
  319. AppendToLine('**',False);
  320. end;
  321. procedure TBaseMarkdownWriter.DescrEndBold;
  322. begin
  323. AppendToLine('**',False);
  324. end;
  325. procedure TBaseMarkdownWriter.DescrBeginItalic;
  326. begin
  327. AppendToLine('*',False);
  328. end;
  329. procedure TBaseMarkdownWriter.DescrEndItalic;
  330. begin
  331. AppendToLine('*',False);
  332. end;
  333. procedure TBaseMarkdownWriter.DescrBeginEmph;
  334. begin
  335. AppendToLine('*',False);
  336. end;
  337. procedure TBaseMarkdownWriter.DescrEndEmph;
  338. begin
  339. AppendToLine('*',False);
  340. end;
  341. procedure TBaseMarkdownWriter.StartSpan(aRender : TRender);
  342. begin
  343. case aRender of
  344. rStrong : AppendToLine('**',False);
  345. rEmphasis : AppendToLine('*',False);
  346. rCode : AppendToLine('`',False);
  347. end;
  348. end;
  349. procedure TBaseMarkdownWriter.PushIndent(aNewIndent: Byte);
  350. begin
  351. if FCurrentIndentIndex>=MaxIndents then
  352. FPDocError(SErrMaxIndentStack);
  353. Inc(FCurrentIndentIndex);
  354. Findents[FCurrentIndentIndex]:=aNewIndent;
  355. CalcPrefix;
  356. end;
  357. procedure TBaseMarkdownWriter.PopIndent;
  358. begin
  359. if FCurrentIndentIndex<=0 then
  360. FPDocError(SErrMinIndentStack);
  361. Dec(FCurrentIndentIndex);
  362. CalcPrefix;
  363. end;
  364. procedure TBaseMarkdownWriter.StartList(aType: TListType);
  365. begin
  366. If FListStack>=MaxLists then
  367. FPDocError(SErrMaxListStack);
  368. OutputCurrentLine;
  369. Inc(FListStack);
  370. FListTypes[FListStack]:=aType;
  371. if FListStack>1 then
  372. PushIndent(CurrentIndent+Length(GetListprefix));
  373. end;
  374. procedure TBaseMarkdownWriter.StopList(aType: TListType);
  375. begin
  376. OutputCurrentLine;
  377. If FListStack<=0 then
  378. FPDocError(SErrMinListStack);
  379. if FListTypes[FListStack]<>aType then
  380. FPDocError(SErrPopListStack);
  381. if FListStack>1 then
  382. PopIndent;
  383. Dec(FListStack);
  384. end;
  385. procedure TBaseMarkdownWriter.BeginIgnore;
  386. begin
  387. Inc(FIgnoreCount);
  388. end;
  389. procedure TBaseMarkdownWriter.EndIgnore;
  390. begin
  391. If FignoreCount>0 then
  392. Dec(FIgnoreCount);
  393. end;
  394. procedure TBaseMarkdownWriter.DoLineBreak;
  395. begin
  396. if FCurrentLine<>'' then
  397. begin
  398. FCurrentLine:=FCurrentLine+' ';
  399. OutputCurrentLine;
  400. end;
  401. end;
  402. function TBaseMarkdownWriter.InList: Boolean;
  403. begin
  404. Result:=FlistStack>0;
  405. end;
  406. function TBaseMarkdownWriter.CurrentList: TListType;
  407. begin
  408. if FListStack=0 then
  409. FPDOcError(SErrNotInList);
  410. Result:=FListTypes[FListStack];
  411. end;
  412. procedure TBaseMarkdownWriter.EndSpan(aRender : TRender);
  413. begin
  414. case aRender of
  415. rStrong : AppendToLine('**',False);
  416. rEmphasis : AppendToLine('*',False);
  417. rCode : AppendToLine('`',False);
  418. end;
  419. end;
  420. procedure TBaseMarkdownWriter.DescrBeginUnderline;
  421. begin
  422. StartSpan(UnderlineRendering);
  423. end;
  424. procedure TBaseMarkdownWriter.DescrEndUnderline;
  425. begin
  426. EndSpan(UnderlineRendering);
  427. end;
  428. procedure TBaseMarkdownWriter.DescrWriteImageEl(const AFileName, ACaption, ALinkName : DOMString);
  429. Var
  430. aLink,D,FN : String;
  431. L : integer;
  432. begin
  433. // Determine URL for image.
  434. If (Module=Nil) then
  435. D:=Allocator.GetRelativePathToTop(Package)
  436. else
  437. D:=Allocator.GetRelativePathToTop(Module);
  438. L:=Length(D);
  439. If (L>0) and (D[L]<>'/') then
  440. D:=D+'/';
  441. FN:=D + BaseImageURL+ Utf8Encode(AFileName);
  442. EnsureEmptyLine;
  443. aLink:='!['+UTF8Encode(aCaption)+']('+FN+')';
  444. AppendToLine(aLink,False);
  445. end;
  446. procedure TBaseMarkdownWriter.DescrWriteFileEl(const AText: DOMString);
  447. begin
  448. AppendRendered(UTF8Encode(aText),FileRendering);
  449. end;
  450. procedure TBaseMarkdownWriter.DescrWriteKeywordEl(const AText: DOMString);
  451. begin
  452. AppendKeyWord(UTF8ENcode(aText));
  453. end;
  454. procedure TBaseMarkdownWriter.DescrWriteVarEl(const AText: DOMString);
  455. begin
  456. AppendRendered(UTF8Encode(aText),VarRendering);
  457. end;
  458. procedure TBaseMarkdownWriter.DescrBeginLink(const AId: DOMString);
  459. var
  460. a,s,n : String;
  461. begin
  462. a:=UTF8Encode(AId);
  463. s := UTF8Encode(ResolveLinkID(a));
  464. if Length(s) > 0 then
  465. begin
  466. FLink:=S;
  467. AppendToLine('[');
  468. end
  469. else
  470. begin
  471. FLink:='';
  472. if assigned(module) then
  473. s:=module.name
  474. else
  475. s:='?';
  476. if a='' then a:='<empty>';
  477. if Assigned(CurrentContext) then
  478. N:=CurrentContext.Name
  479. else
  480. N:='?';
  481. DoLog(SErrUnknownLinkID, [s,n,a]);
  482. end
  483. end;
  484. procedure TBaseMarkdownWriter.DescrEndLink;
  485. begin
  486. AppendToLine(']('+FLink+') ',false);
  487. FLink:='';
  488. end;
  489. procedure TBaseMarkdownWriter.DescrBeginURL(const AURL: DOMString);
  490. begin
  491. FLink:=UTF8Encode(aURL);
  492. AppendToLine('[');
  493. end;
  494. procedure TBaseMarkdownWriter.DescrEndURL;
  495. begin
  496. AppendToLine(']('+FLink+') ',false);
  497. FLink:='';
  498. end;
  499. procedure TBaseMarkdownWriter.DescrWriteLinebreak;
  500. begin
  501. AppendToLine(' ');
  502. OutputCurrentLine;
  503. end;
  504. procedure TBaseMarkdownWriter.DescrBeginParagraph;
  505. begin
  506. EnsureEmptyLine;
  507. end;
  508. procedure TBaseMarkdownWriter.DescrEndParagraph;
  509. begin
  510. EnsureEmptyLine;
  511. end;
  512. procedure TBaseMarkdownWriter.DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String);
  513. Var
  514. hl : string;
  515. begin
  516. hl:=AHighlighterName;
  517. if SameText(hl,'Pascal') or (hl='') then
  518. hl:='delphi';
  519. OutputCurrentLine;
  520. AppendToLine('```'+hl,False);
  521. PushIndent(0);
  522. end;
  523. procedure TBaseMarkdownWriter.DescrWriteCodeLine(const ALine: String);
  524. begin
  525. EmitCodeLine(aLine);
  526. end;
  527. procedure TBaseMarkdownWriter.DescrEndCode;
  528. begin
  529. OutputCurrentLine;
  530. AppendToLine('```',False);
  531. OutputCurrentLine;
  532. PopIndent;
  533. end;
  534. procedure TBaseMarkdownWriter.DescrBeginOrderedList;
  535. begin
  536. StartList(ltOrdered);
  537. end;
  538. procedure TBaseMarkdownWriter.DescrEndOrderedList;
  539. begin
  540. StopList(ltOrdered);
  541. end;
  542. procedure TBaseMarkdownWriter.DescrBeginUnorderedList;
  543. begin
  544. StartList(ltUnordered);
  545. end;
  546. procedure TBaseMarkdownWriter.DescrEndUnorderedList;
  547. begin
  548. StopList(ltUnordered);
  549. end;
  550. procedure TBaseMarkdownWriter.DescrBeginDefinitionList;
  551. begin
  552. StartList(ltDefinition);
  553. end;
  554. procedure TBaseMarkdownWriter.DescrEndDefinitionList;
  555. begin
  556. StopList(ltDefinition);
  557. end;
  558. function TBaseMarkdownWriter.GetListPrefix : String;
  559. begin
  560. Case CurrentList of
  561. ltOrdered : Result:='1. ';
  562. ltUnordered : Result:='- ';
  563. ltDefinition : Result:=': ';
  564. end;
  565. end;
  566. procedure TBaseMarkdownWriter.DescrBeginListItem;
  567. Var
  568. Pref : String;
  569. begin
  570. Pref:=GetListPrefix;
  571. AppendToLine(Pref);
  572. end;
  573. procedure TBaseMarkdownWriter.DescrEndListItem;
  574. begin
  575. OutputCurrentLine;
  576. end;
  577. procedure TBaseMarkdownWriter.DescrBeginDefinitionTerm;
  578. begin
  579. if MarkDownEngine=meMkDocs then
  580. EnsureEmptyLine
  581. else
  582. begin
  583. AppendToLine('- ');
  584. StartSpan(DefinitionTermRender);
  585. end;
  586. end;
  587. procedure TBaseMarkdownWriter.DescrEndDefinitionTerm;
  588. begin
  589. if MarkDownEngine=meMkDocs then
  590. OutputCurrentLine
  591. else
  592. EndSpan(DefinitionTermRender);
  593. end;
  594. procedure TBaseMarkdownWriter.DescrBeginDefinitionEntry;
  595. begin
  596. if MarkDownEngine=meMkDocs then
  597. begin
  598. AppendToLine(': ');
  599. Indent;
  600. end
  601. else
  602. AppendToLine(DefinitionSeparator);
  603. end;
  604. procedure TBaseMarkdownWriter.DescrEndDefinitionEntry;
  605. begin
  606. OutputCurrentLine;
  607. if MarkDownEngine=meMkDocs then
  608. begin
  609. Undent;
  610. EnsureEmptyLine;
  611. end;
  612. end;
  613. procedure TBaseMarkdownWriter.DescrBeginSectionTitle;
  614. begin
  615. EnsureEmptyLine;
  616. end;
  617. procedure TBaseMarkdownWriter.DescrBeginSectionBody;
  618. begin
  619. EnsureEmptyLine;
  620. end;
  621. procedure TBaseMarkdownWriter.DescrEndSection;
  622. begin
  623. EnsureEmptyLine;
  624. end;
  625. procedure TBaseMarkdownWriter.DescrBeginRemark;
  626. begin
  627. if MarkDownEngine=meMkDocs then
  628. begin
  629. EnsureEmptyLine;
  630. AppendToLine('!!! Remark',False);
  631. OutputCurrentLine;
  632. end
  633. else
  634. FContentPrefix:='> ';
  635. end;
  636. procedure TBaseMarkdownWriter.DescrEndRemark;
  637. begin
  638. if MarkDownEngine=meMkDocs then
  639. begin
  640. OutputCurrentLine;
  641. AppendToLine('!!!',False);
  642. end;
  643. EnsureEmptyLine;
  644. FContentPrefix:='';
  645. end;
  646. procedure TBaseMarkdownWriter.DescrBeginTable(ColCount: Integer; HasBorder: Boolean);
  647. begin
  648. EnsureEmptyLine;
  649. FTableColCount:=ColCount;
  650. end;
  651. procedure TBaseMarkdownWriter.DescrEndTable;
  652. begin
  653. EnsureEmptyLine;
  654. end;
  655. procedure TBaseMarkdownWriter.DescrBeginTableCaption;
  656. begin
  657. BeginIgnore;
  658. end;
  659. procedure TBaseMarkdownWriter.DescrEndTableCaption;
  660. begin
  661. EndIgnore;
  662. end;
  663. procedure TBaseMarkdownWriter.DescrBeginTableHeadRow;
  664. begin
  665. end;
  666. procedure TBaseMarkdownWriter.DescrEndTableHeadRow;
  667. Var
  668. I : Integer;
  669. begin
  670. AppendToLine(' |',False);
  671. OutputCurrentLine;
  672. AppendToLine('|',False);
  673. For I:=1 to FTableColCount do
  674. AppendToLine('---|',False);
  675. OutputCurrentLine;
  676. end;
  677. procedure TBaseMarkdownWriter.DescrBeginTableRow;
  678. begin
  679. end;
  680. procedure TBaseMarkdownWriter.DescrEndTableRow;
  681. begin
  682. AppendToLine(' |',False);
  683. OutputCurrentLine;
  684. end;
  685. procedure TBaseMarkdownWriter.DescrBeginTableCell;
  686. begin
  687. AppendToLine('| ',False);
  688. end;
  689. procedure TBaseMarkdownWriter.DescrEndTableCell;
  690. begin
  691. AppendToLine(' ',False);
  692. end;
  693. function TBaseMarkdownWriter.OutputCurrentLine: Boolean;
  694. begin
  695. If FIgnoreCount>0 then
  696. exit(False);
  697. Result:=FCurrentLine<>'';
  698. if Result then
  699. begin
  700. FMarkDown.Add(Prefix+FCurrentLine);
  701. FCurrentLine:='';
  702. end;
  703. end;
  704. constructor TBaseMarkdownWriter.Create(APackage: TPasPackage;
  705. AEngine: TFPDocEngine);
  706. begin
  707. inherited Create(APackage, AEngine);
  708. FMarkDown:=TStringList.Create;
  709. FMetaData:=TStringList.Create;
  710. FMetaData.NameValueSeparator:=':';
  711. FIndents[FCurrentIndentIndex]:=0;
  712. CalcPrefix;
  713. Theme:='readthedocs';
  714. end;
  715. destructor TBaseMarkdownWriter.Destroy;
  716. begin
  717. FreeAndNil(FMarkDown);
  718. FreeAndNil(FMetadata);
  719. inherited Destroy;
  720. end;
  721. procedure TBaseMarkdownWriter.AppendHeader(aLevel: THeaderLevel;
  722. const AHeader: String; DoEscape : Boolean = true);
  723. begin
  724. EnsureEmptyLine;
  725. AppendToLine(StringOfChar('#',aLevel)+' ',False);
  726. AppendToLine(aHeader,DoEscape);
  727. end;
  728. procedure TBaseMarkdownWriter.Indent;
  729. begin
  730. Inc(FIndents[FCurrentIndentIndex],IndentSize);
  731. CalcPrefix;
  732. end;
  733. procedure TBaseMarkdownWriter.Undent;
  734. begin
  735. if IndentSize>CurrentIndent then
  736. FPDocError(SErrIndentMismatch);
  737. Dec(FIndents[FCurrentIndentIndex],IndentSize);
  738. CalcPrefix;
  739. end;
  740. procedure TBaseMarkdownWriter.ClearMarkDown;
  741. begin
  742. FMarkDown.Clear;
  743. FMetaData.Clear;
  744. FCurrentLine:='';
  745. FContentPrefix:='';
  746. FCurrentIndentIndex:=0;;
  747. FIndents[FCurrentIndentIndex]:=0;
  748. CalcPrefix;
  749. end;
  750. procedure TBaseMarkdownWriter.EnsureEmptyLine;
  751. begin
  752. if OutputCurrentLine then
  753. FMarkDown.Add('')
  754. else if (FmarkDown.Count>0) and (FMarkDown[FmarkDown.Count-1]<>'') then
  755. FMarkDown.Add('');
  756. end;
  757. procedure TBaseMarkdownWriter.SaveToFile(aFileName: string);
  758. Var
  759. Doc: TStrings;
  760. I : Integer;
  761. N,V : String;
  762. begin
  763. Doc:=TStringList.Create;
  764. try
  765. if FMetadata.Count>0 then
  766. begin
  767. Doc.Add('---');
  768. For I:=0 to FMetadata.Count-1 do
  769. begin
  770. FMetaData.GetNameValue(I,N,V);
  771. Doc.Add(Lowercase(N)+': "'+V+'"');
  772. end;
  773. Doc.Add('---');
  774. end;
  775. Doc.AddStrings(FMarkDown);
  776. Doc.SaveToFile(aFileName);
  777. finally
  778. Doc.Free;
  779. end;
  780. end;
  781. procedure TBaseMarkdownWriter.AddMetaData(const aName, aValue: string);
  782. begin
  783. FMetadata.Values[aName]:=aValue;
  784. end;
  785. end.