{ FPDoc - Free Pascal Documentation Tool Copyright (C) 2000 - 2005 by Areca Systems GmbH / Sebastian Guenther, sg@freepascal.org * HTML/XHTML output generator See the file COPYING, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. } {$mode objfpc} {$H+} unit dw_markdown; {$WARN 5024 off : Parameter "$1" not used} interface uses Classes, dGlobals, PasTree, dWriter, dw_basemd, DOM; type TMemberListOption = (mloAppendParent,mloAppendType,mloCheckVisibility); TMemberListOptions = Set of TMemberListOption; { TMarkdownWriter } TMarkdownWriter = class(TBaseMarkdownWriter) private FBaseImageURL: String; FImageFileList: TStrings; FIndexColCount: Integer; FAdditionalConfig : TStrings; FFooterMarkDown : TStrings; FHeaderMarkDown : TStrings; FOnTest: TNotifyEvent; FNavigation : TStrings; function GetAdditionalConfig: TStrings; function GetClassDeclaration(aEl: TPasClassType): String; function GetClassDeclarationFirstLine(aEl: TPasClassType): String; function GetDeclaration(aEl: TPasElement): String; function GetFooterMarkDown: TStrings; function GetHeaderMarkDown: TStrings; function GetPageCount: Integer; procedure SetOnTest(const AValue: TNotifyEvent); protected function GetFileBaseDir(aOutput: String): String; override; // MkDocs procedure WriteMkdocsYaml; virtual; procedure CreateMkdocsYaml(mkDocs: TStrings); virtual; procedure AddToNavigation(const aFileName: String; aElement: TPasElement; aSubPageIndex: Integer); // Some raw markdown functions procedure AppendPageFooter; virtual; procedure WriteMetadata; virtual; procedure AppendPageHeader; virtual; Procedure AppendText(Const S : String); Procedure AppendTitle(Const S : String); Procedure AppendTitle(Const Fmt : String; Const Args : Array of const); // Description Procedure AppendShortDescr(AContext : TPasElement; DocNode : TDocNode); virtual; procedure AppendShortDescr(Element: TPasElement); virtual; procedure AppendShortDescrCell(Element: TPasElement); virtual; procedure AppendDescr(AContext: TPasElement; DescrNode: TDOMElement; AutoInsertBlock: Boolean); virtual; procedure AppendDescrSection(AContext: TPasElement; DescrNode: TDOMElement; const ATitle: String); virtual; procedure AppendDescrSection(AContext: TPasElement; DescrNode: TDOMElement; const ATitle: DOMString); virtual; Procedure AppendHyperlink(Element: TPasElement); Function CreateHyperlink(Element: TPasElement) : string; // Reusable routines procedure CollectDeclarationTypes(aList: TStringList; aElement: TPasElement ); procedure CollectSeeAlsoNodes(aList: TStringList; aSeeAlso: TDomNode); procedure AddModuleIdentifiers(AModule : TPasModule; L : TStrings); procedure AppendSourceRef(AElement: TPasElement); virtual; procedure AppendDeclaration(aEl: TPasElement; aKind: string; PrependVisibility: Boolean); virtual; procedure FinishElementPage(AElement: TPasElement; SkipDescription: Boolean=false); virtual; Procedure AppendSeeAlsoSection(AElement : TPasElement;DocNode : TDocNode); virtual; Procedure AppendExampleSection(AElement : TPasElement;DocNode : TDocNode); virtual; procedure AppendProcArgsSection(Element: TPasProcedureBase); virtual; procedure CreateMemberDeclarations(AParent: TPasElement; Members: TFPList; Options : TMemberListOptions); virtual; Procedure CreateTopicLinks(Node : TDocNode; PasElement : TPasElement); virtual; procedure CreateIndexPage(L : TStringList); virtual; procedure CreateSimpleSubpage(aModule: TPasModule; const ATitle, aItem: String; AList: TFPList); virtual; // Various kind of pages. // Main entry procedure CreatePageBody(AElement: TPasElement; ASubpageIndex: Integer); virtual; // Package procedure CreatePackagePageBody; virtual; procedure CreatePackageIndex; virtual; procedure CreatePackageClassHierarchy; virtual; procedure CreateClassHierarchyPage(AddUnit : Boolean); virtual; // Module procedure CreateModuleIndexPage(AModule: TPasModule); virtual; procedure CreateModuleMainPageBody(AModule: TPasModule); virtual; procedure CreateModulePageBody(AModule: TPasModule; ASubpageIndex: Integer); virtual; // Per simple type procedure CreateResStringsPage(aModule: TPasModule); virtual; Procedure CreateTopicPageBody(AElement : TTopicElement); virtual; procedure CreateConstPageBody(AConst: TPasConst); virtual; procedure CreateTypePageBody(AType: TPasType); virtual; procedure CreateVarPageBody(AVar: TPasVariable); virtual; procedure CreateProcPageBody(AProc: TPasProcedureBase); virtual; // Class/Record procedure CreateClassMainPage(aClass: TPasClassType); virtual; procedure CreateClassPageBody(AClass: TPasClassType; ASubpageIndex: Integer); virtual; procedure CreateClassMemberPageBody(AElement: TPasElement); virtual; procedure CreateInheritanceSubpage(aClass: TPasClassType; aTitle : string; AFilter: TMemberFilter); virtual; procedure CreateSortedSubpage(ACLass: TPasClassType; aTitle : string; AFilter: TMemberFilter ); virtual; public constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); override; destructor Destroy; override; // Single-page generation procedure WriteDocPage(const aFileName: String; aElement: TPasElement; aSubPageIndex: Integer); override; // Start producing html complete package documentation function ModuleForElement(AnElement:TPasElement):TPasModule; Function InterPretOption(Const Cmd,Arg : String) : boolean; override; Procedure WriteDoc; override; Class Function FileNameExtension : String; override; class procedure Usage(List: TStrings); override; Class procedure SplitImport(var AFilename, ALinkPrefix: String); override; property OnTest: TNotifyEvent read FOnTest write SetOnTest; Property IndexColCount : Integer Read FIndexColCount write FIndexColCount; Property BaseImageURL : String Read FBaseImageURL Write FBaseImageURL; Property HeaderMarkDown : TStrings Read GetHeaderMarkDown; Property FooterMarkDown : TStrings Read GetFooterMarkDown; property AdditionalConfig : TStrings Read GetAdditionalConfig; end; implementation uses SysUtils, fpdocclasstree; Function FixHTMLpath(S : String) : STring; begin Result:=StringReplace(S,'\','/',[rfReplaceAll]); end; constructor TMarkdownWriter.Create(APackage: TPasPackage; AEngine: TFPDocEngine); begin inherited Create(APackage, AEngine); IndexColCount:=3; FImageFileList := TStringList.Create; FNavigation:=TStringList.Create; end; destructor TMarkdownWriter.Destroy; begin FreeAndNil(FImageFileList); FreeAndNil(FAdditionalConfig); FreeAndNil(FFooterMarkDown); FreeAndNil(FHeaderMarkDown); FreeAndNil(FNavigation); inherited Destroy; end; procedure TMarkdownWriter.AddToNavigation(const aFileName: String; aElement: TPasElement; aSubPageIndex: Integer); Procedure AddToNav(aLevel : Integer; aName,aFile : String); begin if aName<>'' then aName:=''''+aName+''':'; if aFile<>'' then aFile:=' '''+aFile+''''; FNavigation.Add(StringOfChar(' ',aLevel*4)+'- '+aName+aFile); end; begin if aElement is TPasPackage then begin case aSubPageIndex of IdentifierIndex: begin AddToNav(1,SDocPackageLinkTitle,''); AddToNav(2,SDocOverview,aFileName); end; IndexSubIndex : AddToNav(2,SDocIdentifierIndex,aFileName); ClassHierarchySubIndex : AddToNav(2,SDocPackageClassHierarchy,aFileName); end end else if (aElement is TPasModule) then begin case aSubPageIndex of IdentifierIndex : begin AddToNav(1,Format(StringReplace(SDocUnitMenuTitle,'''','',[rfReplaceALl]),[aElement.Name]),''); AddToNav(2,SDocOverview,aFileName); end; ResstrSubindex: AddToNav(2,SDocResStrings,aFileName); ConstsSubindex: AddToNav(2,SDocConstants,aFileName); TypesSubindex: AddToNav(2,SDocTypes,aFileName); ClassesSubindex: AddToNav(2,SDocClasses,aFileName); ProcsSubindex: AddToNav(2,SDocProceduresAndFunctions,aFileName); VarsSubindex: AddToNav(2,SDocVariables,aFileName); end; end end; procedure TMarkdownWriter.WriteDocPage(const aFileName: String; aElement: TPasElement; aSubPageIndex: Integer); begin if MarkDownEngine=meMkDocs then AddToNavigation(aFileName,aElement,aSubPageIndex); ClearMarkDown; WriteMetaData; AppendPageHeader; CreatePageBody(AElement, ASubpageIndex); AppendPageFooter; SaveToFile(GetFileBaseDir(Engine.Output)+aFileName); end; procedure TMarkdownWriter.WriteMetadata; begin end; procedure TMarkdownWriter.AppendPageHeader; Var S : String; begin if Assigned(FHeaderMarkDown) then begin EnsureEmptyLine; For S in FHeaderMarkDown do AppendToLine(S,False) end; end; procedure TMarkdownWriter.WriteMkdocsYaml; Var mkDocs : TStrings; begin mkDocs:=TStringList.Create; try CreatemkDocsYaml(mkDocs); mkDocs.SaveToFile(Engine.Output+PathDelim+'mkdocs.yml'); finally Mkdocs.Free; end; end; procedure TMarkdownWriter.CreateMkdocsYaml(mkDocs: TStrings); begin With MKDocs do begin add('site_name: '+SDocPackageTitle,[Package.Name]); add(''); add('docs_dir: docs'); add(''); add('site_dir: site'); add(''); add('markdown_extensions:'); add(' - def_list'); add(' - codehilite'); add(' - admonition'); add(''); add('theme: '+Theme); If Assigned(FAdditionalConfig) then begin add(''); AddStrings(FAdditionalConfig); end; add(''); add('nav:'); AddStrings(FNavigation); end; end; procedure TMarkdownWriter.WriteDoc; begin Inherited; If MarkDownEngine=memkDocs then WriteMkdocsYaml; end; function TMarkdownWriter.GetFooterMarkDown: TStrings; begin If FFooterMarkDown=Nil then FFooterMarkDown:=TStringList.Create; Result:=FFooterMarkDown; end; function TMarkdownWriter.GetHeaderMarkDown: TStrings; begin If FHeaderMarkDown=Nil then FHeaderMarkDown:=TStringList.Create; Result:=FHeaderMarkDown; end; function TMarkdownWriter.ModuleForElement(AnElement:TPasElement):TPasModule; begin result:=TPasModule(AnElement); while assigned(result) and not (result is TPasModule) do result:=TPasModule(result.parent); if not (result is TPasModule) then result:=nil; end; procedure TMarkdownWriter.AppendShortDescr(AContext: TPasElement; DocNode: TDocNode) ; Var N : TDocNode; begin if Not Assigned(DocNode) then exit; N:=Nil; If (DocNode.Link<>'') then N:=Engine.FindLinkedNode(DocNode); If (N=Nil) then N:=DocNode; If Assigned(N.ShortDescr) then if not ConvertShort(AContext,N.ShortDescr) then Warning(AContext, SErrInvalidShortDescr) end; procedure TMarkdownWriter.AppendShortDescr(Element: TPasElement); begin AppendShortDescr(Element,Engine.FindDocNode(Element)); end; procedure TMarkdownWriter.AppendShortDescrCell(Element: TPasElement); begin if Not Assigned(Engine.FindShortDescr(Element)) then exit; DescrBeginTableCell; AppendShortDescr(Element); DescrEndTableCell; end; procedure TMarkdownWriter.AppendDescr(AContext: TPasElement; DescrNode: TDOMElement; AutoInsertBlock: Boolean); begin if Not Assigned(DescrNode) then exit; EnsureEmptyLine; ConvertDescr(AContext, DescrNode, AutoInsertBlock); end; procedure TMarkdownWriter.AppendDescrSection(AContext: TPasElement; DescrNode: TDOMElement; const ATitle: String); begin if IsDescrNodeEmpty(DescrNode) then exit; If (ATitle<>'') then // Can be empty for topic. AppendHeader(2,ATitle); AppendDescr(AContext, DescrNode, True); end; procedure TMarkdownWriter.AppendDescrSection(AContext: TPasElement; DescrNode: TDOMElement; const ATitle: DOMString); begin AppendDescrSection(aContext,DescrNode,UTF8Encode(aTitle)); end; procedure TMarkdownWriter.AppendHyperlink(Element: TPasElement); begin if Not Assigned(Element) then begin AppendText(''); exit; end; AppendToLine(CreateHyperLink(Element),False) end; function TMarkdownWriter.CreateHyperlink(Element: TPasElement): string; var s: DOMString; UnitList: TFPList; i: Integer; ThisPackage: TLinkNode; begin if Not Assigned(Element) then Exit('\'); if Not Element.InheritsFrom(TPasUnresolvedTypeRef) then begin if Element is TPasEnumValue then s := ResolveLinkID(Element.Parent.PathName) else s := ResolveLinkID(Element.PathName); end else begin s := ResolveLinkID(Element.Name); if Length(s) = 0 then begin { Try all packages } ThisPackage := Engine.RootLinkNode.FirstChild; while Assigned(ThisPackage) do begin s := ResolveLinkID(ThisPackage.Name + '.' + Element.Name); if Length(s) > 0 then break; ThisPackage := ThisPackage.NextSibling; end; if Length(s) = 0 then begin { Okay, then we have to try all imported units of the current module } UnitList := Module.InterfaceSection.UsesList; for i := UnitList.Count - 1 downto 0 do begin { Try all packages } ThisPackage := Engine.RootLinkNode.FirstChild; while Assigned(ThisPackage) do begin s := ResolveLinkID(ThisPackage.Name + '.' + TPasType(UnitList[i]).Name + '.' + Element.Name); if Length(s) > 0 then break; ThisPackage := ThisPackage.NextSibling; end; if length(s)=0 then s := ResolveLinkID('#rtl.System.' + Element.Name); if Length(s) > 0 then break; end; end; end; end; if Length(s) > 0 then Result:=CreateLink(Element.Name,UTF8Encode(S)) else Result:=Element.Name; end; procedure TMarkdownWriter.AppendProcArgsSection(Element: TPasProcedureBase); var HasFullDescr, IsFirstType: Boolean; ResultEl: TPasResultElement; DocNode: TDocNode; aList : TStringList; Procedure CollectVariant(AProc: TPasProcedure); var i: Integer; Arg: TPasArgument; aType : TPasProcedureType; begin aType:=aProc.ProcType; for I:=0 to aType.Args.Count-1 do begin Arg:=TPasArgument(aType.Args[i]); if IsDescrNodeEmpty(Engine.FindShortDescr(Arg)) then Continue; aList.AddObject(Arg.Name,Arg); end; if (aType is TPasFunctionType) and (ResultEl=Nil) then ResultEl:=TPasFunctionType(aType).ResultEl; end; Procedure WriteType(aProc : TPasProcedure; aName: string); var i: Integer; Arg: TPasArgument; aType : TPasProcedureType; begin aType:=aProc.ProcType; for I:=0 to aType.Args.Count-1 do begin Arg:=TPasArgument(aType.Args[i]); if SameText(Arg.Name,aName) then begin if not IsFirstType then AppendText(', '); AppendHyperlink(Arg.ArgType); end; if IsDescrNodeEmpty(Engine.FindShortDescr(Arg)) then Continue; aList.AddObject(Arg.Name,Arg); end; end; Procedure WriteTypes(aName: string); Var I : Integer; begin IsFirstType:=True; if Element.ClassType <> TPasOverloadedProc then WriteType(TPasProcedure(Element),aName) else for i := 0 to TPasOverloadedProc(Element).Overloads.Count - 1 do WriteType(TPasProcedure(TPasOverloadedProc(Element).Overloads[i]),AName); end; Var I: Integer; begin ResultEl:=Nil; aList:=TStringList.Create; try AList.Duplicates:=DupIgnore; if Element.ClassType <> TPasOverloadedProc then CollectVariant(TPasProcedure(Element)) else for i := 0 to TPasOverloadedProc(Element).Overloads.Count - 1 do CollectVariant(TPasProcedure(TPasOverloadedProc(Element).Overloads[i])); If AList.Count<>0 then begin AppendHeader(2,SDocArguments); AppendTableHeader([SDocName,SDocTypes,SDocDescription]); for I:=0 to aList.Count-1 do begin DescrBeginTableRow; DescrBeginTableCell; AppendText(aList[i]); DescrEndTableCell; DescrBeginTableCell; WriteTypes(aList[i]); DescrEndTableCell; AppendShortDescrCell(TPasArgument(aList.Objects[i])); DescrEndTableRow; end; DescrEndTable; end; finally aList.Free; end; if Not Assigned(ResultEl) then Exit; DocNode := Engine.FindDocNode(ResultEl); HasFullDescr := Assigned(DocNode) and not IsDescrNodeEmpty(DocNode.Descr); if HasFullDescr or (Assigned(DocNode) and not IsDescrNodeEmpty(DocNode.ShortDescr)) then begin AppendHeader(2, SDocFunctionResult); if HasFullDescr then AppendDescr(ResultEl,DocNode.Descr, True) else AppendDescr(ResultEl,DocNode.ShortDescr, False); end; end; procedure TMarkdownWriter.AppendSourceRef(AElement: TPasElement); begin EnsureEmptyLine; AppendToLine(Format(SDocSourcePosition, [ExtractFileName(AElement.SourceFilename), AElement.SourceLinenumber])); EnsureEmptyLine; end; procedure TMarkdownWriter.CollectSeeAlsoNodes(aList : TStringList; aSeeAlso: TDomNode); Var Node : TDOMNode; L : String; El : TDOMElement; begin Node:=aSeeAlso.FirstChild; While Assigned(Node) do begin if (Node.NodeType=ELEMENT_NODE) and (Node.NodeName='link') then begin El:=TDOMElement(Node); l:=UTF8encode(El['id']); aList.AddObject(L,El); end; Node:=Node.NextSibling; end; end; procedure TMarkdownWriter.CollectDeclarationTypes(aList : TStringList; aElement : TPasElement); Procedure MaybeAdd(aType : TPasType); Var N : TDocNode; begin if aType is TPasPointerType then aType:=TPasPointerType(aType).DestType; if (aType=Nil) or (aType.Name='') then exit; N:=Engine.FindDocNode(aType); if N<>Nil then aList.AddObject(aType.Name,aType); end; Var I : integer; begin if aElement is TPasVariable then MaybeAdd(TPasVariable(aElement).VarType) else if aElement is TPasMembersType then begin for I:=0 to TPasMembersType(aElement).Members.Count-1 do if TObject(TPasMembersType(aElement).Members[i]) is TPasVariable then MaybeAdd(TPasVariable(TPasMembersType(aElement).Members[i]).VarType); end; end; procedure TMarkdownWriter.AppendSeeAlsoSection(AElement: TPasElement; DocNode: TDocNode) ; Procedure AppendSeeALso(aID : String; El: TDomElement); Var S,N : DOMString; doBold : Boolean; begin s:= ResolveLinkID(aID); doBold:=Length(S)=0; if DoBold then begin if assigned(module) then s:=UTF8Decode(module.name) else s:='?'; if aID='' then aID:=''; if Assigned(AElement) then N:=UTF8Decode(AElement.Name) else N:='?'; DoLog(SErrUnknownLinkID, [s,N,aID]); end ; if doBold then DescrBeginBold else DescrBeginURL(S); if Not IsDescrNodeEmpty(El) then ConvertBaseShortList(AElement, El, True) else AppendText(aID); if doBold then DescrEndBold else DescrEndURL(); end; Procedure AppendTypeRef(aName : String; El: TPasType); begin AppendHyperLink(El); end; var I : Integer; aList : TStringList; DescrEl : TDomElement; begin if Not (Assigned(DocNode) and Assigned(DocNode.SeeAlso)) then Exit; AList:=TStringList.Create; Try aList.Duplicates:=dupIgnore; // AList will have a TDomElement (seealso) or a TPasType element as object CollectSeeAlsoNodes(aList,DocNode.SeeAlso); CollectDeclarationTypes(aList,aElement); if aList.Count = 0 then exit; aList.Sort; AppendHeader(2,SDocSeeAlso); AppendTableHeader([SDocName,SDocDescription]); For I:=0 to aList.Count-1 do begin DescrEl:=Nil; DescrBeginTableRow; DescrBeginTableCell; if aList.Objects[I] is TDomElement then AppendSeeAlso(aList[i],TDomElement(aList.Objects[i])) else if aList.Objects[i] is TPasType then AppendTypeRef(aList[i],TPasType(aList.Objects[i])); DescrEndTableCell; DescrBeginTableCell; DescrEl:=Engine.FindShortDescr(ModuleForElement(AElement),UTF8Encode(aList[i])); if Assigned(DescrEl) then ConvertShort(AElement, DescrEl) else AppendToLine(' ',False); DescrEndTableCell; DescrEndTableRow; end; DescrEndTable; Finally aList.Free; end; end; procedure TMarkdownWriter.AppendExampleSection ( AElement: TPasElement; DocNode: TDocNode ) ; var Node: TDOMNode; fn,s: String; f: Text; begin if not (Assigned(DocNode) and Assigned(DocNode.FirstExample)) then Exit; Node := DocNode.FirstExample; while Assigned(Node) do begin if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'example') then begin fn:=Engine.GetExampleFilename(TDOMElement(Node)); If (fn<>'') then begin AppendHeader(2, SDocExample); try Assign(f, FN); Reset(f); try DescrBeginCode(False, UTF8Encode(TDOMElement(Node)['highlighter'])); while not EOF(f) do begin ReadLn(f, s); DescrWriteCodeLine(s); end; finally Close(f); DescrEndCode; end; except on e: Exception do begin e.Message := '[example] ' + e.Message; raise; end; end; end; end; Node := Node.NextSibling; end; end; procedure TMarkdownWriter.AppendPageFooter; Var S : String; begin if Assigned(FFooterMarkDown) then begin EnsureEmptyLine; For S in FFooterMarkDown do AppendToLine(S,False) end; end; procedure TMarkdownWriter.FinishElementPage(AElement: TPasElement; SkipDescription : Boolean = false); var DocNode: TDocNode; begin DocNode := Engine.FindDocNode(AElement); If Not Assigned(DocNode) then exit; // Description if Assigned(DocNode.Descr) and not SkipDescription then AppendDescrSection(AElement, DocNode.Descr, UTF8Encode(SDocDescription)); // Append "Errors" section if Assigned(DocNode.ErrorsDoc) then AppendDescrSection(AElement, DocNode.ErrorsDoc, UTF8Encode(SDocErrors)); // Append Version info if Assigned(DocNode.Version) then AppendDescrSection(AElement, DocNode.Version, UTF8Encode(SDocVersion)); // Append "See also" section AppendSeeAlsoSection(AElement,DocNode); // Append examples, if present AppendExampleSection(AElement,DocNode); // Append notes, if present ConvertNotes(AElement,DocNode.Notes); end; procedure TMarkdownWriter.CreateTopicPageBody(AElement: TTopicElement); var DocNode: TDocNode; begin DocNode:=AElement.TopicNode; if not Assigned(DocNode) then // should always be true, but we're being careful. exit; AppendShortDescr(AElement, DocNode); if Assigned(DocNode.Descr) then AppendDescrSection(AElement, DocNode.Descr, ''); AppendSeeAlsoSection(AElement,DocNode); CreateTopicLinks(DocNode,AElement); AppendExampleSection(AElement,DocNode); ConvertNotes(AElement,DocNode.Notes); end; procedure TMarkdownWriter.CreateClassHierarchyPage(AddUnit : Boolean); type TypeEN = (NPackage, NModule, NName); Procedure AddClassList; begin DescrBeginUnorderedList; end; Procedure EndClassList; begin DescrEndUnorderedList; end; function ExtractName(APathName: String; Tp: TypeEN):String; var l:TStringList; begin Result:= Trim(APathName); if Result = '' then exit; l:=TStringList.Create; try l.AddDelimitedText(Result, '.', True); if l.Count=3 then Result:= l.Strings[Integer(Tp)] else Result:=''; finally l.free; end; end; Procedure AppendClass(EN : TPasElementNode); Var PE,PM : TPasElement; I : Integer; begin if not Assigned(EN) then exit; PE:=EN.Element; DescrBeginListItem; AppendHyperLink(PE); PM:=ModuleForElement(PE); if (PM<>Nil) then begin AppendText(' ('); AppendHyperLink(PM); AppendText(')'); end else AppendText(EN.Element.Name); if EN.ChildCount>0 then begin AddClassList; For I:=0 to EN.ChildCount-1 do AppendClass(EN.Children[i] as TPasElementNode); EndClassList; end; DescrEndListItem; end; begin AddClassList; AppendClass(TreeClass.RootNode); EndClassList; end; procedure TMarkdownWriter.CreatePackageClassHierarchy; Var S : String; begin S:=Package.Name; If Length(S)>0 then Delete(S,1,1); AppendTitle(SDocPackageClassHierarchy, [S]); CreateClassHierarchyPage(True); end; procedure TMarkdownWriter.CreatePageBody(AElement: TPasElement; ASubpageIndex: Integer); var i: Integer; Element: TPasElement; begin CurDirectory := Allocator.GetFilename(AElement, ASubpageIndex); i := Length(CurDirectory); while (i > 0) and not (CurDirectory[i] in AllowDirectorySeparators) do Dec(i); CurDirectory := Copy(CurDirectory, 1, i); BaseDirectory := Allocator.GetRelativePathToTop(AElement); if AElement.ClassType = TPasPackage then begin Module:=Nil; If (ASubPageIndex=0) then CreatePackagePageBody else if ASubPageIndex=IndexSubIndex then CreatePackageIndex else if ASubPageIndex=ClassHierarchySubIndex then CreatePackageClassHierarchy end else begin Element := AElement; while (Element<>Nil) and (not (Element.ClassType.inheritsfrom(TPasModule))) do Element := Element.Parent; Module := TPasModule(Element); if AElement.ClassType.inheritsfrom(TPasModule) then CreateModulePageBody(TPasModule(AElement), ASubpageIndex) else if AElement.Parent.InheritsFrom(TPasClassType) then CreateClassMemberPageBody(AElement) else if AElement.ClassType = TPasConst then CreateConstPageBody(TPasConst(AElement)) else if AElement.InheritsFrom(TPasClassType) then CreateClassPageBody(TPasClassType(AElement), ASubpageIndex) else if AElement.InheritsFrom(TPasType) then CreateTypePageBody(TPasType(AElement)) else if AElement.ClassType = TPasVariable then CreateVarPageBody(TPasVariable(AElement)) else if AElement.InheritsFrom(TPasProcedureBase) then CreateProcPageBody(TPasProcedureBase(AElement)) else if AElement.ClassType = TTopicELement then CreateTopicPageBody(TTopicElement(AElement)) else if AElement.ClassType = TPasProperty then CreateClassMemberPageBody(TPasProperty(AElement)) else writeln('Unknown classtype: ',AElement.classtype.classname); end; end; procedure TMarkdownWriter.CreateIndexPage(L : TStringList); Var Lists : Array['A'..'Z'] of TStringList; CL : TStringList; E : TPasElement; CCount,I,Rows,J,Index : Integer; S : String; C : Char; begin For C:='A' to 'Z' do Lists[C]:=Nil; L.Sort; Cl:=Nil; // Divide over alphabet For I:=0 to L.Count-1 do begin S:=L[i]; E:=TPasElement(L.Objects[i]); If not (E is TPasUnresolvedTypeRef) then begin If (S<>'') then begin C:=Upcase(S[1]); If C='_' then C:='A'; If (C in ['A'..'Z']) and (Lists[C]=Nil) then begin CL:=TStringList.Create; Lists[C]:=CL; end; end; if assigned(cl) then CL.AddObject(S,E); end; end; Try // Create a quick jump table to all available letters. CCount:=0; for C:='A' to 'Z' do If (Lists[C]<>Nil) then Inc(CCount); DescrBeginTable(CCount,False); DescrBeginTableHeadRow; for C:='A' to 'Z' do If (Lists[C]<>Nil) then begin DescrBeginTableCell; AppendLink(C,'#'+LowerCase(C)); DescrendTableCell; end; DescrEndTableHeadRow; // Now emit all identifiers. For C:='A' to 'Z' do begin CL:=Lists[C]; If CL<>Nil then begin AppendHeader(3,C); DescrBeginTable(IndexColCount,False); DescrBeginTableHeadRow; For I:=1 to IndexColCount do begin DescrBeginTableCell; AppendToLine('  ',False); DescrEndTableCell; end; DescrEndTableHeadRow; // Determine number of rows needed Rows:=(CL.Count div IndexColCount); If ((CL.Count Mod IndexColCount)<>0) then Inc(Rows); // Fill rows For I:=0 to Rows-1 do begin DescrBeginTableRow; For J:=0 to IndexColCount-1 do begin DescrBeginTableCell; Index:=(J*Rows)+I; If (Index0 then Delete(S,1,1); AppendTitle(SDocPackageIndex, [S]); CreateIndexPage(L); Finally L.Free; end; end; procedure TMarkdownWriter.CreatePackagePageBody; var DocNode: TDocNode; i: Integer; ThisModule: TPasModule; L : TStringList; begin AppendTitle(SDocPackageTitle, [Copy(Package.Name, 2, 256)]); AppendShortDescr(Package); AppendHeader(2,SDocUnits); L:=TStringList.Create; Try L.Sorted:=True; // Sort modules. For I:=0 to Package.Modules.Count-1 do L.AddObject(TPasModule(Package.Modules[i]).Name,TPasModule(Package.Modules[i])); AppendTableHeader([SDocUnits,SDocDescription]); // Now create table. for i:=0 to L.Count - 1 do begin ThisModule := TPasModule(L.Objects[i]); DescrBeginTableRow; DescrBeginTableCell; AppendHyperlink(ThisModule); DescrEndTableCell; AppendShortDescrCell(ThisModule); DescrEndTableRow; end; DescrEndTable; Finally L.Free; end; DocNode := Engine.FindDocNode(Package); if Assigned(DocNode) then begin if Assigned(DocNode.Descr) then AppendDescrSection(nil, DocNode.Descr, UTF8Decode(SDocDescription)); CreateTopicLinks(DocNode,Package); end; end; procedure TMarkdownWriter.CreateTopicLinks ( Node: TDocNode; PasElement: TPasElement ) ; var DocNode: TDocNode; First : Boolean; ThisTopic: TPasElement; begin DocNode:=Node.FirstChild; First:=True; While Assigned(DocNode) do begin If DocNode.TopicNode then begin if first then begin First:=False; AppendHeader(2,SDocRelatedTopics); AppendTableHeader([SDocTopic,SDocDescription]); end; ThisTopic:=FindTopicElement(DocNode); if Assigned(ThisTopic) then begin DescrBeginTableRow; DescrBeginTableCell; AppendHyperlink(ThisTopic); DescrEndTableCell; AppendShortDescrCell(ThisTopic); DescrEndTableRow; end; end; DocNode:=DocNode.NextSibling; end; if not First then DescrEndTable; end; function TMarkdownWriter.GetFileBaseDir(aOutput: String): String; begin Result:=inherited GetFileBaseDir(aOutput)+'docs'+pathdelim; end; procedure TMarkdownWriter.CreateModuleIndexPage(AModule: TPasModule); Var L : TStringList; begin L:=TStringList.Create; try AddModuleIdentifiers(AModule,L); AppendTitle(SDocModuleIndex, [AModule.Name]); CreateIndexPage(L); Finally L.Free; end; end; procedure TMarkdownWriter.CreateModuleMainPageBody(AModule: TPasModule); var i: Integer; UnitRef: TPasType; DocNode: TDocNode; begin AppendTitle(SDocUnitTitle, [AModule.Name]); AppendShortDescr(AModule); if AModule.InterfaceSection.UsesList.Count > 0 then begin AppendTableHeader(['Uses unit',SDocDescription]); for i := 0 to AModule.InterfaceSection.UsesList.Count - 1 do begin UnitRef := TPasType(AModule.InterfaceSection.UsesList[i]); DocNode := Engine.FindDocNode(UnitRef); if Assigned(DocNode) and DocNode.IsSkipped then continue; DescrBeginTableRow; DescrBeginTableCell; AppendHyperlink(UnitRef); DescrEndTableCell; AppendShortDescrCell(UnitRef); end; DescrEndTable; end; DocNode := Engine.FindDocNode(AModule); if Assigned(DocNode) then begin if Assigned(DocNode.Descr) then AppendDescrSection(AModule, DocNode.Descr, UTF8Decode(SDocOverview)); ConvertNotes(AModule,DocNode.Notes); CreateTopicLinks(DocNode,AModule); end; end; procedure TMarkdownWriter.CreateSimpleSubpage(aModule : TPasModule; const ATitle,aItem: String; AList: TFPList); var i: Integer; Decl: TPasElement; SortedList: TFPList; DocNode: TDocNode; begin AppendTitle(SDocUnitTitle + ': %s', [AModule.Name, aTitle]); SortedList := TFPList.Create; try for i := 0 to AList.Count - 1 do begin Decl := TPasElement(AList[i]); DocNode := Engine.FindDocNode(Decl); if not (Assigned(DocNode) and DocNode.IsSkipped) then SortedList.Add(Decl); end; SortedList.Sort(@SortPasElements); AppendTableHeader([aItem,SDocDescription]); for i := 0 to SortedList.Count - 1 do begin Decl:=TPasElement(SortedList[i]); DescrBeginTableRow; DescrBeginTableCell; AppendHyperlink(Decl); DescrEndTableCell; AppendShortDescrCell(Decl); DescrEndTableRow; end; DescrEndTable; finally SortedList.Free; end; end; procedure TMarkdownWriter.CreateResStringsPage(aModule : TPasModule); var i: Integer; Decl: TPasResString; begin AppendTitle(SDocUnitTitle + ': %s', [AModule.Name, SDocResStrings]); If AModule.InterfaceSection.ResStrings.Count = 0 then exit; for i := 0 to AModule.InterfaceSection.ResStrings.Count - 1 do begin Decl := TPasResString(AModule.InterfaceSection.ResStrings[i]); AppendToLine(' %s',[LowerCase(Decl.Name),Decl.Name]); Indent; UTF8Decode(Decl.Expr.getDeclaration(true)); undent; end; end; procedure TMarkdownWriter.CreateModulePageBody(AModule: TPasModule; ASubpageIndex: Integer); begin case ASubpageIndex of 0: CreateModuleMainPageBody(aModule); ResstrSubindex: CreateResStringsPage(aModule); ConstsSubindex: CreateSimpleSubpage(aModule,SDocConstants, SDocConstant, AModule.InterfaceSection.Consts); TypesSubindex: CreateSimpleSubpage(aModule,SDocTypes, SDocType, AModule.InterfaceSection.Types); ClassesSubindex: CreateSimpleSubpage(aModule,SDocClasses, SDocClass, AModule.InterfaceSection.Classes); ProcsSubindex: CreateSimpleSubpage(aModule,SDocProceduresAndFunctions, SDocProcedureOrFunction, AModule.InterfaceSection.Functions); VarsSubindex: CreateSimpleSubpage(aModule,SDocVariables, SDocVariable, AModule.InterfaceSection.Variables); IndexSubIndex: CreateModuleIndexPage(AModule); end; end; procedure TMarkdownWriter.CreateConstPageBody(AConst: TPasConst); begin AppendTitle(AConst.Name); AppendShortDescr(AConst); AppendHeader(2,SDocDeclaration); AppendSourceRef(AConst); DescrBeginCode(False,'Pascal'); EmitLine('const'); EmitLine(aConst.GetDeclaration(True)); DescrEndCode; FinishElementPage(AConst); end; procedure TMarkdownWriter.CreateTypePageBody(AType: TPasType); begin AppendTitle(AType.Name); AppendShortDescr(AType); AppendHeader(2,SDocDeclaration); AppendSourceRef(AType); DescrBeginCode(False,'Pascal'); EmitLine('Type'); EmitLine(aType.GetDeclaration(True)); DescrEndCode; FinishElementPage(AType); end; procedure TMarkdownWriter.CreateMemberDeclarations(AParent: TPasElement; Members: TFPList; Options: TMemberListOptions); function GetMemberType(aMember : TPasElement) : string; begin if aMember is TPasProcedure then Result:=SDocMethod else if aMember is TPasProperty then Result:=SDocProperty else if aMember is TPasConst then Result:=SDocConstant else if aMember is TPasType then Result:=SDocType else Result:=SDocField; end; var Member: TPasElement; MVisibility : TPasMemberVisibility; i,aCount: Integer; // isRecord, isOverLoad : Boolean; begin // isRecord:=AParent is TPasRecordType; if Members.Count = 0 then begin AppendText(SDocNoneAVailable); Exit; end; if mloAppendType in Options then AppendTableHeader([SDocMember,SDocType,SDocVisibility,SDocDescription]) else AppendTableHeader([SDocMember,SDocVisibility,SDocDescription]); aCount:=0; Members.Sort(@SortPasElements); for i := 0 to Members.Count - 1 do begin Member := TPasElement(Members[i]); MVisibility:=Member.Visibility; if mloCheckVisibility in Options then if not Engine.ShowElement(Member) then Continue; isOverLoad:=(Member is TPasOverloadedProc); if isOverload then Member:=TPasElement((Member as TPasOverloadedProc).Overloads[0]); Inc(aCount); DescrBeginTableRow; DescrBeginTableCell; AppendHyperlink(Member); if mloAppendParent in options then begin AppendText('('); AppendHyperLink(Member.Parent); AppendText(')'); end; DescrEndTableCell; if mloAppendType in Options then begin DescrBeginTableCell; AppendText(GetMemberType(Member)); DescrEndTableCell; end; DescrBeginTableCell; AppendText(VisibilityNames[MVisibility]); DescrEndTableCell; AppendShortDescrCell(member); DescrEndTableRow; end; DescrEndTable; if ACount=0 then AppendText(SDocNoneAVailable) end; procedure TMarkdownWriter.AppendTitle(const S: String); begin AddMetaData('title',S); AppendHeader(1,S); EnsureEmptyLine; end; procedure TMarkdownWriter.AppendTitle(const Fmt: String; const Args: array of const); begin AppendTitle(Format(Fmt,Args)); end; function TMarkdownWriter.GetClassDeclarationFirstLine(aEl: TPasClassType): String; Var aLine : string; I : Integer; begin aLine:=aEL.Name; if aEl.GenericTemplateTypes.Count>0 then begin aLine:='generic '+aLine+'<'; For I:=0 to aEl.GenericTemplateTypes.Count-1 do begin if I>0 then aLine:=aLine+', '; aLine:=aLine+TPasGenericTemplateType(aEl.GenericTemplateTypes[i]).Name; end; aLine:=aLine+'>'; end; aLine:=aLine+' = '+aEl.ElementTypeName; if aEl.HelperForType<>Nil then aLine:=aLine+' for '+aEl.HelperForType.Name; if aEL.ExternalName<>'' then aLine:=aLine+' external name '''+ael.ExternalName+''''; if Assigned(aEL.AncestorType) then begin aLine:=aLine+' ('+ael.AncestorType.Name; if Assigned(ael.Interfaces) and (aEl.Interfaces.Count>0) then For I:=0 to aEl.Interfaces.Count-1 do aLine:=aLine+', '+TPasElement(aEl.Interfaces[i]).Name; aLine:=aLine+')'; end; if Assigned(aEl.GUIDExpr) then aLine:=aLine+' ['+aEl.GUIDExpr.GetDeclaration(True)+']'; Result:=aLine; end; function TMarkdownWriter.GetClassDeclaration(aEl: TPasClassType): String; Var S,T : TStrings; I,J : Integer; LastVis : TPasMemberVisibility; aMember : TPasElement; begin T:=Nil; lastVis:=VisDefault; S:=TStringList.Create; try T:=TStringList.Create; S.Add(GetClassDeclarationFirstLine(aEl)); for I:=0 to aEl.Members.Count-1 do begin aMember:=TPasElement(aEl.Members[i]); if aMember.Visibility<>LastVis then begin LastVis:=aMember.Visibility; S.Add(VisibilityNames[LastVis]); end; T.Text:=GetDeclaration(aMember); for J:=0 to T.count-1 do S.Add(' '+T[J]); end; if not aEl.IsShortDefinition then S.Add('end'); Result:=S.Text; finally S.Free; T.Free; end; end; function TMarkdownWriter.GetDeclaration(aEl: TPasElement): String; Var I : Integer; Ovl : TPasOverloadedProc; S : String; begin if (aEl is TPasClassType) then exit(GetClassDeclaration(TPasClassType(aEl))+';'); if Not (aEl is TPasOverloadedProc) then Exit(aEl.GetDeclaration(True)+';'); ovl:=aEl as TPasOverloadedProc; S:=''; for I:=0 to ovl.Overloads.Count-1 do begin if s<>'' then S:=S+sLineBreak; S:=S+TPasElement(Ovl.Overloads[i]).GetDeclaration(True)+';'; end; Result:=S; end; procedure TMarkdownWriter.AppendDeclaration(aEl : TPasElement; aKind : string; PrependVisibility : Boolean); Var S : String; begin DescrBeginCode(False,'Pascal'); if PrependVisibility then S:=VisibilityNames[aEL.Visibility]+' ' else S:=''; S:=S+aKind; EmitLine(S); S:=GetDeclaration(aEl); EmitCode(S,2); DescrEndCode; end; procedure TMarkdownWriter.CreateClassMainPage(aClass : TPasClassType); procedure AppendMemberListLink(AListSubpageIndex: Integer; const AText: String); begin AppendToLine('\[',False); AppendLink(aText,FixHtmlPath(ResolveLinkWithinPackage(AClass, AListSubpageIndex))); AppendText(' ('); AppendLink(SDocByName,FixHtmlPath(ResolveLinkWithinPackage(AClass, AListSubpageIndex+1))); AppendToLine(')\]',False); end; var i: Integer; ThisInterface, ThisClass: TPasClassType; ThisTreeNode: TPasElementNode; DocNode: TDocNode; begin DocNode := Engine.FindDocNode(aClass); //WriteLn('@ClassPageBody.CreateMainPage Class=', AClass.Name); AppendTitle(AClass.Name); AppendMemberListLink(PropertiesByInheritanceSubindex,SDocProperties); AppendMemberListLink(MethodsByInheritanceSubindex, SDocMethods); AppendMemberListLink(EventsByInheritanceSubindex, SDocEvents); EnsureEmptyLine; AppendShortDescr(AClass); AppendHeader(2,SDocDeclaration); AppendSourceRef(AClass); AppendDeclaration(aClass,'Type',False); // Description if Assigned(DocNode) and Assigned(DocNode.Descr) then AppendDescrSection(aClass, DocNode.Descr, SDocDescription); AppendHeader(2,SDocMembers); CreateMemberDeclarations(aClass, AClass.Members,[mloAppendType]); AppendHeader(2,SDocInheritance); EnsureEmptyLine; AppendTableHeader([SDocClass,SDocDescription]); ThisClass := AClass; ThisTreeNode := Nil; if AClass.ObjKind = okInterface then ThisTreeNode := TreeInterface.GetPasElNode(AClass) else ThisTreeNode := TreeClass.GetPasElNode(AClass); while True do begin DescrBeginTableRow; DescrBeginTableCell; // Show class item if Assigned(ThisClass) Then AppendHyperlink(ThisClass); if Assigned(ThisClass) and (ThisClass.Interfaces.count>0) then begin AppendText('('); for i:=0 to ThisClass.interfaces.count-1 do begin ThisInterface:=TPasClassType(ThisClass.Interfaces[i]); if I>0 then AppendText(', '); AppendHyperlink( ThisInterface); end; AppendText(')'); end; DescrEndTableCell; AppendShortDescrCell(ThisClass); DescrEndTableRow; if Not Assigned(ThisTreeNode) then Break else if not Assigned(ThisTreeNode.ParentNode) then begin ThisClass := nil; ThisTreeNode:= nil; break; end else begin DescrBeginTableRow; DescrBeginTableCell; AppendText('|'); DescrEndTableCell; DescrBeginTableCell; DescrEndTableCell; ThisClass := ThisTreeNode.ParentNode.Element; ThisTreeNode := ThisTreeNode.ParentNode; end; end; DescrEndTable; FinishElementPage(AClass,True); end; procedure TMarkdownWriter.CreateInheritanceSubpage(aClass: TPasClassType; aTitle: string; AFilter: TMemberFilter); var ThisClass: TPasClassType; i,aCount: Integer; Member: TPasElement; aList : TFPList; begin aTitle:=aClass.Name+' : '+aTitle+ ' '+SDocByInheritance; AppendTitle(aTitle); ThisClass := AClass; aCount:=0; aList:=TFPList.Create; try while assigned(ThisClass) do begin aList.Clear; for i := 0 to ThisClass.Members.Count - 1 do begin Member := TPasElement(ThisClass.Members[i]); if (Engine.ShowElement(Member) and AFilter(Member)) then aList.Add(Member); end; aCount:=aCount+aList.Count; if AList.Count>0 then begin AppendHeader(2,CreateHyperLink(ThisClass),False); CreateMemberDeclarations(aClass, aList, []); end; if ThisClass.AncestorType is TPasClassType then ThisClass := TPasClassType(ThisClass.AncestorType) else ThisClass:=Nil; end; if aCount=0 then AppendText(SDocNoneAVailable); finally aList.Free; end; end; procedure TMarkdownWriter.CreateSortedSubpage(ACLass: TPasClassType; aTitle: string; AFilter: TMemberFilter); var List: TFPList; ThisClass: TPasClassType; i : Integer; Member: TPasElement; begin aTitle:=aClass.Name+' : '+aTitle+' '+SDocByName; AppendTitle(aTitle); List := TFPList.Create; try ThisClass := AClass; while Assigned(ThisClass) do begin for i := 0 to ThisClass.Members.Count - 1 do begin Member := TPasElement(ThisClass.Members[i]); if Engine.ShowElement(Member) and AFilter(Member) then List.Add(Member) end; if (ThisClass.AncestorType is TPasClassType) then ThisClass := TPasClassType(ThisClass.AncestorType) else ThisClass := Nil; end; CreateMemberDeclarations(aClass, List, [mloAppendParent]); finally List.Free; end; end; function TMarkdownWriter.GetAdditionalConfig: TStrings; begin if FAdditionalConfig=Nil then FAdditionalConfig:=TstringList.Create; Result:=FAdditionalConfig; end; procedure TMarkdownWriter.CreateClassPageBody(AClass: TPasClassType; ASubpageIndex: Integer); begin case ASubpageIndex of 0: CreateClassMainPage(aClass); PropertiesByInheritanceSubindex: CreateInheritanceSubpage(aClass,SDocPropertyOverview,@PropertyFilter); PropertiesByNameSubindex: CreateSortedSubpage(aClass,SDocPropertyOverview, @PropertyFilter); MethodsByInheritanceSubindex: CreateInheritanceSubpage(aClass,SDocMethodOverview,@MethodFilter); MethodsByNameSubindex: CreateSortedSubpage(aClass,SDocMethodOverview,@MethodFilter); EventsByInheritanceSubindex: CreateInheritanceSubpage(aClass,SDocEventOverview,@EventFilter); EventsByNameSubindex: CreateSortedSubpage(aClass,SDocEventOverview, @EventFilter); end; end; procedure TMarkdownWriter.CreateClassMemberPageBody(AElement: TPasElement); procedure CreateVarPage(Element: TPasVariable); begin AppendDeclaration(Element,'Var',True); end; procedure CreateTypePage(Element: TPasType); begin AppendDeclaration(Element,'Type',True); end; procedure CreateConstPage(Element: TPasConst); begin AppendDeclaration(Element,'Const',True); end; procedure CreatePropertyPage(Element: TPasProperty); begin AppendDeclaration(Element,'Property',True); end; var s: String; begin AppendTitle(aElement.FullName); AppendShortDescr(AElement); AppendHeader(2, SDocDeclaration); AppendSourceRef(AElement); if AElement is TPasProperty then S:='Property' else if AElement is TPasConst then S:='Const' else if (AElement is TPasVariable) then S:='var' else if AElement is TPasProcedureBase then s:='' else if AElement is TPasType then S:='Type' else AppendText('<' + AElement.ClassName + '>'); AppendDeclaration(aElement,S,True); FinishElementPage(AElement); end; procedure TMarkdownWriter.CreateVarPageBody(AVar: TPasVariable); begin AppendTitle(AVar.FullName); AppendShortDescr(AVar); AppendHeader(2, SDocDeclaration); AppendSourceRef(AVar); AppendDeclaration(aVar,'var',false); FinishElementPage(AVar); end; procedure TMarkdownWriter.CreateProcPageBody(AProc: TPasProcedureBase); begin AppendTitle(AProc.Name); AppendShortDescr(AProc); AppendHeader(2,SDocDeclaration); AppendSourceRef(AProc); AppendDeclaration(AProc,'',False); FinishElementPage(AProc); end; function TMarkdownWriter.InterPretOption ( const Cmd, Arg: String ) : boolean; procedure ReadFile(aStrings : TStrings; aFileName : string); begin aFileName:= SetDirSeparators(aFileName); if copy(aFileName,1,1)<>'@' then aStrings.text:=aFileName else begin Delete(aFileName,1,1); aStrings.LoadFromFile(aFileName); end; end; begin Result:=True; if Cmd = '--footer' then ReadFile(FooterMarkDown,Arg) else if Cmd = '--header' then ReadFile(HeaderMarkDown,Arg) else if Cmd = '--index-colcount' then IndexColCount := StrToIntDef(Arg,IndexColCount) else if Cmd = '--image-url' then FBaseImageURL := Arg else if Cmd = '--theme' then if arg='-' then Theme:='' else Theme := Arg end; class procedure TMarkdownWriter.Usage(List: TStrings); begin List.add('--header=file'); List.Add(SHTMLUsageHeader); List.add('--footer=file'); List.Add(SHTMLUsageFooter); List.Add('--index-colcount=N'); List.Add(SHTMLIndexColcount); List.Add('--image-url=url'); List.Add(SHTMLImageUrl); end; class procedure TMarkdownWriter.SplitImport(var AFilename, ALinkPrefix: String); var i: integer; begin i := Pos(',', AFilename); if i > 0 then begin //split into filename and prefix ALinkPrefix := Copy(AFilename,i+1,Length(AFilename)); SetLength(AFilename, i-1); end else if ALinkPrefix = '' then begin //synthesize outdir\pgk.xct, ..\pkg ALinkPrefix := '../' + ChangeFileExt(ExtractFileName(AFilename), ''); AFilename := ChangeFileExt(AFilename, '.xct'); end; end; class function TMarkdownWriter.FileNameExtension: String; begin result:='md'; end; // private methods function TMarkdownWriter.GetPageCount: Integer; begin Result := PageInfos.Count; end; procedure TMarkdownWriter.SetOnTest(const AValue: TNotifyEvent); begin if FOnTest=AValue then exit; FOnTest:=AValue; end; procedure TMarkdownWriter.AppendText(const S: String); begin AppendToLine(S,True); end; initialization // Do not localize. RegisterWriter(TMarkdownWriter,'md','Markdown output.'); finalization UnRegisterWriter('md'); end.