would be divided up into:
procedure
tsKeyWord
tsSpace
TForm1
tsIdentifier
.
tsSymbol
FormCreate
tsIdentifier
(
tsSymbol
Sender
tsIdentifier
:
tsSymbol
TObject
tsIdentifier
);
tsSymbol
tsSpace
{Create Form}
tsComment
<CR><LF>
tsCRLF
How is it Done?
The RichEdit control normally loads preformatted text from .RTF files by way of by of the RichEdit.Lines.LoadFromFile() function. YourPasEdit uses the RichEdit.Lines.LoadFromStream() function to load the file from a TPasConversion - a custom TMemoryStream descendant. This stream takes the plaint text Pascal source file, loads it into its internal memory buffe, and then converts it from plain text to a text impregnated with RTF codes. This way when it is loaded into the RichEdit control via RichEdit.Lines.LoadFromStream the Pascal source file appears in the control color-syntax highlighted.
To the main Editor, this process is transparent - the code looks something like this:
begin
NewRichEdit := TRichEdit.Create;
PasCon.Clear; // Prepare the TPasConversion PasCon.LoadFromFile(FName); // Load the File into the Memory Stream PasCon.ConvertReadStream; // Convert the stream to RTF format
NewRichEdit.Lines.BeginUpdate; NewRichEdit.Lines.LoadFromStream(PasCon); // Read from the TPasConversion NewRichEdit.Lines.EndUpdate
NewRichEdit.Show;
Result := NewRichEdit;
end
EXAMPLE - snippet of code from the NewRichEditCreate(Fname) routine
As I said, it is the TMemoryStream derived TPasConversion which does all the hard work:
<SOURCE PASCAL FILE> | V Plain source loaded into memory (TPasConversion.LoadFromFile) | V Converted internally by parsing the source file (ConvertReadStream) | V Result made available (SetMemoryPointer) | V RichEdit.LoadFromStream
Most of the work in TPasConversion is done by the ConvertReadStream procedure. Its purpose is to split up each line of source code into tokens (as showed previously) and then depending on its TokenType, load it into the outbuffer preceded by RTF codes to make it a particular Color, Bold, Italics etc. Here what it looks like:
// prepare the Outbuf to a certain default size
FOutBuffSize:= size+3; ReAllocMem(FOutBuff, FOutBuffSize);
// Initialise the parser to its begining state
FTokenState := tsUnknown; FComment := csNo; FBuffPos := 0; FReadBuff := Memory;
// Write leading RTF Header
WriteToBuffer(''''{\rtf1\ansi\deff0\deftab720{\fonttbl{\f0\fswiss MS SansSerif;} {\f1\froman\fcharset2 Symbol;}{\f2\fmodern Courier New;}}''''+#13+#10); WriteToBuffer(''''{\colortbl\red0\green0\blue0;}''''+#13+#10); WriteToBuffer(''''\deflang1033\pard\plain\f2\fs20 '''');
// Create the INSTREAM (FReadBuff) and tokenize it
Result:= Read(FReadBuff^, Size); FReadBuff[Result] := #0;
if Result > 0 then begin
Run:= FReadBuff; TokenPtr:= Run;
while Run^ <> #0 do begin
Case Run^ of
#13: // Deal with CRLFs begin FComment:= csNo; HandleCRLF; end;
#1..#9, #11, #12, #14..#32: // Deal with various whitespaces, control codes begin while Run^ in [#1..#9, #11, #12, #14..#32] do inc(Run); FTokenState:= tsSpace; TokenLen:= Run - TokenPtr; SetString(TokenStr, TokenPtr, TokenLen); SetRTF; WriteToBuffer(Prefix + TokenStr + Postfix); TokenPtr:= Run; end;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~ much code removed ~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
end;
end
EXAMPLE - snippet showing the while loop that breaks up the INSTREAM into recognised tokens
Most of the work is done by the [case Run^ in ... end;] section which "breaks off" a token from the INSTREAM (FReadBuf) based on the logic in the case statement. The case statement is organised in such a way that it can quickly decipher the input stream into the various TokenTypes by examining each character in turn. Having worked out which tokentype it is, the actual encoding part is relatively easy:
FTokenState:= tsSpace; TokenLen:= Run - TokenPtr; SetString(TokenStr, TokenPtr, TokenLen); ScanForRTF; SetRTF; WriteToBuffer(Prefix + TokenStr + Postfix);
EXAMPLE - basic steps in encoding the output stream of the TPasConversion object
What’s happening here is the program:
|