YOUSEFUL / DELPHI / COMPONENTS
TAtlasSyntaxMemo
Rich Edit control that colours Object Pascal source as you type and keeps its own text-only undo and redo history.
Description
A TRichEdit set up for source code: plain text, no word wrap, both scroll bars, Tab inserts a tab, Consolas 11, 700 by 450 pixels. Every other property, method and event of TRichEdit (Text, Lines, SelStart, ReadOnly, OnChange, ...) works as usual.
Highlighting. After every text change the whole text is tokenized with AtlasPascalTokens and coloured with AtlasTokenColor: keywords dark blue and bold, strings dark red, comments green, numbers purple, everything else near-black. The caret, the selection, the scroll position and the Modified flag are preserved. Highlighting needs a window, so it only runs once the editor has a Parent: text assigned before that stays uncoloured until the next change or a call to Highlight. See TAtlasTokenKind for exactly what the lexer recognizes. It is a lexer, not a parser: there is no code completion, error marking, folding, line numbers, bracket matching or support for other languages.
Undo. The editor records a copy of the whole text after each change, up to 128 copies, and UndoText and RedoText step through them. Ctrl+Z, Ctrl+Y and Ctrl+Shift+Z, the WM_UNDO, EM_UNDO and EM_REDO messages, and the inherited Undo and CanUndo all use this history instead of the Rich Edit one, so colouring never becomes an undo step. Undo and redo restore the text only: the caret moves to the end and colours are recomputed. A new edit after an undo discards the redo steps. The history starts with the empty text the editor had when it was created and there is no method to clear it, so undoing past a loaded file empties the editor. A read-only editor ignores undo and redo.
Size. Each change re-colours the whole text and stores another full copy of it, so the editor suits small and moderate files. Performance with large files has not been measured.
Members
| Member | Kind | What it does |
|---|---|---|
Create | constructor | Creates the editor with PlainText, no WordWrap, ScrollBars set to ssBoth, WantTabs, font Consolas 11, 700 by 450 pixels, and a text history holding the empty text. |
Destroy | destructor | Frees the text history and destroys the control. |
Highlight | method | Tokenizes the current text and applies the colours now, keeping the caret, selection, scroll position and Modified. |
UndoText | method | Restores the text as it was before the last change, moves the caret to the end and sets Modified. |
RedoText | method | Re-applies the change the last UndoText took back, moves the caret to the end and sets Modified. |
CanUndoText | method | True when UndoText has an earlier text to go back to. |
CanRedoText | method | True when RedoText has a later text to restore, that is after an undo and before any new edit. |
Tokens | method | A copy of the tokens found by the last Highlight, covering the whole text. |
Constructors
Create constructor
constructor Create(AOwner: TComponent); override;Creates the editor with PlainText, no WordWrap, ScrollBars set to ssBoth, WantTabs, font Consolas 11, 700 by 450 pixels, and a text history holding the empty text.
Destructors
Destroy destructor
destructor Destroy; override;Frees the text history and destroys the control.
Methods
Highlight method
procedure Highlight;Tokenizes the current text and applies the colours now, keeping the caret, selection, scroll position and Modified. The editor calls it after every change; call it yourself after assigning Parent to an editor that already has text. Does nothing while the editor has no Parent, or when called during its own formatting.
Editor.Parent := Self;
Editor.Highlight;UndoText method
procedure UndoText;Restores the text as it was before the last change, moves the caret to the end and sets Modified. Does nothing when there is nothing to undo or the editor is read-only.
if Editor.CanUndoText then
Editor.UndoText;RedoText method
procedure RedoText;Re-applies the change the last UndoText took back, moves the caret to the end and sets Modified. Does nothing when there is nothing to redo or the editor is read-only.
CanUndoText method
function CanUndoText: Boolean;True when UndoText has an earlier text to go back to. The inherited CanUndo returns the same value.
CanRedoText method
function CanRedoText: Boolean;True when RedoText has a later text to restore, that is after an undo and before any new edit.
Tokens method
function Tokens: TArray<TAtlasToken>;A copy of the tokens found by the last Highlight, covering the whole text. Start is the 0-based character position in the editor, with each line break counted as one character, so it can be assigned to SelStart directly. Empty until the first highlight.
for Token in Editor.Tokens do
if Token.Kind = atkComment then
Inc(CommentCount);Examples
A Pascal editor with Undo and Redo buttons
A form built in code. The editor gets its Parent before any text, so loaded files are coloured at once, and OnChange keeps the buttons in step with CanUndoText and CanRedoText. Call LoadFile with the name of a .pas file after creating the form.
uses System.SysUtils, System.Classes, System.IOUtils, Vcl.Forms, Vcl.Controls, Vcl.StdCtrls, Vcl.ExtCtrls,
Atlas.Syntax;
type
TSourceForm = class(TForm)
private
FEditor: TAtlasSyntaxMemo;
FUndo, FRedo: TButton;
procedure EditorChange(Sender: TObject);
procedure UndoClick(Sender: TObject);
procedure RedoClick(Sender: TObject);
public
constructor Create(AOwner: TComponent); override;
procedure LoadFile(const FileName: string);
end;
constructor TSourceForm.Create(AOwner: TComponent);
var
Bar: TPanel;
begin
inherited CreateNew(AOwner);
Caption := 'Source';
Width := 800;
Height := 600;
Bar := TPanel.Create(Self);
Bar.Parent := Self;
Bar.Align := alTop;
Bar.Height := 40;
FUndo := TButton.Create(Self);
FUndo.Parent := Bar;
FUndo.SetBounds(8, 6, 80, 28);
FUndo.Caption := 'Undo';
FUndo.OnClick := UndoClick;
FRedo := TButton.Create(Self);
FRedo.Parent := Bar;
FRedo.SetBounds(96, 6, 80, 28);
FRedo.Caption := 'Redo';
FRedo.OnClick := RedoClick;
FEditor := TAtlasSyntaxMemo.Create(Self);
FEditor.Parent := Self; // before any text: highlighting needs a parent
FEditor.Align := alClient;
FEditor.OnChange := EditorChange;
EditorChange(nil);
end;
procedure TSourceForm.LoadFile(const FileName: string);
begin
FEditor.Text := TFile.ReadAllText(FileName);
FEditor.Modified := False;
end;
procedure TSourceForm.EditorChange(Sender: TObject);
begin
FUndo.Enabled := FEditor.CanUndoText;
FRedo.Enabled := FEditor.CanRedoText;
end;
procedure TSourceForm.UndoClick(Sender: TObject);
begin
FEditor.UndoText;
FEditor.SetFocus;
end;
procedure TSourceForm.RedoClick(Sender: TObject);
begin
FEditor.RedoText;
FEditor.SetFocus;
end;