Hi, I attached a highlighter I made for Markdown. I like to use this in
my projects to plan things.
It seems to work fine with any attribute that only requires a single
line. I tried and failed to make multiline fenced code work. I'm sure
there is just something I wasn't understanding.
```
multiline
code
block
```
Indenting works though.
If anyone has some clues I'd love to know what I did wrong :)
Regards,
Andrew Haines
{-------------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.
Author of this file is Andrew Haines.
All Rights Reserved.
Contributors to the SynEdit and mwEdit projects are listed in the
Contributors.txt file.
Alternatively, the contents of this file may be used under the terms of the
GNU General Public License Version 2 or later (the "GPL"), in which case
the provisions of the GPL are applicable instead of those above.
If you wish to allow use of your version of this file only under the terms
of the GPL and not to allow others to use your version of this file
under the MPL, indicate your decision by deleting the provisions above and
replace them with the notice and other provisions required by the GPL.
If you do not delete the provisions above, a recipient may use your version
of this file under either the MPL or the GPL.
$Id$
Known Issues:
Features that require multiple lines do not work
Tables are not highlighted
Links are not handled
Superscript/subscript
Probably more.
-------------------------------------------------------------------------------}
{
@abstract(A Basic Markdown language highlighter for SynEdit)
@author(Andrew Haines>)
@created(2023-01-03)
The SynHighlighterMarkdown implements a highlighter for Markdown for the SynEdit projects.
}
unit SynHighlighterMarkdown;
{$mode ObjFPC}{$H+}
{$ModeSwitch typehelpers}
{ $define TryMultiLine}
interface
uses
Classes, SysUtils, Graphics, SynEditTypes, SynEditHighlighter, SynEditHighlighterFoldBase, fgl;
type
{ TSynMarkdownRange }
TSynMarkdownRange = class(TSynCustomHighlighterRange)
private
FIsInFencedCodeBlock: Boolean;
procedure SetIsInFencedCodeBlock(AValue: Boolean);
public
property IsInFencedCodeBlock: Boolean read FIsInFencedCodeBlock write SetIsInFencedCodeBlock;
procedure Clear; override;
function Compare(Range: TSynCustomHighlighterRange): integer; override;
procedure Assign(Src: TSynCustomHighlighterRange); override;
function MaxFoldLevel: Integer; override;
end;
{ TSynMarkdownSyn }
TSynMarkdownSyn = class(TSynCustomFoldHighlighter)
private type
TAttr = (atText, atH1, atH2, atH3, atUnorderedList, atItalic, atBold, atBlockQuote, atCode, atFencedCode, atStrikethrough, atHighlight,
atStrongBold, atHorizontalRule, atEmoji, atOrderedList, atUnderline, atIndentedCode, atHeaderAlt);
TAttrs = set of TAttr;
const
Tokens : array [TAttr] of String = (
#255, '#', '##', '###', '-', '*', '**', '>', '`', '```', '~~', '==', '***', '---', ':', #255, '__', ' ', '===='
);
TokenIsPair: array [TAttr] of Boolean = (
False, False, False, False, False, True, True, False, True, True, True, True, True, False, True, False, True, False, False
);
TokenIsFirst: array [TAttr] of Boolean = ( // meaning only whitespace is allow before it
False, True, True, True, True, False, False, True, False, False, False, False, False, True, False, True, False, True, True
);
TokenIsVarLength: array [TAttr] of Boolean = (
False, False, False, True, False, False, False, True, False, True, False, False, False, True, False, False, False, True, True
);
TokenIsMultiline : array [TAttr] of Boolean = (
False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False
);
SpecialChars: TCharArray = (
'#', '*', '>', '`', '=', '-','[',']','{','}', '^', '|', '~', ':', '_', '+', ' '
);
cHeaderAttr = [atH1, atH2, atH3, atHeaderAlt];
type
TRange = (rCode);
TRangeSet = set of TRange;
{ TAttrInstance }
TAttrInstance = Class
private
FText: PChar;
public
Attr: TAttr;
Start: PChar;
Length: Integer;
Matched: Boolean;
function IsWhitespace: Boolean;
function Text: String;
constructor Create(AAttr: TAttr; AStart: PChar; ALength: Integer);
function NeedsMatch: Boolean;
end;
TMultiLine = class
StartLine: Integer;
EndLine: Integer;
end;
TMultiLineList = specialize TFPGObjectList<TMultiLine>;
private
FMultiLines: TMultiLineList;
FActiveAttrs: TAttrs;
FAttrs: Array[TAttr] of TSynHighlighterAttributes;
FLineTokens: specialize TFPGObjectList<TAttrInstance>;
FCurrentAttr: TSynHighlighterAttributes; // a composite
function GetAttr(AIndex: TAttr): TSynHighlighterAttributes;
procedure SetAttr(AIndex: TAttr; AValue: TSynHighlighterAttributes);
procedure TokenizeLine;
function Token: TAttrInstance;
procedure AddCodeFoldPoint(AToken: TAttrInstance);
protected
FRange: TRangeSet;
FTokenIndex: Integer;
FLineNumber: Integer;
FLineText: String;
public
function GetRangeClass: TSynCustomHighlighterRangeClass; override;
function GetRange: Pointer; override;
procedure SetRange(Value: Pointer); override;
procedure ResetRange; override;
class function GetLanguageName: string; override;
function GetTokenKind: integer; override;
function GetSampleSource: string; override;
procedure SetLine(const NewValue: String; LineNumber: Integer); override;
procedure Next; override;
function GetEol: Boolean; override;
procedure GetTokenEx(out TokenStart: PChar; out TokenLength: integer); override;
/// GetToken Attribute
function GetTokenAttribute: TSynHighlighterAttributes; override;
function SynRange: TSynMarkdownRange;
public
function GetToken: String; override;
function GetTokenPos: Integer; override;
function GetDefaultAttribute(Index: integer): TSynHighlighterAttributes; override;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property TextAttr: TSynHighlighterAttributes index atText read GetAttr write SetAttr;
property H1Attr: TSynHighlighterAttributes index atH1 read GetAttr write SetAttr;
property H2Attr: TSynHighlighterAttributes index atH2 read GetAttr write SetAttr;
property H3Attr: TSynHighlighterAttributes index atH3 read GetAttr write SetAttr;
property BlockquoteAttr: TSynHighlighterAttributes index atBlockQuote read GetAttr write SetAttr;
property OrderedListAttr: TSynHighlighterAttributes index atOrderedList read GetAttr write SetAttr;
property UnorderedListAttr: TSynHighlighterAttributes index atUnorderedList read GetAttr write SetAttr;
property CodeBlockAttr: TSynHighlighterAttributes index atFencedCode read GetAttr write SetAttr;
property HighlightAttr: TSynHighlighterAttributes index atHighlight read GetAttr write SetAttr;
property HorizontalRuleAttr: TSynHighlighterAttributes index atHorizontalRule read GetAttr write SetAttr;
property EmojiAttr: TSynHighlighterAttributes index atEmoji read GetAttr write SetAttr;
end;
implementation
type
{ TCharArrayHelper }
TCharArrayHelper = type helper for TCharArray
function Contains(C: Char): Boolean;
end;
{ TSynMarkdownRange }
procedure TSynMarkdownRange.SetIsInFencedCodeBlock(AValue: Boolean);
begin
if FIsInFencedCodeBlock=AValue then Exit;
FIsInFencedCodeBlock:=AValue;
end;
procedure TSynMarkdownRange.Clear;
begin
inherited Clear;
if Self is TSynMarkdownRange then
IsInFencedCodeBlock:=False;
end;
function TSynMarkdownRange.Compare(Range: TSynCustomHighlighterRange): integer;
begin
Result:=inherited Compare(Range);
if Result <>0 then
Exit;
if Range is TSynMarkdownRange then
Result := ord(TSynMarkdownRange(Range).IsInFencedCodeBlock) - ord(IsInFencedCodeBlock);
end;
procedure TSynMarkdownRange.Assign(Src: TSynCustomHighlighterRange);
begin
inherited Assign(Src);
if Src is TSynMarkdownRange then
begin
IsInFencedCodeBlock:=TSynMarkdownRange(Src).IsInFencedCodeBlock;
end;
end;
function TSynMarkdownRange.MaxFoldLevel: Integer;
begin
Result:=inherited MaxFoldLevel;
end;
{ TSynMarkdownSyn.TAttrInstance }
function TSynMarkdownSyn.TAttrInstance.IsWhitespace: Boolean;
var
i: Integer;
begin
Result := True;
i := 0;
while Result and (i < Length) do
begin
Result := Start[i] in [' ', #9];
Inc(i);
end;
end;
function TSynMarkdownSyn.TAttrInstance.Text: String;
begin
Result := Copy(Start, 0, Length);
end;
constructor TSynMarkdownSyn.TAttrInstance.Create(AAttr: TAttr; AStart: PChar;
ALength: Integer);
begin
Attr:=AAttr;
Start:=AStart;
Length:=ALength;
end;
function TSynMarkdownSyn.TAttrInstance.NeedsMatch: Boolean;
begin
Result := TokenIsPair[Attr];
end;
{ TCharArrayHelper }
function TCharArrayHelper.Contains(C: Char): Boolean;
var
i: Integer;
begin
for i := Low(Self) to High (Self) do
if Self[i] = C then
Exit(True);
Result := False;
end;
{ TSynMarkdownSyn }
function TSynMarkdownSyn.GetAttr(AIndex: TAttr): TSynHighlighterAttributes;
begin
Result := FAttrs[AIndex];
end;
procedure TSynMarkdownSyn.SetAttr(AIndex: TAttr;
AValue: TSynHighlighterAttributes);
begin
FAttrs[AIndex].Assign(AValue);
end;
procedure TSynMarkdownSyn.TokenizeLine;
function IsToken(const AText: String; out AAttr: TAttr): Boolean;
var
lAttr: TAttr;
FirstChar, C: Char;
IsSameChar: Boolean = True;
begin
// exact matches first
for lAttr in TAttr do
begin
if AText = Tokens[lAttr] then
begin
AAttr:=lAttr;
Exit(True);
end;
end;
FirstChar := AText[1];
for C in AText do
begin
IsSameChar := IsSameChar and (C = FirstChar);
if not IsSameChar then break;
end;
if IsSameChar then
begin // check for var length
for lAttr in TAttr do
begin
if TokenIsVarLength[lAttr] and (Pos(Tokens[lAttr], AText) > 0) then
begin
AAttr:=lAttr;
Exit(True);
end;
end;
end;
if AText = '+' then
begin
AAttr:=atUnorderedList;
Exit(True);
end;
if AText = '_' then
begin
AAttr:=atItalic;
Exit(True);
end;
Result := False;
end;
var
lAttr: TAttr;
lPos: Integer = 1;
l, lLen, i, j, lDotCount: Integer;
C: Char;
lToken: String;
lTokenInstance: TAttrInstance;
begin
FLineTokens.Clear;
l := Length(FLineText);
while lPos <= l do
begin
lDotCount := 0;
C := FLineText[lPos];
lLen := 0;
if SpecialChars.Contains(C) then
begin
while (lPos+lLen <= l) and (FLineText[lPos+lLen] = C) do
begin
Inc(lLen);
end;
lToken := Copy(FLineText, lPos, lLen);
if IsToken(lToken, lAttr) then
FLineTokens.Add(TAttrInstance.Create(lAttr, @FLineText[lPos], lLen))
else
FLineTokens.Add(TAttrInstance.Create(atText, @FLineText[lPos], lLen));
Inc(lPos, lLen);
end
else if C in ['0'..'9', '.'] then
begin
// For ordered lists
while (lPos+lLen <= l) and (FLineText[lPos+lLen] in ['0'..'9', '.']) do
begin
if FLineText[lPos+lLen] = '.' then
Inc(lDotCount);
Inc(lLen);
end;
if (lLen > 1) and (lDotCount = 1) and (FLineText[lPos+lLen-1] = '.') then
FLineTokens.Add(TAttrInstance.Create(atOrderedList, @FLineText[lPos], lLen))
else
FLineTokens.Add(TAttrInstance.Create(atText, @FLineText[lPos], lLen));
Inc(lPos, lLen);
end
else
begin
while (lPos+lLen <= l)
and (not SpecialChars.Contains(FLineText[lPos+lLen]))
and (not (FLineText[lPos+lLen] in ['0'..'9', '.'])) do
begin
Inc(lLen);
end;
FLineTokens.Add(TAttrInstance.Create(atText, @FLineText[lPos], lLen));
Inc(lPos, lLen);
end;
end;
for i := 0 to FLineTokens.Count -1 do
begin
lTokenInstance := FLineTokens[i];
if (FLineTokens.Count > 1) and (lTokenInstance.Attr in [atHorizontalRule, atHeaderAlt]) then
lTokenInstance.Attr := atText; // only valid if it's the only token on the line
if TokenIsPair[lTokenInstance.Attr] and not lTokenInstance.Matched then
begin
for j := i+1 to FLineTokens.Count -1 do
begin
if (FLineTokens[j].Attr = lTokenInstance.Attr) and not FLineTokens[j].Matched then
begin
lTokenInstance.Matched:=True;
FLineTokens[j].Matched:=True;
Break;
end;
end;
if not lTokenInstance.Matched and (i = 0) and (lTokenInstance.Text = '*') then
lTokenInstance.Attr:=atUnorderedList;
end;
if (lTokenInstance.Attr = atIndentedCode) and (i < FLineTokens.Count -2) and not (FLineTokens[i+1].Attr = atText) then
lTokenInstance.Attr:=atText;
end;
end;
function TSynMarkdownSyn.Token: TAttrInstance;
begin
if GetEol then Exit(nil);
Result := FLineTokens[FTokenIndex];
end;
procedure TSynMarkdownSyn.AddCodeFoldPoint(AToken: TAttrInstance);
var
lBlock: TSynCustomCodeFoldBlock;
begin
lBlock := StartCodeFoldBlock(Pointer(Ord(AToken.Attr)));
end;
function TSynMarkdownSyn.GetRangeClass: TSynCustomHighlighterRangeClass;
begin
Result := TSynMarkdownRange;
end;
function TSynMarkdownSyn.GetRange: Pointer;
begin
Result :=inherited GetRange;
with TSynMarkdownRange(Result) do
begin
IsInFencedCodeBlock:=rCode in FRange;
end;
end;
procedure TSynMarkdownSyn.SetRange(Value: Pointer);
begin
inherited SetRange(Value);
with TSynMarkdownRange(Value) do
begin
if IsInFencedCodeBlock then
begin
Include(FRange, rCode);
end
else
Exclude(FRange, rCode);
end;
end;
procedure TSynMarkdownSyn.ResetRange;
begin
inherited ResetRange;
Exclude(FRange, rCode);
SynRange.IsInFencedCodeBlock:=False;
end;
class function TSynMarkdownSyn.GetLanguageName: string;
begin
Result:='Markdown';
end;
function TSynMarkdownSyn.GetSampleSource: string;
begin
Result :=
'# Heading 1'+LineEnding+
'## Heading 2'+LineEnding+
'Something *italic* and **bold** and not something that is'+LineEnding+
'~~completed~~ or ==highlighted== or ***really bold***.'+LineEnding+
'With some code `programming = fun` (hopefully) '+LineEnding+
'- Concise'+LineEnding+
'- Specific'+LineEnding+
'## Indented code'+LineEnding+
' a := b;'+LineEnding+
' b := 1;'+LineEnding+
'----------------------------------------------------------'+LineEnding+
'> Block quote'+LineEnding+
'> Regards';
end;
procedure TSynMarkdownSyn.SetLine(const NewValue: String; LineNumber: Integer);
begin
inherited;
FActiveAttrs:=[];
FCurrentAttr.Assign(FAttrs[atText]);
FTokenIndex:=-1;
FLineText := NewValue;
FLineNumber := LineNumber;
TokenizeLine;
Next;
end;
procedure TSynMarkdownSyn.Next;
begin
if FTokenIndex >= FLineTokens.Count then
Exit;
Inc(FTokenIndex);
if GetEol then
begin
Exit;
end;
if Token.Attr in cHeaderAttr then
begin
//lRange := SynRange;
EndCodeFoldBlock();
AddCodeFoldPoint(Token);
end;
if (rCode in FRange) and (Token.Attr <> atFencedCode) then
Token.Attr:=atText;
{$IFDEF TRYMULTILINE}
if (Token.Attr = atFencedCode) and not (Token.Matched) and IsScanning then
begin
if not(rCode in FRange) then
Include(FRange, rCode)
else
Exclude(Frange,rCode);
end;
{$ENDIF}
end;
function TSynMarkdownSyn.GetEol: Boolean;
begin
//Result := FTokenPos > length(FLineText);
Result := FTokenIndex >= FLineTokens.Count;
end;
procedure TSynMarkdownSyn.GetTokenEx(out TokenStart: PChar; out
TokenLength: integer);
begin
TokenStart := Token.Start;
TokenLength := Token.Length;
end;
operator and (A, B: TSynMarkdownSyn.TAttrs): TSynMarkdownSyn.TAttrs;
var
e: TSynMarkdownSyn.TAttr;
begin
Result := [];
for e in A do
if e in B then
Include(Result,e);
end;
function TSynMarkdownSyn.GetTokenAttribute: TSynHighlighterAttributes;
var
lAdd: TAttrs = [];
lToken: TAttr;
begin
lToken := Token.Attr;
if TokenIsFirst[lToken]
and ((FTokenIndex = 0) or ((FTokenIndex = 1) and FLineTokens[0].IsWhitespace))
then
// some tokens are only valid if they are the first on a line. whitespace is ok before it.
begin
Include(lAdd, lToken);
if lToken in [atHorizontalRule, atHeaderAlt] then // highlight this immediately and not on the following token
Include(FActiveAttrs, ltoken);
end
else if not TokenIsFirst[lToken] then
// the token can be any token that isn't required to be first on a line.
begin
if not (lToken in FActiveAttrs) and
(not TokenIsPair[lToken] or (Token.Matched)) then
begin
Include(lAdd, ltoken) // the next toke will use this
end
else if (lToken in FActiveAttrs) and Token.Matched then
begin
Exclude(FActiveAttrs, lToken); // don't format closing matched symbol
end;
end;
{$IFDEF TRYMULTILINE}
if (rCode in FRange){ and (SynRange.IsInFencedCodeBlock) }then
Include(FActiveAttrs, atFencedCode);
{$ENDIF}
if (FActiveAttrs = []) then
Result := TextAttr
else
begin
if ([atH1, atHeaderAlt] and FActiveAttrs <> []) then FCurrentAttr.Assign(FAttrs[atH1])
else if (atH2 in FActiveAttrs) then FCurrentAttr.Assign(FAttrs[atH2])
else if (atH3 in FActiveAttrs) then FCurrentAttr.Assign(FAttrs[atH3])
else if (atEmoji in FActiveAttrs) then FCurrentAttr.Assign(FAttrs[atEmoji])
else if (atBlockQuote in FActiveAttrs) then FCurrentAttr.Assign(FAttrs[atBlockQuote])
else if (atCode in FActiveAttrs) then FCurrentAttr.Assign(FAttrs[atCode])
else if (atFencedCode in FActiveAttrs) and (not (Token.Attr = atFencedCode)) then FCurrentAttr.Assign(FAttrs[atFencedCode])
else if (atHighlight in FActiveAttrs) then FCurrentAttr.Assign(FAttrs[atHighlight])
else if (atUnorderedList in FActiveAttrs) then FCurrentAttr.Assign(FAttrs[atUnorderedList])
else if (atHorizontalRule in FActiveAttrs) then FCurrentAttr.Assign(FAttrs[atHorizontalRule])
else if (atOrderedList in FActiveAttrs) then FCurrentAttr.Assign(FAttrs[atOrderedList])
else if (atIndentedCode in FActiveAttrs) then FCurrentAttr.Assign(FAttrs[atCode])
else FCurrentAttr.Assign(FAttrs[atText]);
if (atItalic in FActiveAttrs) then FCurrentAttr.Style := FCurrentAttr.Style + FAttrs[atItalic].Style;
if (atBold in FActiveAttrs) then FCurrentAttr.Style := FCurrentAttr.Style + FAttrs[atBold].Style;
if (atStrikethrough in FActiveAttrs) then FCurrentAttr.Style := FCurrentAttr.Style + FAttrs[atStrikethrough].Style;
if (atStrongBold in FActiveAttrs) then FCurrentAttr.Style := FCurrentAttr.Style + FAttrs[atStrongBold].Style;
if (atUnderline in FActiveAttrs) then FCurrentAttr.Style := FCurrentAttr.Style + FAttrs[atUnderline].Style;
//if (rCode in FRange) and (Token.Attr <> atFencedCode) then
end;
if (Token.Attr <> atText) and (Token.Matched) then
FCurrentAttr.Foreground:=clLtGray;
Result := FCurrentAttr;
FActiveAttrs+=lAdd;
end;
function TSynMarkdownSyn.SynRange: TSynMarkdownRange;
begin
Result := TSynMarkdownRange(GetRange);
end;
function TSynMarkdownSyn.GetToken: String;
begin
Result := Token.Text;
end;
function TSynMarkdownSyn.GetTokenPos: Integer;
begin
Result := Token.Start-@FLineText[1];
end;
function TSynMarkdownSyn.GetTokenKind: integer;
begin
// Map Attribute into a unique number
Result := ord(Token.Attr)
end;
function TSynMarkdownSyn.GetDefaultAttribute(Index: integer
): TSynHighlighterAttributes;
begin
// Some default attributes
case Index of
SYN_ATTR_COMMENT: Result := FAttrs[atItalic];
SYN_ATTR_IDENTIFIER: Result := FAttrs[atText];
SYN_ATTR_WHITESPACE: Result := FAttrs[atText];
else Result := nil;
end;
end;
constructor TSynMarkdownSyn.Create(AOwner: TComponent);
var
lAttr: TAttr;
begin
inherited Create(AOwner);
FMultiLines := TMultiLineList.Create(True);
FLineTokens := specialize TFPGObjectList<TAttrInstance>.Create;
FCurrentAttr := TSynHighlighterAttributes.Create;
for lAttr := Low(TAttr) to High(TAttr) do
case lAttr of
atBlockQuote:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create('blockquote','blockquote');
AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Foreground:=clGreen;
end;
atBold:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create; // bold
//AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Style:=[fsBold];
end;
atStrongBold:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create('strongbold','strongbold');
AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Style:=[fsBold,fsUnderline];
end;
atCode:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create('code','code');
AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Foreground:=clBlack;
FAttrs[lAttr].Background:=clGrayText;
end;
atFencedCode:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create('code block','codeblock');
AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Foreground:=clBlack;
FAttrs[lAttr].Background:=clGrayText;
end;
atH1:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create('h1','h1');
AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Style:=[fsBold];
FAttrs[lAttr].Background:=clSkyBlue;
FAttrs[lAttr].Foreground:=clBlack;
FAttrs[lAttr].FrameEdges:=sfeAround;
end;
atH2:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create('h2','h2');
AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Style:=[fsBold];
FAttrs[lAttr].FrameEdges:=sfeAround;
end;
atH3:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create('h3','h3');
AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Style:=[fsBold];
FAttrs[lAttr].FrameEdges:=sfeAround;
end;
atHighlight:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create('highlight','highlight');
AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Background:=clHighlight;
FAttrs[lAttr].Foreground:=clHighlightText;
end;
atItalic:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create; // Italic
//AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Style:=[fsItalic];
end;
atStrikethrough:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create; // Strikethrough
//AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Style:=[fsStrikeOut];
end;
atText:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create('text','text');
AddAttribute(FAttrs[lAttr]);
end;
atUnorderedList:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create('unorderedlist','unorderedlist');
AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Foreground:=clBlue;
end;
atHorizontalRule:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create('horizontalrule','horizontalrule');
AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Background:=clSilver;
FAttrs[lAttr].Foreground:=clSilver+1; // basically invisible
end;
atEmoji:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create('emoji','emoji');
AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Background:=clWhite;
FAttrs[lAttr].Foreground:=clGray;
end;
atOrderedList:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create('orderedlist','orderedlist');
AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Assign(FAttrs[atUnorderedList]);
end;
atUnderline:
begin
FAttrs[lAttr] := TSynHighlighterAttributes.Create; // underline
//AddAttribute(FAttrs[lAttr]);
FAttrs[lAttr].Style:=[fsUnderline];
end;
atIndentedCode:
begin
FAttrs[lAttr] := FAttrs[atCode]; // just link it to code
end;
atHeaderAlt:
begin
FAttrs[lAttr] := FAttrs[atH1]; // link to h1
end
else
// yes I know this will cause a format error too. It's good. all attributes must be defined
raise Exception.CreateFmt('attribute not created or assigned! %s', [lAttr]);
end;
SetAttributesOnChange(@DefHighlightChange);
end;
destructor TSynMarkdownSyn.Destroy;
begin
FCurrentAttr.Free;
FMultiLines.Free;
FLineTokens.Free;
inherited Destroy;
end;
end.
--
_______________________________________________
lazarus mailing list
lazarus@lists.lazarus-ide.org
https://lists.lazarus-ide.org/listinfo/lazarus