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

Reply via email to