123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139 |
- {
- FPDoc - Free Pascal Documentation Tool
- Copyright (C) 2000 - 2003 by
- Areca Systems GmbH / Sebastian Guenther, [email protected]
- 2005-2012 by
- various FPC contributors
- * Output string definitions
- * Basic writer (output generator) class
- 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.
- }
- unit dWriter;
- {$MODE objfpc}
- {$H+}
- {$WARN 5024 off : Parameter "$1" not used}
- interface
- uses Classes, DOM, contnrs, dGlobals, PasTree, SysUtils, fpdocclasstree;
- type
- // Phony element for pas pages.
- TTopicElement = Class(TPaselement)
- TopicNode : TDocNode;
- Previous,
- Next : TPasElement;
- Subtopics : TList;
- Constructor Create(const AName: String; AParent: TPasElement); override;
- Destructor Destroy; override;
- end;
- { TFileAllocator }
- TFileAllocator = class
- private
- FSubPageNames: Boolean;
- protected
- function GetFilePostfix(ASubindex: Integer):String;
- public
- procedure Create(); overload;
- procedure AllocFilename(AElement: TPasElement; ASubindex: Integer); virtual;
- function GetFilename(AElement: TPasElement;
- ASubindex: Integer): String; virtual; abstract;
- function GetRelativePathToTop(AElement: TPasElement): String; virtual;
- function GetCSSFilename(ARelativeTo: TPasElement): DOMString; virtual;
- property SubPageNames: Boolean read FSubPageNames write FSubPageNames;
- end;
- TLongNameFileAllocator = class(TFileAllocator)
- private
- FExtension: String;
- public
- constructor Create(const AExtension: String);
- function GetFilename(AElement: TPasElement; ASubindex: Integer): String; override;
- function GetRelativePathToTop(AElement: TPasElement): String; override;
- property Extension: String read FExtension;
- end;
- TWriterLogEvent = Procedure(Sender : TObject; Const Msg : String) of object;
- TWriterNoteEvent = Procedure(Sender : TObject; Note : TDomElement; Var EmitNote : Boolean) of object;
-
- { TFPDocWriter }
- TFPDocWriter = class
- private
- FEmitNotes: Boolean;
- FEngine : TFPDocEngine;
- FPackage : TPasPackage;
- FContext : TPasElement;
- FTopics : TList;
- FImgExt : String;
- FBeforeEmitNote : TWriterNoteEvent;
- procedure ConvertURL(AContext: TPasElement; El: TDOMElement);
- procedure CreateClassTree;
- protected
- TreeClass: TClassTreeBuilder; // Global class tree
- TreeInterface: TClassTreeBuilder; // Global interface tree
- procedure AddElementsFromList(L: TStrings; List: TFPList; UsePathName : Boolean = False);
- Procedure DoLog(Const Msg : String);
- Procedure DoLog(Const Fmt : String; Args : Array of const);
- Procedure OutputResults(); virtual;
- procedure Warning(AContext: TPasElement; const AMsg: String);
- procedure Warning(AContext: TPasElement; const AMsg: String;
- const Args: array of const);
- // function FindShortDescr(const Name: String): TDOMElement;
- // Description conversion
- function IsDescrNodeEmpty(Node: TDOMNode): Boolean;
- function IsExtShort(Node: TDOMNode): Boolean;
- function ConvertShort(AContext: TPasElement; El: TDOMElement): Boolean;
- function ConvertNotes(AContext: TPasElement; El: TDOMElement): Boolean; virtual;
- function ConvertBaseShort(AContext: TPasElement; Node: TDOMNode): Boolean;
- procedure ConvertBaseShortList(AContext: TPasElement; Node: TDOMNode;
- MayBeEmpty: Boolean);
- procedure ConvertLink(AContext: TPasElement; El: TDOMElement);
- function ConvertExtShort(AContext: TPasElement; Node: TDOMNode): Boolean;
- procedure ConvertDescr(AContext: TPasElement; El: TDOMElement;
- AutoInsertBlock: Boolean);
- function ConvertNonSectionBlock(AContext: TPasElement;
- Node: TDOMNode): Boolean;
- procedure ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
- Node: TDOMNode);
- function ConvertSimpleBlock(AContext: TPasElement; Node: TDOMNode): Boolean;
- Function FindTopicElement(Node : TDocNode): TTopicElement;
- Procedure ConvertImage(El : TDomElement);
- Procedure DescrEmitNotesHeader(AContext : TPasElement); virtual;
- Procedure DescrEmitNotesFooter(AContext : TPasElement); virtual;
- procedure DescrWriteText(const AText: DOMString); virtual; abstract;
- procedure DescrBeginBold; virtual; abstract;
- procedure DescrEndBold; virtual; abstract;
- procedure DescrBeginItalic; virtual; abstract;
- procedure DescrEndItalic; virtual; abstract;
- procedure DescrBeginUnderline; virtual; abstract;
- procedure DescrEndUnderline; virtual; abstract;
- procedure DescrBeginEmph; virtual; abstract;
- procedure DescrEndEmph; virtual; abstract;
- procedure DescrWriteImageEl(const AFileName, ACaption,ALinkName : DOMString); virtual;
- procedure DescrWriteFileEl(const AText: DOMString); virtual; abstract;
- procedure DescrWriteKeywordEl(const AText: DOMString); virtual; abstract;
- procedure DescrWriteVarEl(const AText: DOMString); virtual; abstract;
- procedure DescrBeginLink(const AId: DOMString); virtual; abstract;
- procedure DescrEndLink; virtual; abstract;
- procedure DescrBeginURL(const AURL: DOMString); virtual; abstract;
- procedure DescrEndURL; virtual; abstract;
- procedure DescrWriteLinebreak; virtual; abstract;
- procedure DescrBeginParagraph; virtual; abstract;
- procedure DescrEndParagraph; virtual; abstract;
- procedure DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String); virtual; abstract;
- procedure DescrWriteCodeLine(const ALine: String); virtual; abstract;
- procedure DescrEndCode; virtual; abstract;
- procedure DescrBeginOrderedList; virtual; abstract;
- procedure DescrEndOrderedList; virtual; abstract;
- procedure DescrBeginUnorderedList; virtual; abstract;
- procedure DescrEndUnorderedList; virtual; abstract;
- procedure DescrBeginDefinitionList; virtual; abstract;
- procedure DescrEndDefinitionList; virtual; abstract;
- procedure DescrBeginListItem; virtual; abstract;
- procedure DescrEndListItem; virtual; abstract;
- procedure DescrBeginDefinitionTerm; virtual; abstract;
- procedure DescrEndDefinitionTerm; virtual; abstract;
- procedure DescrBeginDefinitionEntry; virtual; abstract;
- procedure DescrEndDefinitionEntry; virtual; abstract;
- procedure DescrBeginSectionTitle; virtual; abstract;
- procedure DescrBeginSectionBody; virtual; abstract;
- procedure DescrEndSection; virtual; abstract;
- procedure DescrBeginRemark; virtual; abstract;
- procedure DescrEndRemark; virtual; abstract;
- procedure DescrBeginTable(ColCount: Integer; HasBorder: Boolean); virtual; abstract;
- procedure DescrEndTable; virtual; abstract;
- procedure DescrBeginTableCaption; virtual; abstract;
- procedure DescrEndTableCaption; virtual; abstract;
- procedure DescrBeginTableHeadRow; virtual; abstract;
- procedure DescrEndTableHeadRow; virtual; abstract;
- procedure DescrBeginTableRow; virtual; abstract;
- procedure DescrEndTableRow; virtual; abstract;
- procedure DescrBeginTableCell; virtual; abstract;
- procedure DescrEndTableCell; virtual; abstract;
- procedure PrepareDocumentation; virtual;
- // Descendents must override this.
- procedure DoWriteDocumentation; virtual; Abstract;
- Property CurrentContext : TPasElement Read FContext ;
- public
- Constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); virtual;
- destructor Destroy; override;
- procedure AddModuleIdentifiers(AModule: TPasModule; L: TStrings);
- property Engine : TFPDocEngine read FEngine;
- Property Package : TPasPackage read FPackage;
- Property Topics : TList Read FTopics;
- Property ImageExtension : String Read FImgExt Write FImgExt;
- // Should return True if option was succesfully interpreted.
- Function InterpretOption(Const Cmd,Arg : String) : Boolean; Virtual;
- Class Function FileNameExtension : String; virtual;
- Class Procedure Usage(List : TStrings); virtual;
- Class procedure SplitImport(var AFilename, ALinkPrefix: String); virtual;
- // Here we start the generation of documentation
- procedure WriteDocumentation;
- Function WriteDescr(Element: TPasElement) : TDocNode;
- procedure WriteDescr(Element: TPasElement; DocNode: TDocNode);
- procedure WriteDescr(AContext: TPasElement; DescrNode: TDOMElement); virtual;
- Procedure FPDocError(Msg : String);
- Procedure FPDocError(Fmt : String; Args : Array of Const);
- Function ShowMember(M : TPasElement) : boolean;
- Procedure GetMethodList(ClassDecl: TPasClassType; List : TStringList);
- Property EmitNotes : Boolean Read FEmitNotes Write FEmitNotes;
- Property BeforeEmitNote : TWriterNoteEvent Read FBeforeEmitNote Write FBeforeEmitNote;
- end;
- const
- // The Multi-Page doc writer identifies each page by it's index.
- IdentifierIndex = 0;
- // Subpage indices for modules
- ResstrSubindex = 1;
- ConstsSubindex = 2;
- TypesSubindex = 3;
- ClassesSubindex = 4;
- ProcsSubindex = 5;
- VarsSubindex = 6;
- // Maybe needed later for topic overview ??
- TopicsSubIndex = 7;
- IndexSubIndex = 8;
- ClassHierarchySubIndex = 9;
- InterfaceHierarchySubIndex = 10;
- // Subpage indices for classes
- PropertiesByInheritanceSubindex = 11;
- PropertiesByNameSubindex = 12;
- MethodsByInheritanceSubindex = 13;
- MethodsByNameSubindex = 14;
- EventsByInheritanceSubindex = 15;
- EventsByNameSubindex = 16;
- Type
- { TMultiFileDocWriter }
- { TPageInfo }
- TPageInfo = class
- Public
- Element: TPasElement;
- SubpageIndex: Integer;
- Constructor Create(aElement : TPasElement; aIndex : Integer);
- end;
- { TLinkData }
- TLinkData = Class(TObject)
- FPathName,
- FLink,
- FModuleName : String;
- Constructor Create(Const APathName,ALink,AModuleName : string);
- end;
- TMultiFileDocWriter = Class(TFPDocWriter)
- Private
- FSubPageNames: Boolean;
- FBaseDirectory: String;
- FCurDirectory: String;
- FModule: TPasModule;
- FPageInfos: TFPObjectList; // list of TPageInfo objects
- FLinkUnresolvedCnt: Integer;
- FOutputPageNames: TStringList;
- function GetOutputPageNames: TStrings;
- function GetPageCount: Integer;
- function LinkFix(ALink:String):String;
- Protected
- FAllocator: TFileAllocator;
- Procedure LinkUnresolvedInc();
- // General resolving routine
- function ResolveLinkID(const Name: String): DOMString;
- // Simplified resolving routine. Excluded last path after dot
- function ResolveLinkIDUnStrict(const Name: String): DOMString;
- function ResolveLinkIDInUnit(const Name,AUnitName: String): DOMString;
- function ResolveLinkWithinPackage(AElement: TPasElement; ASubpageIndex: Integer): String;
- procedure PrepareDocumentation; override;
- function CreateAllocator() : TFileAllocator; virtual; abstract;
- Procedure OutputResults(); override;
- // aFileName is the filename allocated by the Allocator, nothing prefixed.
- procedure WriteDocPage(const aFileName: String; aElement: TPasElement; aSubPageIndex: Integer); virtual; abstract;
- procedure AllocatePages; virtual;
- // Default page allocation mechanism.
- function AddPage(AElement: TPasElement; ASubpageIndex: Integer): TPageInfo; virtual;
- procedure AddPages(AElement: TPasElement; ASubpageIndex: Integer; AList: TFPList); virtual;
- procedure AddTopicPages(AElement: TPasElement); virtual;
- procedure AllocateClassMemberPages(AModule: TPasModule; LinkList: TObjectList); virtual;
- procedure AllocateModulePages(AModule: TPasModule; LinkList: TObjectList); virtual;
- procedure AllocatePackagePages; virtual;
- // Prefix every filename generated with the result of this.
- function GetFileBaseDir(aOutput: String): String; virtual;
- function InterPretOption(const Cmd, Arg: String): boolean; override;
- function ModuleHasClasses(AModule: TPasModule): Boolean;
- // Allocate pages etc.
- Procedure DoWriteDocumentation; override;
- Function MustGeneratePage(aFileName : String) : Boolean; virtual;
- Property PageInfos : TFPObjectList Read FPageInfos;
- Property SubPageNames: Boolean Read FSubPageNames;
- Public
- constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); override;
- Destructor Destroy; override;
- class procedure Usage(List: TStrings); override;
- property PageCount: Integer read GetPageCount;
- Property Allocator : TFileAllocator Read FAllocator;
- Property Module: TPasModule Read FModule Write FModule;
- Property CurDirectory: String Read FCurDirectory Write FCurDirectory; // relative to curdir of process
- property BaseDirectory: String read FBaseDirectory Write FBaseDirectory; // relative path to package base directory
- Property OutputPageNames : TStrings Read GetOutputPageNames;
- end;
- TFPDocWriterClass = Class of TFPDocWriter;
- EFPDocWriterError = Class(Exception);
- // Member Filter Callback type
- TMemberFilter = function(AMember: TPasElement): Boolean;
- // Filter Callbacks
- function PropertyFilter(AMember: TPasElement): Boolean;
- function MethodFilter(AMember: TPasElement): Boolean;
- function EventFilter(AMember: TPasElement): Boolean;
- // Register backend
- Procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName,ADescr : String);
- // UnRegister backend
- Procedure UnRegisterWriter(Const AName : String);
- // Return back end class. Exception if not found.
- Function GetWriterClass(AName : String) : TFPDocWriterClass;
- // Return index of back end class.
- Function FindWriterClass(AName : String) : Integer;
- // List of backend in name=descr form.
- Procedure EnumWriters(List : TStrings);
- // Sort elements on name
- function SortPasElements(Item1, Item2: Pointer): Integer;
- implementation
- uses strutils, fpdocstrs;
- function SortPasElements(Item1, Item2: Pointer): Integer;
- begin
- Result:=CompareText(TPasElement(Item1).Name,TPasElement(Item2).Name)
- end;
- { ---------------------------------------------------------------------
- Filter callbacks
- ---------------------------------------------------------------------}
- function PropertyFilter(AMember: TPasElement): Boolean;
- begin
- Result := (AMember.ClassType = TPasProperty) and
- (Copy(AMember.Name, 1, 2) <> 'On');
- end;
- function MethodFilter(AMember: TPasElement): Boolean;
- begin
- Result := AMember.InheritsFrom(TPasProcedureBase);
- // Writeln(aMember.Name,' (',aMember.ClassName,') is Method ',Result);
- end;
- function EventFilter(AMember: TPasElement): Boolean;
- begin
- Result := (AMember.ClassType = TPasProperty) and
- (Copy(AMember.Name, 1, 2) = 'On');
- end;
- { ---------------------------------------------------------------------
- Writer registration
- ---------------------------------------------------------------------}
- Type
- { TWriterRecord }
- TWriterRecord = Class(TObject)
- Private
- FClass : TFPDocWriterClass;
- FName : String;
- FDescription : String;
- Public
- Constructor Create (AClass : TFPDocWriterClass; Const AName,ADescr : String);
- end;
- { TPageInfo }
- constructor TPageInfo.Create(aElement: TPasElement; aIndex: Integer);
- begin
- Element:=aELement;
- SubpageIndex:=aIndex;
- end;
- { TLinkData }
- constructor TLinkData.Create(Const APathName, ALink, AModuleName: string);
- begin
- FPathName:=APathName;
- FLink:=ALink;
- FModuleName:=AModuleName;
- end;
- { TMultiFileDocWriter }
- constructor TMultiFileDocWriter.Create(APackage: TPasPackage;
- AEngine: TFPDocEngine);
- begin
- inherited Create(APackage, AEngine);
- FPageInfos:=TFPObjectList.Create;
- FSubPageNames:= False;
- FLinkUnresolvedCnt:=0;
- end;
- destructor TMultiFileDocWriter.Destroy;
- begin
- FreeAndNil(FPageInfos);
- FreeAndNil(FAllocator);
- inherited Destroy;
- end;
- function TMultiFileDocWriter.GetPageCount: Integer;
- begin
- Result := PageInfos.Count;
- end;
- function TMultiFileDocWriter.GetOutputPageNames: TStrings;
- begin
- If (FoutputPageNames=Nil) then
- begin
- FOutputPageNames:=TStringList.Create;
- FOutputPageNames.Sorted:=True;
- end;
- Result:=FOutputPageNames;
- end;
- procedure TMultiFileDocWriter.OutputResults();
- begin
- DoLog('Unresolved links: %d', [FLinkUnresolvedCnt]);
- inherited OutputResults();
- end;
- procedure TMultiFileDocWriter.LinkUnresolvedInc();
- begin
- Inc(FLinkUnresolvedCnt);
- end;
- function TMultiFileDocWriter.ResolveLinkID(const Name: String): DOMString;
- var
- res: String;
- begin
- res:=Engine.ResolveLink(Module,Name, True);
- // engine can return backslashes on Windows
- res:= LinkFix(res);
- Result:=UTF8Decode(res);
- end;
- function TMultiFileDocWriter.ResolveLinkIDUnStrict(const Name: String
- ): DOMString;
- var
- idDot, idLast: Integer;
- res: String;
- begin
- res:=Engine.ResolveLink(Module,Name, True);
- if res = '' then
- begin
- // do simplify on one level from end.
- // TOCO: I want to move that code to last check of Engine.ResolveLink() for not Strict
- IdDot:= Pos('.', Name);
- IdLast:= 0;
- // search last dot
- while idDot > 0 do
- begin
- IdLast:= idDot;
- IdDot:= Pos('.', Name, IdLast+1);
- end;
- if idLast > 0 then
- // have cut last element
- res:= Engine.ResolveLink(Module, Copy(Name, 1, IdLast-1), True);
- end;
- res:= LinkFix(res);
- Result:=UTF8Decode(res);
- end;
- function TMultiFileDocWriter.LinkFix(ALink: String): String;
- var
- res, s:String;
- begin
- res:= ALink;
- if Length(res) > 0 then
- begin
- // If the link is in the same directory as current dir, then remove the directory part.
- s:=Copy(res, 1, Length(CurDirectory) + 1);
- if (S= CurDirectory + '/') or (s= CurDirectory + '\') then
- res := Copy(res, Length(CurDirectory) + 2, Length(res))
- else if not IsLinkAbsolute(res) then
- res := BaseDirectory + res;
- end;
- Result:= res;
- end;
- { Used for:
- - <link> elements in descriptions
- - "see also" entries
- - AppendHyperlink (for unresolved parse tree element links)
- }
- function TMultiFileDocWriter.ResolveLinkIDInUnit(const Name,AUnitName: String): DOMString;
- begin
- Result:=ResolveLinkID(Name);
- If (Result='') and (AUnitName<>'') and (length(Name)>0) and (Name[1]<>'#') then
- Result:=ResolveLinkID(AUnitName+'.'+Name);
- end;
- function TMultiFileDocWriter.ResolveLinkWithinPackage(AElement: TPasElement;
- ASubpageIndex: Integer): String;
- var
- ParentEl: TPasElement;
- s : String;
- begin
- ParentEl := AElement;
- while Assigned(ParentEl) and not (ParentEl.ClassType = TPasPackage) do
- ParentEl := ParentEl.Parent;
- if Assigned(ParentEl) and (TPasPackage(ParentEl) = Engine.Package) then
- begin
- Result := Allocator.GetFilename(AElement, ASubpageIndex);
- // engine/allocator can return backslashes on Windows
- s:=Copy(Result, 1, Length(CurDirectory) + 1);
- if (S= CurDirectory + '/') or (s= CurDirectory + '\') then
- Result := Copy(Result, Length(CurDirectory) + 2, Length(Result))
- else
- Result := BaseDirectory + Result;
- end else
- SetLength(Result, 0);
- end;
- procedure TMultiFileDocWriter.PrepareDocumentation;
- begin
- inherited PrepareDocumentation;
- FAllocator:= CreateAllocator();
- FAllocator.SubPageNames:= SubPageNames;
- end;
- function TMultiFileDocWriter.AddPage(AElement: TPasElement;
- ASubpageIndex: Integer): TPageInfo;
- begin
- Result:= TPageInfo.Create(aElement,aSubPageIndex);
- PageInfos.Add(Result);
- Allocator.AllocFilename(AElement, ASubpageIndex);
- if ASubpageIndex = 0 then
- Engine.AddLink(AElement.PathName,Allocator.GetFilename(AElement, ASubpageIndex));
- end;
- procedure TMultiFileDocWriter.AddTopicPages(AElement: TPasElement);
- var
- PreviousTopic,
- TopicElement : TTopicElement;
- DocNode,
- TopicNode : TDocNode;
- begin
- DocNode:=Engine.FindDocNode(AElement);
- If not Assigned(DocNode) then
- exit;
- TopicNode:=DocNode.FirstChild;
- PreviousTopic:=Nil;
- While Assigned(TopicNode) do
- begin
- If TopicNode.TopicNode then
- begin
- TopicElement:=TTopicElement.Create(TopicNode.Name,AElement);
- Topics.Add(TopicElement);
- TopicElement.TopicNode:=TopicNode;
- TopicElement.Previous:=PreviousTopic;
- If Assigned(PreviousTopic) then
- PreviousTopic.Next:=TopicElement;
- PreviousTopic:=TopicElement;
- if AElement is TTopicElement then
- TTopicElement(AElement).SubTopics.Add(TopicElement);
- AddPage(TopicElement,IdentifierIndex);
- if AElement is TTopicElement then
- TTopicElement(AElement).SubTopics.Add(TopicElement)
- else // Only one level of recursion.
- AddTopicPages(TopicElement);
- end;
- TopicNode:=TopicNode.NextSibling;
- end;
- end;
- function TMultiFileDocWriter.ModuleHasClasses(AModule: TPasModule): Boolean;
- begin
- result:=assigned(AModule)
- and assigned(AModule.InterfaceSection)
- and assigned(AModule.InterfaceSection.Classes)
- and (AModule.InterfaceSection.Classes.Count>0);
- end;
- procedure TMultiFileDocWriter.AddPages(AElement: TPasElement; ASubpageIndex: Integer;
- AList: TFPList);
- var
- i,j: Integer;
- R : TPasRecordtype;
- FPEl : TPasElement;
- DocNode: TDocNode;
- begin
- if AList.Count > 0 then
- begin
- AddPage(AElement, ASubpageIndex);
- for i := 0 to AList.Count - 1 do
- begin
- AddPage(TPasElement(AList[i]), 0);
- if (TObject(AList[i]) is TPasRecordType) then
- begin
- R:=TObject(AList[I]) as TPasRecordType;
- For J:=0 to R.Members.Count-1 do
- begin
- FPEl:=TPasElement(R.Members[J]);
- if ((FPEL is TPasProperty) or (FPEL is TPasProcedureBase))
- and Engine.ShowElement(FPEl) then
- begin
- DocNode := Engine.FindDocNode(FPEl);
- if Assigned(DocNode) then
- AddPage(FPEl, 0);
- end;
- end;
- end;
- end;
- end;
- end;
- procedure TMultiFileDocWriter.AllocateClassMemberPages(AModule: TPasModule;
- LinkList: TObjectList);
- var
- i, j, k: Integer;
- ClassEl: TPasClassType;
- FPEl, AncestorMemberEl: TPasElement;
- DocNode: TDocNode;
- ALink : DOMString;
- DidAutolink: Boolean;
- begin
- for i := 0 to AModule.InterfaceSection.Classes.Count - 1 do
- begin
- ClassEl := TPasClassType(AModule.InterfaceSection.Classes[i]);
- AddPage(ClassEl, 0);
- // !!!: Only add when there are items
- AddPage(ClassEl, PropertiesByInheritanceSubindex);
- AddPage(ClassEl, PropertiesByNameSubindex);
- AddPage(ClassEl, MethodsByInheritanceSubindex);
- AddPage(ClassEl, MethodsByNameSubindex);
- AddPage(ClassEl, EventsByInheritanceSubindex);
- AddPage(ClassEl, EventsByNameSubindex);
- for j := 0 to ClassEl.Members.Count - 1 do
- begin
- FPEl := TPasElement(ClassEl.Members[j]);
- if Not Engine.ShowElement(FPEl) then
- continue;
- DocNode := Engine.FindDocNode(FPEl);
- if Assigned(DocNode) then
- begin
- if Assigned(DocNode.Node) then
- ALink:=DocNode.Node['link']
- else
- ALink:='';
- If (ALink<>'') then
- LinkList.Add(TLinkData.Create(FPEl.PathName,UTF8Encode(ALink),AModule.name))
- else
- AddPage(FPEl, 0);
- end
- else
- begin
- DidAutolink := False;
- if Assigned(ClassEl.AncestorType) and
- (ClassEl.AncestorType.ClassType.inheritsfrom(TPasClassType)) then
- begin
- for k := 0 to TPasClassType(ClassEl.AncestorType).Members.Count - 1 do
- begin
- AncestorMemberEl :=
- TPasElement(TPasClassType(ClassEl.AncestorType).Members[k]);
- if AncestorMemberEl.Name = FPEl.Name then
- begin
- DocNode := Engine.FindDocNode(AncestorMemberEl);
- if Assigned(DocNode) then
- begin
- DidAutolink := True;
- Engine.AddLink(FPEl.PathName,
- Engine.FindAbsoluteLink(AncestorMemberEl.PathName));
- break;
- end;
- end;
- end;
- end;
- if not DidAutolink then
- AddPage(FPEl, 0);
- end;
- end;
- end;
- end;
- procedure TMultiFileDocWriter.AllocateModulePages(AModule: TPasModule; LinkList : TObjectList);
- var
- i: Integer;
- s: String;
- begin
- if not assigned(Amodule.Interfacesection) then
- exit;
- AddPage(AModule, 0);
- AddPage(AModule,IndexSubIndex);
- AddTopicPages(AModule);
- with AModule do
- begin
- if InterfaceSection.ResStrings.Count > 0 then
- begin
- AddPage(AModule, ResstrSubindex);
- s := Allocator.GetFilename(AModule, ResstrSubindex);
- for i := 0 to InterfaceSection.ResStrings.Count - 1 do
- with TPasResString(InterfaceSection.ResStrings[i]) do
- Engine.AddLink(PathName, s + '#' + LowerCase(Name));
- end;
- AddPages(AModule, ConstsSubindex, InterfaceSection.Consts);
- AddPages(AModule, TypesSubindex, InterfaceSection.Types);
- if InterfaceSection.Classes.Count > 0 then
- begin
- AddPage(AModule, ClassesSubindex);
- AllocateClassMemberPages(AModule,LinkList);
- end;
- AddPages(AModule, ProcsSubindex, InterfaceSection.Functions);
- AddPages(AModule, VarsSubindex, InterfaceSection.Variables);
- end;
- end;
-
- procedure TMultiFileDocWriter.AllocatePackagePages;
- Var
- I : Integer;
- H : Boolean;
- begin
- if Length(Package.Name) <= 1 then
- exit;
- AddPage(Package, 0);
- AddPage(Package,IndexSubIndex);
- I:=0;
- H:=False;
- While (I<Package.Modules.Count) and Not H do
- begin
- H:=ModuleHasClasses(TPasModule(Package.Modules[i]));
- Inc(I);
- end;
- if H then
- AddPage(Package,ClassHierarchySubIndex);
- AddTopicPages(Package);
- end;
- procedure TMultiFileDocWriter.AllocatePages;
- Var
- L : TObjectList;
- ML : TFPList;
- I : Integer;
- begin
- // Allocate page for the package itself, if a name is given (i.e. <> '#')
- AllocatePackagePages;
- ML:=Nil;
- L:=TObjectList.Create;
- try
- ML:=TFPList.Create;
- ML.AddList(Package.Modules);
- ML.Sort(@SortPasElements);
- for i := 0 to ML.Count - 1 do
- AllocateModulePages(TPasModule(ML[i]),L);
- // Resolve links
- For I:=0 to L.Count-1 do
- With TLinkData(L[i]) do
- Engine.AddLink(FPathName,UTF8Encode(ResolveLinkIDInUnit(FLink,FModuleName)));
- finally
- L.Free;
- ML.Free;
- end;
- end;
- function TMultiFileDocWriter.GetFileBaseDir(aOutput: String) : String;
- begin
- Result:=aOutput;
- if Result<>'' then
- Result:=IncludeTrailingPathDelimiter(Result);
- end;
- procedure TMultiFileDocWriter.DoWriteDocumentation;
- procedure CreatePath(const AFilename: String);
- var
- EndIndex: Integer;
- Path: String;
- begin
- EndIndex := Length(AFilename);
- if EndIndex = 0 then
- exit;
- while not (AFilename[EndIndex] in AllowDirectorySeparators) do
- begin
- Dec(EndIndex);
- if EndIndex = 0 then
- exit;
- end;
- Path := Copy(AFilename, 1, EndIndex - 1);
- if not DirectoryExists(Path) then
- begin
- CreatePath(Path);
- MkDir(Path);
- end;
- end;
- var
- i: Integer;
- FileName : String;
- FinalFilename: String;
- begin
- AllocatePages;
- DoLog(SWritingPages, [PageCount]);
- if Engine.Output <> '' then
- Engine.Output := IncludeTrailingBackSlash(Engine.Output);
- for i := 0 to PageInfos.Count - 1 do
- with TPageInfo(PageInfos[i]) do
- begin
- FileName:= Allocator.GetFilename(Element, SubpageIndex);
- if MustGeneratePage(FileName) then
- begin
- FinalFilename := GetFileBaseDir(Engine.Output) + FileName;
- CreatePath(FinalFilename);
- WriteDocPage(FileName,ELement,SubPageIndex);
- end;
- end;
- end;
- function TMultiFileDocWriter.MustGeneratePage(aFileName: String): Boolean;
- begin
- Result:=Not Assigned(FOutputPageNames);
- if Not Result then
- Result:=FOutputPageNames.IndexOf(aFileName)<>-1;
- end;
- class procedure TMultiFileDocWriter.Usage(List: TStrings);
- begin
- List.AddStrings(['--use-subpagenames', SUsageSubNames]);
- List.AddStrings(['--only-pages=LIST', SUsageOnlyPages]);
- end;
- function TMultiFileDocWriter.InterPretOption(const Cmd, Arg: String): boolean;
- Var
- I : Integer;
- FN : String;
- begin
- Writeln('Cmd : ',Cmd);
- Result := True;
- if Cmd = '--use-subpagenames' then
- FSubPageNames:= True
- else
- if Cmd = '--only-pages' then
- begin
- Result:=Arg<>'';
- if Result then
- begin
- if Arg[1]='@' then
- begin
- FN:=Copy(Arg,2,Length(Arg)-1);
- OutputPageNames.LoadFromFile(FN);
- end
- else
- begin
- For I:=1 to WordCount(Arg,[',']) do
- OutputPageNames.Add(ExtractWord(I,Arg,[',']));
- end;
- Writeln('OutputPagenames ',OutputPagenames.CommaText);
- end
- end
- else
- Result:=inherited InterPretOption(Cmd, Arg);
- end;
- { TWriterRecord }
- constructor TWriterRecord.Create(AClass: TFPDocWriterClass; const AName,
- ADescr: String);
- begin
- FClass:=AClass;
- FName:=AName;
- FDescription:=ADescr;
- end;
- Var
- Writers : TStringList;
- Procedure InitWriterList;
- begin
- Writers:=TStringList.Create;
- Writers.Sorted:=True;
- end;
- Procedure DoneWriterList;
- Var
- I : Integer;
- begin
- For I:=Writers.Count-1 downto 0 do
- Writers.Objects[i].Free;
- FreeAndNil(Writers);
- end;
- procedure RegisterWriter(AClass : TFPDocWriterClass; Const AName, ADescr : String);
- begin
- If Writers.IndexOf(AName)<>-1 then
- Raise EFPDocWriterError.CreateFmt(SErralreadyRegistered,[ANAme]);
- Writers.AddObject(AName,TWriterRecord.Create(AClass,AName,ADescr));
- end;
- function FindWriterClass(AName : String) : Integer;
- begin
- Result:=Writers.IndexOf(AName);
- end;
- function GetWriterClass(AName : String) : TFPDocWriterClass;
- Var
- Index : Integer;
- begin
- Index:=FindWriterClass(AName);
- If Index=-1 then
- Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
- Result:=(Writers.Objects[Index] as TWriterRecord).FClass;
- end;
- // UnRegister backend
- Procedure UnRegisterWriter(Const AName : String);
- Var
- Index : Integer;
- begin
- Index:=Writers.IndexOf(AName);
- If Index=-1 then
- Raise EFPDocWriterError.CreateFmt(SErrUnknownWriterClass,[ANAme]);
- Writers.Objects[Index].Free;
- Writers.Delete(Index);
- end;
- Procedure EnumWriters(List : TStrings);
- Var
- I : Integer;
- begin
- List.Clear;
- For I:=0 to Writers.Count-1 do
- With (Writers.Objects[I] as TWriterRecord) do
- List.Add(FName+'='+FDescription);
- end;
- function IsWhitespaceNode(Node: TDOMText): Boolean;
- var
- I,L: Integer;
- S: DOMString;
- P : PWideChar;
-
- begin
- S := Node.Data;
- Result := True;
- I:=0;
- L:=Length(S);
- P:=PWideChar(S);
- While Result and (I<L) do
- begin
- Result:=P^ in [#32,#10,#9,#13];
- Inc(P);
- Inc(I);
- end;
- end;
- { ---------------------------------------------------------------------
- TFileAllocator
- ---------------------------------------------------------------------}
- function TFileAllocator.GetFilePostfix(ASubindex: Integer): String;
- begin
- if FSubPageNames then
- case ASubindex of
- IdentifierIndex: Result:='';
- ResstrSubindex: Result:='reestr';
- ConstsSubindex: Result:='consts';
- TypesSubindex: Result:='types';
- ClassesSubindex: Result:='classes';
- ProcsSubindex: Result:='procs';
- VarsSubindex: Result:='vars';
- TopicsSubIndex: Result:='topics';
- IndexSubIndex: Result:='indexes';
- ClassHierarchySubIndex: Result:='class-tree';
- InterfaceHierarchySubIndex: Result:='interface-tree';
- PropertiesByInheritanceSubindex: Result:='props';
- PropertiesByNameSubindex: Result:='props-n';
- MethodsByInheritanceSubindex: Result:='methods';
- MethodsByNameSubindex: Result:='methods-n';
- EventsByInheritanceSubindex: Result:='events';
- EventsByNameSubindex: Result:='events-n';
- end
- else
- Result:= IntToStr(ASubindex);
- end;
- procedure TFileAllocator.Create();
- begin
- FSubPageNames:= False;
- end;
- procedure TFileAllocator.AllocFilename(AElement: TPasElement;
- ASubindex: Integer);
- begin
- end;
- function TFileAllocator.GetRelativePathToTop(AElement: TPasElement): String;
- begin
- Result:='';
- end;
- function TFileAllocator.GetCSSFilename(ARelativeTo: TPasElement): DOMString;
- begin
- Result := Utf8Decode(GetRelativePathToTop(ARelativeTo)) + 'fpdoc.css';
- end;
- { ---------------------------------------------------------------------
- TLongNameFileAllocator
- ---------------------------------------------------------------------}
- constructor TLongNameFileAllocator.Create(const AExtension: String);
- begin
- inherited Create;
- FExtension := AExtension;
- end;
- function TLongNameFileAllocator.GetFilename(AElement: TPasElement; ASubindex: Integer): String;
- var
- n,s: String;
- i: Integer;
- MElement: TPasElement;
- begin
- Result:='';
- if AElement.ClassType = TPasPackage then
- Result := 'index'
- else if AElement.ClassType = TPasModule then
- Result := LowerCase(AElement.Name) + PathDelim + 'index'
- else
- begin
- if AElement is TPasOperator then
- begin
- if Assigned(AElement.Parent) then
- result:=LowerCase(AElement.Parent.PathName);
- With TPasOperator(aElement) do
- Result:= Result + 'op-'+OperatorTypeToOperatorName(OperatorType);
- s := '';
- N:=LowerCase(aElement.Name); // Should not contain any weird chars.
- Delete(N,1,Pos('(',N));
- i := 1;
- Repeat
- I:=Pos(',',N);
- if I=0 then
- I:=Pos(')',N);
- if I>1 then
- begin
- if (S<>'') then
- S:=S+'-';
- S:=S+Copy(N,1,I-1);
- end;
- Delete(N,1,I);
- until I=0;
- // First char is maybe :
- if (N<>'') and (N[1]=':') then
- Delete(N,1,1);
- Result:=Result + '-'+ s + '-' + N;
- end else
- Result := LowerCase(AElement.PathName);
- // cut off Package Name
- MElement:= AElement.GetModule;
- if Assigned(MElement) then
- AElement:= MElement;
- Result := Copy(Result, Length(AElement.Parent.Name) + 2, MaxInt);
- // to skip dots in unit name
- i := Length(AElement.Name);
- while (i <= Length(Result)) and (Result[i] <> '.') do
- Inc(i);
- if (i <= Length(Result)) and (i > 0) then
- Result[i] := PathDelim;
- end;
- if ASubindex > 0 then
- Result := Result + '-' + GetFilePostfix(ASubindex);
- Result := Result + Extension;
- end;
- function TLongNameFileAllocator.GetRelativePathToTop(AElement: TPasElement): String;
- begin
- if (AElement.ClassType=TPasPackage) then
- Result := ''
- else if (AElement.ClassType=TTopicElement) then
- begin
- If (AElement.Parent.ClassType=TTopicElement) then
- Result:='../'+GetRelativePathToTop(AElement.Parent)
- else if (AElement.Parent.ClassType=TPasPackage) then
- Result:=''
- else if (AElement.Parent.ClassType=TPasModule) then
- Result:='../';
- end
- else
- Result := '../';
- end;
- { ---------------------------------------------------------------------
- TFPDocWriter
- ---------------------------------------------------------------------}
- {
- fmtIPF:
- begin
- if Length(Engine.Output) = 0 then
- WriteLn(SCmdLineOutputOptionMissing)
- else
- CreateIPFDocForPackage(Engine.Package, Engine);
- end;
- }
- constructor TFPDocWriter.Create ( APackage: TPasPackage; AEngine: TFPDocEngine
- ) ;
- begin
- inherited Create;
- FEngine := AEngine;
- FPackage := APackage;
- FTopics:=Tlist.Create;
- FImgExt:='.png';
- TreeClass:= TClassTreeBuilder.Create(FEngine, FPackage, okWithFields);
- TreeInterface:= TClassTreeBuilder.Create(FEngine, FPackage, [okInterface]);
- CreateClassTree;
- end;
- destructor TFPDocWriter.Destroy;
- Var
- i : integer;
- begin
- For I:=0 to FTopics.Count-1 do
- TTopicElement(FTopics[i]).Free;
- FTopics.Free;
- TreeClass.free;
- TreeInterface.Free;
- Inherited;
- end;
- procedure TFPDocWriter.AddModuleIdentifiers(AModule : TPasModule; L : TStrings);
- begin
- if assigned(AModule.InterfaceSection) Then
- begin
- AddElementsFromList(L,AModule.InterfaceSection.Consts);
- AddElementsFromList(L,AModule.InterfaceSection.Types);
- AddElementsFromList(L,AModule.InterfaceSection.Functions);
- AddElementsFromList(L,AModule.InterfaceSection.Classes);
- AddElementsFromList(L,AModule.InterfaceSection.Variables);
- AddElementsFromList(L,AModule.InterfaceSection.ResStrings);
- end;
- end;
- function TFPDocWriter.InterpretOption(const Cmd, Arg: String): Boolean;
- begin
- Result:=False;
- end;
- class function TFPDocWriter.FileNameExtension: String;
- begin
- //Override in linear writers with the expected extension.
- Result := ''; //Output must not contain an extension.
- end;
- class procedure TFPDocWriter.Usage(List: TStrings);
- begin
- // Do nothing.
- end;
- class procedure TFPDocWriter.SplitImport(var AFilename, ALinkPrefix: String);
- var
- i: integer;
- begin
- //override in HTML and CHM writer
- i := Pos(',', AFilename);
- if i > 0 then
- begin //split CSV into filename and prefix
- ALinkPrefix := Copy(AFilename,i+1,Length(AFilename));
- SetLength(AFilename, i-1);
- end;
- end;
- procedure TFPDocWriter.WriteDocumentation;
- begin
- PrepareDocumentation();
- DoWriteDocumentation();
- OutputResults();
- end;
- function TFPDocWriter.FindTopicElement ( Node: TDocNode ) : TTopicElement;
- Var
- I : Integer;
- begin
- Result:=Nil;
- I:=FTopics.Count-1;
- While (I>=0) and (Result=Nil) do
- begin
- If (TTopicElement(FTopics[i]).TopicNode=Node) Then
- Result:=TTopicElement(FTopics[i]);
- Dec(I);
- end;
- end;
- procedure TFPDocWriter.DescrWriteImageEl(const AFileName, ACaption,
- ALinkName: DOMString);
- begin
- DoLog('%s : No support for images yet: %s (caption: "%s")',[ClassName,AFileName,ACaption]);
- end;
- procedure TFPDocWriter.PrepareDocumentation;
- begin
- // Ancestors can call AllocatePages();CreateAllocator(); into base class
- end;
- { ---------------------------------------------------------------------
- Generic documentation node conversion
- ---------------------------------------------------------------------}
- function IsContentNodeType(Node: TDOMNode): Boolean;
- begin
- Result := (Node.NodeType = ELEMENT_NODE) or
- ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or
- (Node.NodeType = ENTITY_REFERENCE_NODE);
- end;
- procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String);
- begin
- if (AContext<>nil) then
- DoLog('[%s] %s',[AContext.PathName,AMsg])
- else
- DoLog('[<no context>] %s', [AMsg]);
- end;
- procedure TFPDocWriter.Warning(AContext: TPasElement; const AMsg: String;
- const Args: array of const);
- begin
- Warning(AContext, Format(AMsg, Args));
- end;
- function TFPDocWriter.IsDescrNodeEmpty(Node: TDOMNode): Boolean;
- var
- Child: TDOMNode;
- begin
- if (not Assigned(Node)) or (not Assigned(Node.FirstChild)) then
- Result := True
- else
- begin
- Child := Node.FirstChild;
- while Assigned(Child) do
- begin
- if (Child.NodeType = ELEMENT_NODE) or (Child.NodeType = TEXT_NODE) or
- (Child.NodeType = ENTITY_REFERENCE_NODE) then
- begin
- Result := False;
- exit;
- end;
- Child := Child.NextSibling;
- end;
- end;
- Result := True;
- end;
- { Check wether the nodes starting with the node given as argument make up an
- 'extshort' production. }
- function TFPDocWriter.IsExtShort(Node: TDOMNode): Boolean;
- begin
- while Assigned(Node) do
- begin
- if Node.NodeType = ELEMENT_NODE then
- if (Node.NodeName <> 'br') and
- (Node.NodeName <> 'link') and
- (Node.NodeName <> 'url') and
- (Node.NodeName <> 'b') and
- (Node.NodeName <> 'file') and
- (Node.NodeName <> 'i') and
- (Node.NodeName <> 'kw') and
- (Node.NodeName <> 'printshort') and
- (Node.NodeName <> 'var') then
- begin
- Result := False;
- exit;
- end;
- Node := Node.NextSibling;
- end;
- Result := True;
- end;
- function TFPDocWriter.ConvertShort(AContext: TPasElement;
- El: TDOMElement): Boolean;
- var
- Node: TDOMNode;
- begin
- Result := False;
- if not Assigned(El) then
- exit;
- FContext:=AContext;
- try
- Node := El.FirstChild;
- while Assigned(Node) do
- begin
- if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
- ConvertLink(AContext, TDOMElement(Node))
- else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'url') then
- ConvertURL(AContext, TDOMElement(Node))
- else
- if not ConvertBaseShort(AContext, Node) then
- exit;
- Node := Node.NextSibling;
- end;
- Result := True;
- finally
- FContext:=Nil;
- end;
- end;
- function TFPDocWriter.ConvertNotes(AContext: TPasElement; El: TDOMElement
- ): Boolean;
- Var
- L : TFPList;
- N : TDomNode;
- I : Integer;
- B : Boolean;
- begin
- Result:=Assigned(El) and EmitNotes;
- If Not Result then
- exit;
- L:=TFPList.Create;
- try
- N:=El.FirstChild;
- While Assigned(N) do
- begin
- If (N.NodeType=ELEMENT_NODE) and (N.NodeName='note') then
- begin
- B:=True;
- if Assigned(FBeforeEmitNote) then
- FBeforeEmitNote(Self,TDomElement(N),B);
- If B then
- L.Add(N);
- end;
- N:=N.NextSibling;
- end;
- Result:=L.Count>0;
- If Not Result then
- exit;
- DescrEmitNotesHeader(AContext);
- DescrBeginUnorderedList;
- For i:=0 to L.Count-1 do
- begin
- DescrBeginListItem;
- ConvertExtShortOrNonSectionBlocks(AContext, TDOMNode(L[i]).FirstChild);
- DescrEndListItem;
- end;
- DescrEndUnorderedList;
- DescrEmitNotesFooter(AContext);
- finally
- L.Free;
- end;
- end;
- function TFPDocWriter.ConvertBaseShort(AContext: TPasElement;
- Node: TDOMNode): Boolean;
- function ConvertText: DOMString;
- var
- s: DOMString;
- i: Integer;
- begin
- if Node.NodeType = TEXT_NODE then
- begin
- s := Node.NodeValue;
- i := 1;
- Result:='';
- while i <= Length(s) do
- if s[i] = #13 then
- begin
- Result := Result + ' ';
- Inc(i);
- if s[i] = #10 then
- Inc(i);
- end else if s[i] = #10 then
- begin
- Result := Result + ' ';
- Inc(i);
- end else
- begin
- Result := Result + s[i];
- Inc(i);
- end;
- end else if Node.NodeType = ENTITY_REFERENCE_NODE then
- if Node.NodeName = 'fpc' then
- Result := 'Free Pascal'
- else if Node.NodeName = 'delphi' then
- Result := 'Delphi'
- else
- begin
- Warning(AContext, Format(SErrUnknownEntityReference, [Node.NodeName]));
- Result := Node.NodeName;
- end
- else if Node.NodeType = ELEMENT_NODE then
- SetLength(Result, 0);
- end;
- function ConvertTextContent: DOMString;
- begin
- Result:='';
- Node := Node.FirstChild;
- while Assigned(Node) do
- begin
- Result := Result + ConvertText;
- Node := Node.NextSibling;
- end;
- end;
- var
- El, DescrEl: TDOMElement;
- hlp : TPasElement;
- begin
- Result := True;
- if Node.NodeType = ELEMENT_NODE then
- if Node.NodeName = 'b' then
- begin
- DescrBeginBold;
- ConvertBaseShortList(AContext, Node, False);
- DescrEndBold;
- end else
- if Node.NodeName = 'i' then
- begin
- DescrBeginItalic;
- ConvertBaseShortList(AContext, Node, False);
- DescrEndItalic;
- end else
- if Node.NodeName = 'em' then
- begin
- DescrBeginEmph;
- ConvertBaseShortList(AContext, Node, False);
- DescrEndEmph;
- end else
- if Node.NodeName = 'u' then
- begin
- DescrBeginUnderline;
- ConvertBaseShortList(AContext, Node, False);
- DescrEndUnderline;
- end else
- if Node.NodeName = 'file' then
- DescrWriteFileEl(ConvertTextContent)
- else if Node.NodeName = 'kw' then
- DescrWriteKeywordEl(ConvertTextContent)
- else if Node.NodeName = 'printshort' then
- begin
- El := TDOMElement(Node);
- hlp:=AContext;
- while assigned(hlp) and not (hlp is TPasModule) do
- hlp:=hlp.parent;
- if not (hlp is TPasModule) then
- hlp:=nil;
- DescrEl := Engine.FindShortDescr(TPasModule(hlp), UTF8Encode(El['id']));
- if Assigned(DescrEl) then
- ConvertShort(AContext, DescrEl)
- else
- begin
- Warning(AContext, Format(SErrUnknownPrintShortID, [El['id']]));
- DescrBeginBold;
- DescrWriteText('#ShortDescr:' + El['id']);
- DescrEndBold;
- end;
- end else if Node.NodeName = 'var' then
- DescrWriteVarEl(ConvertTextContent)
- else
- Result := False
- else
- DescrWriteText(ConvertText);
- end;
- procedure TFPDocWriter.ConvertBaseShortList(AContext: TPasElement;
- Node: TDOMNode; MayBeEmpty: Boolean);
- var
- Child: TDOMNode;
- begin
- Child := Node.FirstChild;
- while Assigned(Child) do
- begin
- if not ConvertBaseShort(AContext, Child) then
- Warning(AContext, SErrInvalidShortDescr)
- else
- MayBeEmpty := True;
- Child := Child.NextSibling;
- end;
- if not MayBeEmpty then
- Warning(AContext, SErrInvalidShortDescr)
- end;
- procedure TFPDocWriter.ConvertLink(AContext: TPasElement; El: TDOMElement);
- begin
- DescrBeginLink(El['id']);
- if not IsDescrNodeEmpty(El) then
- ConvertBaseShortList(AContext, El, True)
- else
- DescrWriteText(El['id']);
- DescrEndLink;
- end;
- procedure TFPDocWriter.ConvertURL(AContext: TPasElement; El: TDOMElement);
- begin
- DescrBeginURL(El['href']);
- if not IsDescrNodeEmpty(El) then
- ConvertBaseShortList(AContext, El, True)
- else
- DescrWriteText(El['href']);
- DescrEndURL;
- end;
- procedure TFPDocWriter.AddElementsFromList ( L: TStrings; List: TFPList;
- UsePathName: Boolean ) ;
- Var
- I : Integer;
- El : TPasElement;
- N : TDocNode;
- begin
- For I:=0 to List.Count-1 do
- begin
- El:=TPasElement(List[I]);
- N:=Engine.FindDocNode(El);
- if (N=Nil) or (not N.IsSkipped) then
- begin
- if UsePathName then
- L.AddObject(El.PathName,El)
- else
- L.AddObject(El.Name,El);
- If el is TPasEnumType then
- AddElementsFromList(L,TPasEnumType(el).Values);
- end;
- end;
- end;
- procedure TFPDocWriter.CreateClassTree;
- var
- L: TStringList;
- M: TPasModule;
- I:Integer;
- begin
- L:=TStringList.Create;
- try
- For I:=0 to Package.Modules.Count-1 do
- begin
- M:=TPasModule(Package.Modules[i]);
- if Not (M is TPasExternalModule) and assigned(M.InterfaceSection) then
- Self.AddElementsFromList(L,M.InterfaceSection.Classes,True)
- end;
- // You can see this tree by using --format=xml option
- TreeClass.BuildTree(L);
- TreeInterface.BuildTree(L);
- Finally
- L.Free;
- end;
- end;
- procedure TFPDocWriter.DoLog(const Msg: String);
- begin
- If Assigned(FEngine.OnLog) then
- FEngine.OnLog(Self,Msg);
- end;
- procedure TFPDocWriter.DoLog(const Fmt: String; Args: array of const);
- begin
- DoLog(Format(Fmt,Args));
- end;
- procedure TFPDocWriter.OutputResults();
- begin
- DoLog('Package: %s - Documentation process finished.', [FPackage.Name]);
- end;
- function TFPDocWriter.ConvertExtShort(AContext: TPasElement;
- Node: TDOMNode): Boolean;
- begin
- Result := False;
- while Assigned(Node) do
- begin
- if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'link') then
- ConvertLink(AContext, TDOMElement(Node))
- else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'url') then
- ConvertURL(AContext, TDOMElement(Node))
- else if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'br') then
- DescrWriteLinebreak
- else
- if not ConvertBaseShort(AContext, Node) then
- exit;
- Node := Node.NextSibling;
- end;
- Result := True;
- end;
- procedure TFPDocWriter.ConvertDescr(AContext: TPasElement; El: TDOMElement;
- AutoInsertBlock: Boolean);
- var
- Node, Child: TDOMNode;
- ParaCreated: Boolean;
- begin
- FContext:=AContext;
- try
- if AutoInsertBlock then
- if IsExtShort(El.FirstChild) then
- DescrBeginParagraph
- else
- AutoInsertBlock := False;
- Node := El.FirstChild;
- if not ConvertExtShort(AContext, Node) then
- begin
- while Assigned(Node) do
- begin
- if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'section') then
- begin
- DescrBeginSectionTitle;
- Child := Node.FirstChild;
- while Assigned(Child) and (Child.NodeType <> ELEMENT_NODE) do
- begin
- if not IsDescrNodeEmpty(Child) then
- Warning(AContext, SErrInvalidContentBeforeSectionTitle);
- Child := Child.NextSibling;
- end;
- if not Assigned(Child) or (Child.NodeName <> 'title') then
- Warning(AContext, SErrSectionTitleExpected)
- else
- ConvertShort(AContext, TDOMElement(Child));
- DescrBeginSectionBody;
- if IsExtShort(Child) then
- begin
- DescrBeginParagraph;
- ParaCreated := True;
- end else
- ParaCreated := False;
- ConvertExtShortOrNonSectionBlocks(AContext, Child.NextSibling);
- if ParaCreated then
- DescrEndParagraph;
- DescrEndSection;
- end else if not ConvertNonSectionBlock(AContext, Node) then
- Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
- Node := Node.NextSibling;
- end;
- end else
- if AutoInsertBlock then
- DescrEndParagraph;
- finally
- FContext:=Nil;
- end;
- end;
- procedure TFPDocWriter.ConvertExtShortOrNonSectionBlocks(AContext: TPasElement;
- Node: TDOMNode);
- begin
- if not ConvertExtShort(AContext, Node) then
- while Assigned(Node) do
- begin
- if not ConvertNonSectionBlock(AContext, Node) then
- Warning(AContext, SErrInvalidDescr, [Node.NodeName]);
- Node := Node.NextSibling;
- end;
- end;
- function TFPDocWriter.ConvertNonSectionBlock(AContext: TPasElement;
- Node: TDOMNode): Boolean;
- procedure ConvertCells(Node: TDOMNode);
- var
- Child: TDOMNode;
- IsEmpty: Boolean;
- begin
- Node := Node.FirstChild;
- IsEmpty := True;
- while Assigned(Node) do
- begin
- if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
- begin
- DescrBeginTableCell;
- Child := Node.FirstChild;
- if not ConvertExtShort(AContext, Child) then
- while Assigned(Child) do
- begin
- if not ConvertSimpleBlock(AContext, Child) then
- Warning(AContext, SErrInvalidTableContent);
- Child := Child.NextSibling;
- end;
- DescrEndTableCell;
- IsEmpty := False;
- end else
- if IsContentNodeType(Node) then
- Warning(AContext, SErrInvalidTableContent);
- Node := Node.NextSibling;
- end;
- if IsEmpty then
- Warning(AContext, SErrTableRowEmpty);
- end;
- procedure ConvertTable;
- function GetColCount(Node: TDOMNode): Integer;
- begin
- Result := 0;
- Node := Node.FirstChild;
- while Assigned(Node) do
- begin
- if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'td') then
- Inc(Result);
- Node := Node.NextSibling;
- end;
- end;
- var
- s: DOMString;
- HasBorder, CaptionPossible, HeadRowPossible: Boolean;
- ColCount, ThisRowColCount: Integer;
- Subnode: TDOMNode;
- begin
- s := TDOMElement(Node)['border'];
- if s = '1' then
- HasBorder := True
- else
- begin
- HasBorder := False;
- if (Length(s) <> 0) and (s <> '0') then
- Warning(AContext, SErrInvalidBorderValue, ['<table>']);
- end;
- // Determine the number of columns
- ColCount := 0;
- Subnode := Node.FirstChild;
- while Assigned(Subnode) do
- begin
- if Subnode.NodeType = ELEMENT_NODE then
- if (Subnode.NodeName = 'caption') or (Subnode.NodeName = 'th') or
- (Subnode.NodeName = 'tr') then
- begin
- ThisRowColCount := GetColCount(Subnode);
- if ThisRowColCount > ColCount then
- ColCount := ThisRowColCount;
- end;
- Subnode := Subnode.NextSibling;
- end;
- DescrBeginTable(ColCount, HasBorder);
- Node := Node.FirstChild;
- CaptionPossible := True;
- HeadRowPossible := True;
- while Assigned(Node) do
- begin
- if Node.NodeType = ELEMENT_NODE then
- if CaptionPossible and (Node.NodeName = 'caption') then
- begin
- DescrBeginTableCaption;
- if not ConvertExtShort(AContext, Node.FirstChild) then
- Warning(AContext, SErrInvalidTableContent);
- DescrEndTableCaption;
- CaptionPossible := False;
- end else if HeadRowPossible and (Node.NodeName = 'th') then
- begin
- DescrBeginTableHeadRow;
- ConvertCells(Node);
- DescrEndTableHeadRow;
- CaptionPossible := False;
- HeadRowPossible := False;
- end else if Node.NodeName = 'tr' then
- begin
- DescrBeginTableRow;
- ConvertCells(Node);
- DescrEndTableRow;
- end else
- Warning(AContext, SErrInvalidTableContent)
- else if IsContentNodeType(Node) then
- Warning(AContext, SErrInvalidTableContent);
- Node := Node.NextSibling;
- end;
- DescrEndTable;
- end;
- begin
- if Node.NodeType <> ELEMENT_NODE then
- begin
- if Node.NodeType = TEXT_NODE then
- Result := IsWhitespaceNode(TDOMText(Node))
- else
- Result := Node.NodeType = COMMENT_NODE;
- exit;
- end;
- if Node.NodeName = 'remark' then
- begin
- DescrBeginRemark;
- Node := Node.FirstChild;
- if not ConvertExtShort(AContext, Node) then
- while Assigned(Node) do
- begin
- if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'table') then
- ConvertTable
- else
- if not ConvertSimpleBlock(AContext, Node) then
- Warning(AContext, SErrInvalidRemarkContent, [Node.NodeName]);
- Node := Node.NextSibling;
- end;
- DescrEndRemark;
- Result := True;
- end else if Node.NodeName = 'table' then
- begin
- ConvertTable;
- Result := True;
- end else
- Result := ConvertSimpleBlock(AContext, Node);
- end;
- function TFPDocWriter.ConvertSimpleBlock(AContext: TPasElement;
- Node: TDOMNode): Boolean;
- procedure ConvertListItems;
- var
- Empty: Boolean;
- begin
- Node := Node.FirstChild;
- Empty := True;
- while Assigned(Node) do
- begin
- if ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or (Node.NodeType = ENTITY_REFERENCE_NODE)
- then
- Warning(AContext, SErrInvalidListContent)
- else if Node.NodeType = ELEMENT_NODE then
- if Node.NodeName = 'li' then
- begin
- DescrBeginListItem;
- ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
- DescrEndListItem;
- Empty := False;
- end else
- Warning(AContext, SErrInvalidElementInList);
- Node := Node.NextSibling;
- end;
- if Empty then
- Warning(AContext, SErrListIsEmpty);
- end;
- procedure ConvertDefinitionList;
- var
- Empty, ExpectDTNext: Boolean;
- begin
- Node := Node.FirstChild;
- Empty := True;
- ExpectDTNext := True;
- while Assigned(Node) do
- begin
- if ((Node.NodeType = TEXT_NODE) and not IsWhitespaceNode(TDOMText(Node))) or (Node.NodeType = ENTITY_REFERENCE_NODE)
- then
- Warning(AContext, SErrInvalidListContent)
- else if Node.NodeType = ELEMENT_NODE then
- if ExpectDTNext and (Node.NodeName = 'dt') then
- begin
- DescrBeginDefinitionTerm;
- if not ConvertShort(AContext, TDOMElement(Node)) then
- Warning(AContext, SErrInvalidDefinitionTermContent);
- DescrEndDefinitionTerm;
- Empty := False;
- ExpectDTNext := False;
- end else if not ExpectDTNext and (Node.NodeName = 'dd') then
- begin
- DescrBeginDefinitionEntry;
- ConvertExtShortOrNonSectionBlocks(AContext, Node.FirstChild);
- DescrEndDefinitionEntry;
- ExpectDTNext := True;
- end else
- Warning(AContext, SErrInvalidElementInList);
- Node := Node.NextSibling;
- end;
- if Empty then
- Warning(AContext, SErrListIsEmpty)
- else if not ExpectDTNext then
- Warning(AContext, SErrDefinitionEntryMissing);
- end;
- procedure ProcessCodeBody(Node: TDOMNode);
- var
- s: String;
- i, j: Integer;
- begin
- Node := Node.FirstChild;
- S:='';
- while Assigned(Node) do
- begin
- if Node.NodeType = TEXT_NODE then
- begin
- s := s + UTF8Encode(Node.NodeValue);
- j := 1;
- for i := 1 to Length(s) do
- // In XML, linefeeds are normalized to #10 by the parser!
- if s[i] = #10 then
- begin
- DescrWriteCodeLine(Copy(s, j, i - j));
- j := i + 1;
- end;
- if j > 1 then
- s := Copy(s, j, Length(s));
- end;
- Node := Node.NextSibling;
- end;
- if Length(s) > 0 then
- DescrWriteCodeLine(s);
- end;
- var
- s: DOMString;
- HasBorder: Boolean;
- begin
- if Node.NodeType <> ELEMENT_NODE then
- begin
- Result := (Node.NodeType = TEXT_NODE) and IsWhitespaceNode(TDOMText(Node));
- exit;
- end;
- if Node.NodeName = 'p' then
- begin
- DescrBeginParagraph;
- if not ConvertExtShort(AContext, Node.FirstChild) then
- Warning(AContext, SErrInvalidParaContent);
- DescrEndParagraph;
- Result := True;
- end else if Node.NodeName = 'code' then
- begin
- s := TDOMElement(Node)['border'];
- if s = '1' then
- HasBorder := True
- else
- begin
- if (Length(s) > 0) and (s <> '0') then
- Warning(AContext, SErrInvalidBorderValue, ['<code>']);
- end;
- DescrBeginCode(HasBorder, UTF8Encode(TDOMElement(Node)['highlighter']));
- ProcessCodeBody(Node);
- DescrEndCode;
- Result := True;
- end else if Node.NodeName = 'pre' then
- begin
- DescrBeginCode(False, 'none');
- ProcessCodeBody(Node);
- DescrEndCode;
- Result := True;
- end else if Node.NodeName = 'ul' then
- begin
- DescrBeginUnorderedList;
- ConvertListItems;
- DescrEndUnorderedList;
- Result := True;
- end else if Node.NodeName = 'ol' then
- begin
- DescrBeginOrderedList;
- ConvertListItems;
- DescrEndOrderedList;
- Result := True;
- end else if Node.NodeName = 'dl' then
- begin
- DescrBeginDefinitionList;
- ConvertDefinitionList;
- DescrEndDefinitionList;
- Result := True;
- end else if Node.NodeName = 'img' then
- begin
- begin
- ConvertImage(Node as TDomElement);
- Result:=True;
- end;
- end else
- Result := False;
- end;
- procedure TFPDocWriter.ConvertImage ( El: TDomElement ) ;
- Var
- FN,Cap,LinkName : DOMString;
- begin
- FN:=El['file'];
- Cap:=El['caption'];
- LinkName:=El['name'];
- FN:=UTF8decode(ChangeFileExt(UTF8Encode(FN),ImageExtension));
- DescrWriteImageEl(FN,Cap,LinkName);
- end;
- procedure TFPDocWriter.DescrEmitNotesHeader(AContext: TPasElement);
- begin
- DescrWriteLinebreak;
- DescrBeginBold;
- DescrWriteText(UTF8Decode(SDocNotes));
- DescrEndBold;
- DescrWriteLinebreak;
- end;
- procedure TFPDocWriter.DescrEmitNotesFooter(AContext: TPasElement);
- begin
- DescrWriteLinebreak;
- end;
- Constructor TTopicElement.Create(const AName: String; AParent: TPasElement);
- begin
- Inherited Create(AName,AParent);
- SubTopics:=TList.Create;
- end;
- Destructor TTopicElement.Destroy;
- begin
- // Actual subtopics are freed by TFPDocWriter Topics list.
- SubTopics.Free;
- Inherited;
- end;
- function TFPDocWriter.WriteDescr ( Element: TPasElement ) : TDocNode;
- begin
- Result:=Engine.FindDocNode(Element);
- WriteDescr(ELement,Result);
- end;
- procedure TFPDocWriter.WriteDescr(Element: TPasElement; DocNode: TDocNode);
- begin
- if Assigned(DocNode) then
- begin
- if not IsDescrNodeEmpty(DocNode.Descr) then
- WriteDescr(Element, DocNode.Descr)
- else if not IsDescrNodeEmpty(DocNode.ShortDescr) then
- WriteDescr(Element, DocNode.ShortDescr);
- end;
- end;
- procedure TFPDocWriter.WriteDescr(AContext: TPasElement; DescrNode: TDOMElement);
- begin
- if Assigned(DescrNode) then
- ConvertDescr(AContext, DescrNode, False);
- end;
- procedure TFPDocWriter.FPDocError(Msg: String);
- begin
- Raise EFPDocWriterError.Create(Msg);
- end;
- procedure TFPDocWriter.FPDocError(Fmt: String; Args: array of const);
- begin
- FPDocError(Format(Fmt,Args));
- end;
- function TFPDocWriter.ShowMember(M: TPasElement): boolean;
- begin
- Result:=not ((M.Visibility=visPrivate) and Engine.HidePrivate);
- If Result then
- Result:=Not ((M.Visibility=visProtected) and Engine.HideProtected)
- end;
- procedure TFPDocWriter.GetMethodList ( ClassDecl: TPasClassType;
- List: TStringList ) ;
- Var
- I : Integer;
- M : TPasElement;
- begin
- List.Clear;
- List.Sorted:=False;
- for i := 0 to ClassDecl.Members.Count - 1 do
- begin
- M:=TPasElement(ClassDecl.Members[i]);
- if M.InheritsFrom(TPasProcedureBase) and ShowMember(M) then
- List.AddObject(M.Name,M);
- end;
- List.Sorted:=False;
- end;
- initialization
- InitWriterList;
- finalization
- DoneWriterList;
- end.
|