📄 udocumentdoc.pas
字号:
{Generates an index about all identifiers and files beginning with the
character.
~param List the list of all identifiers and files to generate the
index of
~param EntryIndex the index of the first entry with the letter
~param Index the character to generate the index for
~result the first entry not in the index (i.e. EntryIndex for the next
letter) }
function DoGenerateIndex(List: TIdentifierFileList; EntryIndex: Integer;
Index: Char): Integer; virtual; abstract;
//Generates an index about all identifiers and files.
procedure GenerateIndex;
property DocumentDocMessagesID: TMessageID read FDocumentDocMessagesID;
property GenerateFileTreeFiles: Boolean read FGenerateFileTreeFiles;
property GenerateClassesTreeFiles: Boolean read FGenerateClassesTreeFiles;
property GenerateXFigFiles: Boolean read FGenerateXFigFiles;
property GenerateWMFFiles: Boolean read FGenerateWMFFiles;
public
//Creates the generator object.
constructor Create; override;
//Destroys the generator object.
destructor Destroy; override;
//Will be called for all names of identifier-type identifiers.
function TypeIdentifierText(Ident: TIdentifier): String; override;
//Will be called for all expressions.
function ExprText(const ExprStr: String;
SourceIdent: TIdentifier): String; override;
//Will be called for read- and write-attributes of properties.
function PropertyReadWrite(const ReadWrite: String; Prop: TProperty;
SourceIdent: TIdentifier): String; override;
//Will be called for implements-attribute of properties.
function PropertyImplements(Prop: TProperty;
SourceIdent: TIdentifier): String; override;
//Will be called for a reference on a method of an implemented interface.
function InterfaceMethod(const TheName: String; MethodOf: TRecordType;
SourceIdent: TIdentifier): String; override;
//Will be called for a reference on a method.
function MethodName(const Name: String; MethodOf: TRecordType;
SourceIdent: TIdentifier): String; override;
//Returns the number of available options in generators of this class.
class function GetOptionCount: Cardinal; override;
//Gets a description of an option.
class procedure GetOptionDescription(Index: Cardinal;
var Desc: TOptionDescription);
override;
//Gets the value of an option.
function GetOption(Index: Cardinal): TOptionValue; override;
//Sets the value of an option.
procedure SetOption(Index: Cardinal; const Value: TOptionValue); override;
//Handles text by encoding special characters of the format.
function HandleRawText(const Text: String): String; virtual;
//Returns the current and for the current format escaped text.
function Localize(Text: TDocumentationTexts): String;
//Returns the current and for the current format escaped text with the
//parameters.
function LocalizeFmt(Text: TDocumentationTexts;
const Args: array of const): String;
//Returns the unique ID of an identifier to be used in the documentation.
function GetURIOf(Ident: TIdentifier;
TheFile: TPascalFile = nil): String; virtual;
end;
//plural names/texts of the kinds of record-like types
const Plurals: array[TRecordKind] of TDocumentationTexts =
(dtRecordsHeader,
dtObjectsHeader, dtClassesHeader,
dtInterfacesHeader, dtDispInterfacesHeader);
implementation
uses Windows, SysUtils,
General,
UVectorGraphics,
UTokenParser;
{ * * * *** * * * *** TDocumentDoc *** * * * *** * * * }
//the descriptions of messages that can be added in the class
//~[link TDocumentDoc]
var DocumentDocMessageDescriptions: TMessageDescriptions = nil;
{Creates the generator object and the list of messages. }
constructor TDocumentDoc.Create;
begin
inherited Create; //create object
//register messages of this class
FDocumentDocMessagesID := RegisterMessages(DocumentDocMessageDescriptions);
FGenerateFileTreeFiles := True; //generate all possible files
FGenerateClassesTreeFiles := True;
FGenerateXFigFiles := True;
{$IFNDEF LINUX}
FGenerateWMFFiles := True;
{$ENDIF}
FFileTreeFileBaseName := 'UnitUse'; //set names of the files
FClassTreeFileBaseName[rkRecord] := DescFilePreFix[rkRecord] + 'List';
FClassTreeFileBaseName[rkObject] := DescFilePreFix[rkObject] + 'List';
FClassTreeFileBaseName[rkClass] := DescFilePreFix[rkClass] + 'List';
FClassTreeFileBaseName[rkInterface] := DescFilePreFix[rkInterface] + 'List';
FClassTreeFileBaseName[rkDispInterface] := DescFilePreFix[rkDispInterface] +
'List';
end;
{Destroys the generator object. }
destructor TDocumentDoc.Destroy;
begin
//unregister messages of this class
UnRegisterMessages(FDocumentDocMessagesID);
inherited Destroy; //free the object
end;
{Will be called for all names of identifier-type identifiers. This means it
will be called by the class ~[linkClass TIdentType] for any known type
identifier. A link on the identifier is returned.
~param Ident a type identifier to that a link should be generated
~result a link on the identifier }
function TDocumentDoc.TypeIdentifierText(Ident: TIdentifier): String;
begin
if DoNotDocumentIdentifier(Ident) then //not documented?
//just return the text of it
Result := IdentifierText(Ident.InFile.InternalFileName + '.' +
Ident.Name)
else
//create and return the link
Result := GetIdentNameLink(Ident);
end;
{Will be called for texts of expressions, for instance like used as the index
of an array or the value of a constant. The text should be parsed and
identifiers should be underlaid with a link to their declaration if possible.
~param ExprStr the text of the expression
~param SourceIdent the identifier from that the description that included this
expression has been requested
~result the formatted expression with links
~todo reimplement, I'm not really happy with it, but it does work in most
cases
~todo FileGiven has to check for Unit-Aliases! }
function TDocumentDoc.ExprText(const ExprStr: String;
SourceIdent: TIdentifier): String;
var SourceRec :TRecordType; //current record-like type
State :( //general state in the expression
sNo, //after operator or similar character
sIdent, //after an identifier (or dot)
sBrace, //after an opening brace or bracket
sString); //after a string
Parser :TTokenParser; //parser of the expression
Token :String; //a token in the expression
Next :String; //the next token after an identifier
FileGiven :Boolean; //file of identifier was given
Position :TPosition; //position to start search
Ident :TIdentifier; //identifiers in the expression
dot :Integer; //index of a "." in the token
//file of record-like type to search identifiers in
RecIdent :TRecordType; //used record-like type
IdentName :String; //a used member of a record-like type
begin
assert(assigned(SourceIdent));
assert(assigned(SourceIdent.InFile));
Result := ''; //no expression parsed so far
if SourceIdent is TRecordType then //get current record-like type
SourceRec := TRecordType(SourceIdent) //is is one, use it
else
SourceRec := SourceIdent.MemberOf; //use its record-like type
if assigned(SourceIdent) then //get position to search from
begin
if SourceIdent is TRecordType then //if it is a record-like type
Position := SourceIdent.Position //use its position
else
Position := SourceIdent.ForwardDefPos; //use always the first declaration
end
else
begin
Position.Row := -1; //search from the beginning
Position.Column := 1;
end;
Parser := TTokenParser.Create; //create tokenizer
try
Parser.PascalDialect := SourceIdent.InFile.PascalDialect;
Parser.ParseString(ExprStr); //parse expression text
State := sNo; //begin without a special state
while Parser.GetIdentWithPointsToken(Token) do //parse each token
begin //is an identifier?
if (Token <> '') and (Token[1] in StartIdentifierChars) then
begin
State := sIdent; //next token is after identifier
if LowerCase(Token) = 'nil' then //is nil?
Result := Result + ReservedWord(Token) //just add to the expression
else
begin
Result := Result + ' '; //add a space before it
Parser.PushPosition;
Parser.GetToken(Next); //preview the following token
Parser.PopPosition;
//not initialization of a record, i.e. a field of the record?
if Next <> ':' then
begin
dot := pos('.', Token);
if assigned(SourceRec) then //is in a record-like type?
begin
if dot = 0 then
dot := length(Token) + 1;
//search the identifier in it by this name
Ident := SourceRec.IdentList.GetIdentByName(copy(Token, 1,
dot - 1));
if assigned(Ident) then
begin
if DoNotDocumentIdentifier(Ident) then //not documented?
//just add the text
Result := Result + IdentifierText(copy(Token, 1, dot - 1))
else
//add link to it
Result := Result + GetRecordFieldNameLink(Ident);
Delete(Token, 1, dot); //remove identifier
end
else
dot := pos('.', Token);
end
else
Ident := nil; //not found so far
if not assigned(Ident) then
begin
//search the identifier by this name (globally)
Ident := FindIdentifier(Token, SourceIdent.InFile, Position);
if assigned(Ident) then //the identifier found?
begin
FileGiven := (dot <> 0) and //file name given?
(length(Ident.InFile.InternalFileName) =
dot - 1) and
(CompareText(Ident.InFile.InternalFileName,
copy(Token, 1, dot - 1)) = 0);
if dot = 0 then //get full length of identifier or file
dot := length(Token) + 1;
if DoNotDocumentIdentifier(Ident) then
begin
//add text of identifier
Result := Result + IdentifierText(copy(Token, 1, dot - 1));
if FileGiven then
begin
Delete(Token, 1, dot); //delete file
dot := pos('.', Token); //get identifier text
if dot = 0 then
dot := length(Token);
//add real text of identifier
Result := Result + IdentifierText(copy(Token, 1, dot - 1));
FileGiven := False; //file text already removed
end;
end
else
//get link to identifier
Result := Result + GetIdentNameLink(Ident, FileGiven);
if FileGiven then //if file given
begin
Delete(Token, 1, dot); //delete it
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -