{ *************************************************************************** }
{                                                                             }
{ EControl Syntax Editor SDK                                                  }
{                                                                             }
{ Copyright (c) 2004 - 2015 EControl Ltd., Zaharov Michael                    }
{     www.econtrol.ru                                                         }
{     support@econtrol.ru                                                     }
{                                                                             }
{ *************************************************************************** }

{$mode delphi}

unit ecSyntAnal;

interface

uses
  Classes, Graphics, Controls, ExtCtrls,
  Contnrs, SyncObjs,
  LazUTF8Classes, //TFileStreamUTF8
  eczRegExpr,
  ecStrUtils,
  ecLists,
  ecSyntGramma,
  ATStringProc_TextBuffer,
  proc_StreamComponent;

type
  IecSyntClient = interface
    ['{045EAD6D-5584-4A60-849E-6B8994AA5B8F}']
    procedure FormatChanged; // Lexer properties changed (update without clear)
    procedure Finished;      // Compleat analysis
  end;

  TecLineBreakPos = (lbTop, lbBottom);
  TecLineBreakBound = set of TecLineBreakPos; // for user blocks
  TecVertAlignment = (vaTop, vaCenter, vaBottom);
  TecFormatType = (ftCustomFont, // Any customizing
                   ftFontAttr,   // Except custom font
                   ftColor,      // Any color
                   ftBackGround);// Only background color

  TecSyntAnalyzer       = class;
  TecParserResults      = class;
  TecClientSyntAnalyzer = class;
  TecTagBlockCondition  = class;
  TecSyntaxManager      = class;
  TecSyntaxFormat       = class;
  TecSubAnalyzerRule    = class;
  TecTextRange        = class;

  TOnMatchToken = procedure(Sender: TObject; Client: TecParserResults;
      const Text: ecString; APos: integer; var MatchLen: integer) of object;
  TOnBlockCheck = procedure(Sender: TObject; Client: TecClientSyntAnalyzer;
      const Text: ecString; var RefIdx: integer; var Accept: Boolean) of object;

  TBoundDefEvent = procedure(Sender: TecClientSyntAnalyzer; Range: TecTextRange; var sIdx, eIdx: integer) of object;

  TSyntCollectionItem = class(TCollectionItem)
  private
    FName: string;
    FEnabled: Boolean;
    procedure SetEnabled(const Value: Boolean);
  protected
    procedure AssignTo(Dest: TPersistent); override;
    function GetItemBaseName: string; virtual;
    function GetDisplayName: string; override;
    procedure SetDisplayName(const Value: string); override;
    procedure Loaded; virtual;
    function GetIsInvalid: Boolean; virtual;
  public
    constructor Create(Collection: TCollection); override;
    property IsInvalid: Boolean read GetIsInvalid;
  published
    property DisplayName;
    property Enabled: Boolean read FEnabled write SetEnabled default True;
  end;

  TSyntItemChanged = procedure(Sender: TCollection; Item: TSyntCollectionItem) of object;

  TSyntCollection = class(TCollection)
  private
    FSyntOwner: TecSyntAnalyzer;
    FOnChange: TSyntItemChanged;
    function GetItems(Index: integer): TSyntCollectionItem;
  protected
    procedure Update(Item: TCollectionItem); override;
    function  GetOwner: TPersistent; override;
    procedure Loaded;
  public
    constructor Create(ItemClass: TCollectionItemClass);
    function ItemByName(const AName: string): TSyntCollectionItem;
    function ValidItem(Item: TSyntCollectionItem): Boolean;
    function GetUniqueName(const Base: string): string;

    property SyntOwner: TecSyntAnalyzer read FSyntOwner write FSyntOwner;
    property Items[Index: integer]: TSyntCollectionItem read GetItems; default;
    property OnChange: TSyntItemChanged read FOnChange write FOnChange;
  end;

  TRuleCollectionItem = class(TSyntCollectionItem)
  private
    FStyleName: string;
    FBlockName: string;
    FFormat: TecSyntaxFormat;
    FBlock: TecTagBlockCondition;
    FStrictParent: Boolean;
    FNotParent: Boolean;
    FAlwaysEnabled: Boolean;
    FStatesAbsent: integer;
    FStatesAdd: integer;
    FStatesRemove: integer;
    FStatesPresent: integer;
    function GetStyleName: string;
    procedure SetStyleName(const Value: string);
    function GetBlockName: string;
    procedure SetBlockName(const Value: string);
    procedure SetNotParent(const Value: Boolean);
    procedure SetStrictParent(const Value: Boolean);
    procedure SetAlwaysEnabled(const Value: Boolean);
    function GetSyntOwner: TecSyntAnalyzer;
    procedure SetStatesAdd(const Value: integer);
    procedure SetStatesAbsent(const Value: integer);
    procedure SetStatesRemove(const Value: integer);
    procedure SetStatesPresent(const Value: integer);
  protected
    function ValidStyleName(const AStyleName: string; AStyle: TecSyntaxFormat): string;
    function ValidSetStyle(const AStyleName: string; var AStyleField: string; var AStyle: TecSyntaxFormat): string;
    procedure AssignTo(Dest: TPersistent); override;
    procedure Loaded; override;
  public
    property Style: TecSyntaxFormat read FFormat write FFormat;
    property Block: TecTagBlockCondition read FBlock write FBlock;
    property SyntOwner: TecSyntAnalyzer read GetSyntOwner;
  published
    property StyleName: string read GetStyleName write SetStyleName;
    property BlockName: string read GetBlockName write SetBlockName;
    property StrictParent: Boolean read FStrictParent write SetStrictParent default False;
    property NotParent: Boolean read FNotParent write SetNotParent default False;
    property AlwaysEnabled: Boolean read FAlwaysEnabled write SetAlwaysEnabled default False;
    property StatesAdd: integer read FStatesAdd write SetStatesAdd default 0;
    property StatesRemove: integer read FStatesRemove write SetStatesRemove default 0;
    property StatesPresent: integer read FStatesPresent write SetStatesPresent default 0;
    property StatesAbsent: integer read FStatesAbsent write SetStatesAbsent default 0;
  end;

// *******************************************************************
//  Format for syntax output
// *******************************************************************
  TecBorderLineType = (blNone, blSolid, blDash, blDot, blDashDot, blDashDotDot,
                     blSolid2, blSolid3, blWavyLine, blDouble);
  TecFormatFlag = (ffBold, ffItalic, ffUnderline, ffStrikeOut, ffReadOnly,
                 ffHidden, ffFontName, ffFontSize, ffFontCharset, ffVertAlign);
  TecFormatFlags = set of TecFormatFlag;

  TecChangeCase = (ccNone, ccUpper, ccLower, ccToggle, ccTitle);

  TecSyntaxFormat = class(TSyntCollectionItem)
  private
    FIsBlock: Boolean;
    FFont: TFont;
    FBgColor: TColor;
    FVertAlign: TecVertAlignment;
    FFormatType: TecFormatType;
    FOnChange: TNotifyEvent;
    FHidden: Boolean;
    FBorderTypes: array[0..3] of TecBorderLineType;
    FBorderColors: array[0..3] of TColor;
    FMultiLineBorder: Boolean;
    FReadOnly: Boolean;
    FChangeCase: TecChangeCase;
    FFormatFlags: TecFormatFlags;
    procedure SetFont(const Value: TFont);
    procedure SetBgColor(const Value: TColor);
    procedure FontChanged(Sender: TObject);
    procedure SetVertAlign(const Value: TecVertAlignment);
    procedure SetFormatType(const Value: TecFormatType);
    procedure SetHidden(const Value: Boolean);
    function GetBorderColor(Index: Integer): TColor;
    function GetBorderType(Index: Integer): TecBorderLineType;
    procedure SetBorderColor(Index: Integer; const Value: TColor);
    procedure SetBorderType(Index: Integer;
      const Value: TecBorderLineType);
    procedure SetMultiLineBorder(const Value: Boolean);
    procedure SetReadOnly(const Value: Boolean);
    procedure SetChangeCase(const Value: TecChangeCase);
    procedure SetFormatFlags(const Value: TecFormatFlags);
    function GetHidden: Boolean;
  protected
    procedure AssignTo(Dest: TPersistent); override;
    function GetItemBaseName: string; override;
    procedure Change; dynamic;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
    function HasBorder: Boolean;

    procedure ApplyTo(Canvas: TCanvas; AllowChangeFont: Boolean = True);

    function IsEqual(Other: TecSyntaxFormat): Boolean;
    // Merges style above this style
    procedure Merge(Over: TecSyntaxFormat);
    // Save only common properties
    procedure Intersect(Over: TecSyntaxFormat);

    property OnChange: TNotifyEvent read FOnChange write FOnChange;
    property BorderTypes[Index: integer]: TecBorderLineType read GetBorderType write SetBorderType;
    property BorderColors[Index: integer]: TColor read GetBorderColor write SetBorderColor;
  published
    property Font: TFont read FFont write SetFont;
    property BgColor: TColor read FBgColor write SetBgColor default clNone;
    property VertAlignment: TecVertAlignment read FVertAlign write SetVertAlign default vaCenter;
    property FormatType: TecFormatType read FFormatType write SetFormatType default ftFontAttr;
    property Hidden: Boolean read GetHidden write SetHidden default False;
    property BorderTypeLeft: TecBorderLineType index 0 read GetBorderType write SetBorderType default blNone;
    property BorderColorLeft: TColor index 0 read GetBorderColor write SetBorderColor default clBlack;
    property BorderTypeTop: TecBorderLineType index 1 read GetBorderType write SetBorderType default blNone;
    property BorderColorTop: TColor index 1 read GetBorderColor write SetBorderColor default clBlack;
    property BorderTypeRight: TecBorderLineType index 2 read GetBorderType write SetBorderType default blNone;
    property BorderColorRight: TColor index 2 read GetBorderColor write SetBorderColor default clBlack;
    property BorderTypeBottom: TecBorderLineType index 3 read GetBorderType write SetBorderType default blNone;
    property BorderColorBottom: TColor index 3 read GetBorderColor write SetBorderColor default clBlack;
    property MultiLineBorder: Boolean read FMultiLineBorder write SetMultiLineBorder default False;
    property ReadOnly: Boolean read FReadOnly write SetReadOnly default False;
    property ChangeCase: TecChangeCase read FChangeCase write SetChangeCase default ccNone;
    property FormatFlags: TecFormatFlags read FFormatFlags write SetFormatFlags
                 default [ffBold, ffItalic, ffUnderline, ffStrikeOut, ffReadOnly,
                          ffHidden, ffFontName, ffFontSize, ffFontCharset, ffVertAlign];
  end;

  TecStylesCollection = class(TSyntCollection)
  private
    function GetItem(Index: integer): TecSyntaxFormat;
  public
    function Synchronize(Source: TecStylesCollection): integer;
    constructor Create;
    function Add: TecSyntaxFormat;
    property Items[Index: integer]: TecSyntaxFormat read GetItem; default;
  end;

// *******************************************************************
// description classes of text contents
// *******************************************************************

  TecSyntToken = class(TRange)
  private
    FTokenType: integer;
    FRule: TRuleCollectionItem;
    function GetStyle: TecSyntaxFormat;
  public
    constructor Create(ARule: TRuleCollectionItem; AStartPos, AEndPos: integer);
    function GetStr(const Source: ecString): ecString;
    property TokenType: integer read FTokenType;
    property Rule: TRuleCollectionItem read FRule;
    property Style: TecSyntaxFormat read GetStyle;
  end;

  TecLineBreak = class
  private
    FRefTag: integer;
    FLine: integer;
    FRule: TecTagBlockCondition;
  public
    property Rule: TecTagBlockCondition read FRule;
    property Line: integer read FLine;
    property RefIdx: integer read FRefTag;
  end;

  TecLineBreakRange = class(TRange)
  private
    FRule: TecTagBlockCondition;
  public
    property Rule: TecTagBlockCondition read FRule;
  end;

  TecTextRange = class(TSortedItem)
  private
    FRule: TecTagBlockCondition;
    FStart, FEnd, FIdent: integer;
    FStartPos: integer;
    FParent: TecTextRange;
    FCondIndex: integer;
    FEndCondIndex: integer;
    FIndex: integer;
    function GetLevel: integer;
    function GetIsClosed: Boolean;
  protected
    function GetKey: integer; override;
  public
    constructor Create(AStartIdx, AStartPos: integer);
    function IsParent(Range: TecTextRange): Boolean;

    property Rule: TecTagBlockCondition read FRule;
    property StartIdx: integer read FStart;
    property EndIdx: integer read FEnd;
    property IdentIdx: integer read FIdent;
    property Parent: TecTextRange read FParent;
    property Level: integer read GetLevel;
    property Index: integer read FIndex;
    property StartPos: Integer read FStartPos;

    property IsClosed: Boolean read GetIsClosed;
  end;

  TecSubLexerRange = class(TRange)
  private
    FRule: TecSubAnalyzerRule;   // Rule reference
    FCondEndPos: integer;      // Start pos of the start condition
    FCondStartPos: integer;    // End pos of the end condition
  public
    property Rule: TecSubAnalyzerRule read FRule;
    property CondStartPos: integer read FCondStartPos;
    property CondEndPos: integer read FCondEndPos;
  end;

// *******************************************************************
//                Rules for syntax interpretation
// *******************************************************************

  TecTagConditionType = (tcEqual, tcNotEqual, tcMask, tcSkip, tcStrictMask);

  TecSingleTagCondition = class(TCollectionItem)
  private
    FTagList: TStrings;
    FCondType: TecTagConditionType;
    FTokenTypes: DWORD;
    procedure SetTagList(const Value: TStrings);
    procedure SetIgnoreCase(const Value: Boolean);
    procedure SetTokenTypes(const Value: DWORD);
    procedure SetCondType(const Value: TecTagConditionType);
    procedure TagListChanged(Sender: TObject);
    function GetIgnoreCase: Boolean;
  protected
    procedure AssignTo(Dest: TPersistent); override;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
    function CheckToken(const Source: ecString; Token: TecSyntToken): Boolean;
  published
    property TagList: TStrings read FTagList write SetTagList;
    property CondType: TecTagConditionType read FCondType write SetCondType default tcEqual;
    property TokenTypes: DWORD read FTokenTypes write SetTokenTypes default 0;
    property IgnoreCase: Boolean read GetIgnoreCase write SetIgnoreCase default False;
  end;

  TecConditionCollection = class(TCollection)
  private
    FOwner: TecTagBlockCondition;
    FOnChange: TNotifyEvent;
    function GetItem(Index: integer): TecSingleTagCondition;
  protected
    procedure Update(Item: TCollectionItem); override;
    function  GetOwner: TPersistent; override;
  public
    constructor Create(AOwner: TecTagBlockCondition);
    function Add: TecSingleTagCondition;
    property Items[Index: integer]: TecSingleTagCondition read GetItem; default;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
  end;

  TecTagBlockType = (btTagDetect, btLineBreak, btRangeStart, btRangeEnd);
  TecHighlightPos = (cpAny, cpBound, cpBoundTag, cpRange, cpBoundTagBegin, cpOutOfRange);
  TecDynamicHighlight = (dhNone, dhBound, dhRangeNoBound, dhRange);
  TecAutoCloseMode = (acmDisabled, acmCloseNearest, acmCloseOpened);

  TecTagBlockCondition = class(TRuleCollectionItem)
  private
    FConditions: TecConditionCollection;
    FIdentIndex: integer;
    FLinePos: TecLineBreakPos;
    FBlockOffset: integer;
    FBlockEndCond: TecTagBlockCondition;
    FBlockType: TecTagBlockType;
    FBlockEndName: string;
    FEndOfTextClose: Boolean;
    FNotCollapsed: Boolean;
    FSameIdent: Boolean;
    FInvertColors: Boolean;
    FHighlight: Boolean;
    FDisplayInTree: Boolean;
    FNameFmt: ecString;
    FGroupFmt: ecString;
    FRefToCondEnd: Boolean;
    FDynHighlight: TecDynamicHighlight;
    FHighlightPos: TecHighlightPos;
    FDynSelectMin: Boolean;
    FCancelNextRules: Boolean;
    FOnBlockCheck: TOnBlockCheck;
    FDrawStaple: Boolean;
    FGroupIndex: integer;
    FCollapseFmt: ecString;
    FSelfClose: Boolean;
    FNoEndRule: Boolean;
    FGrammaRuleName: string;
    FGrammaRule: TParserRule;
    FTokenType: integer;
    FTreeItemStyle: string;
    FTreeItemStyleObj: TecSyntaxFormat;
    FTreeGroupStyle: string;
    FTreeGroupStyleObj: TecSyntaxFormat;
    FTreeGroupImage: integer;
    FTreeItemImage: integer;
    FUseCustomPen: Boolean;
    FPen: TPen;
    FIgnoreAsParent: Boolean;
    FAutoCloseText: ecString;
    FAutoCloseMode: TecAutoCloseMode;
    procedure ConditionsChanged(Sender: TObject);
    function GetBlockEndName: string;
    procedure SetBlockEndName(const Value: string);
    procedure SetBlockType(const Value: TecTagBlockType);
    procedure SetConditions(const Value: TecConditionCollection);
    procedure SetBlockEndCond(const Value: TecTagBlockCondition);
    procedure SetLinePos(const Value: TecLineBreakPos);
    procedure SetIdentIndex(const Value: integer);
    procedure SetBlockOffset(const Value: integer);
    procedure SetEndOfTextClose(const Value: Boolean);
    procedure SetNotCollapsed(const Value: Boolean);
    procedure SetSameIdent(const Value: Boolean);
    procedure SetHighlight(const Value: Boolean);
    procedure SetInvertColors(const Value: Boolean);
    procedure SetDisplayInTree(const Value: Boolean);
    procedure SetCancelNextRules(const Value: Boolean);
    procedure SetDynHighlight(const Value: TecDynamicHighlight);
    procedure SetDynSelectMin(const Value: Boolean);
    procedure SetGroupFmt(const Value: ecString);
    procedure SetHighlightPos(const Value: TecHighlightPos);
    procedure SetNameFmt(const Value: ecString);
    procedure SetRefToCondEnd(const Value: Boolean);
    procedure SetDrawStaple(const Value: Boolean);
    procedure SetCollapseFmt(const Value: ecString);
    procedure SetSelfClose(const Value: Boolean);
    procedure SetNoEndRule(const Value: Boolean);
    procedure SetGrammaRuleName(const Value: string);
    procedure SetTokenType(const Value: integer);
    function GetTreeItemStyle: string;
    procedure SetTreeItemStyle(const Value: string);
    function GetTreeGroupStyle: string;
    procedure SetTreeGroupStyle(const Value: string);
    procedure SetTreeGroupImage(const Value: integer);
    procedure SetTreeItemImage(const Value: integer);
    procedure SetPen(const Value: TPen);
    procedure SetUseCustomPen(const Value: Boolean);
    procedure SetIgnoreAsParent(const Value: Boolean);
    procedure SetAutoCloseText(Value: ecString);
    procedure SetAutoCloseMode(const Value: TecAutoCloseMode);
  protected
    procedure AssignTo(Dest: TPersistent); override;
    function GetItemBaseName: string; override;
    procedure Loaded; override;
    function CheckOffset: integer;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
    function Check(const Source: ecString; Tags: TecClientSyntAnalyzer;
                   N: integer;  var RefIdx: integer): Boolean;
    property BlockEndCond: TecTagBlockCondition read FBlockEndCond write SetBlockEndCond;
    property TreeItemStyleObj: TecSyntaxFormat read FTreeItemStyleObj;
    property TreeGroupStyleObj: TecSyntaxFormat read FTreeGroupStyleObj;
  published
    property BlockType: TecTagBlockType read FBlockType write SetBlockType default btRangeStart;
    property ConditionList: TecConditionCollection read FConditions write SetConditions;
    property IdentIndex: integer read FIdentIndex write SetIdentIndex default 0;
    property LinePos: TecLineBreakPos read FLinePos write SetLinePos default lbTop;
    property BlockOffset: integer read FBlockOffset write SetBlockOffset default 0;
    property BlockEnd: string read GetBlockEndName write SetBlockEndName;
    property EndOfTextClose: Boolean read FEndOfTextClose write SetEndOfTextClose default False;
    property NotCollapsed: Boolean read FNotCollapsed write SetNotCollapsed default False;
    property SameIdent: Boolean read FSameIdent write SetSameIdent default False;
    property Highlight: Boolean read FHighlight write SetHighlight default False;
    property InvertColors: Boolean read FInvertColors write SetInvertColors default False;
    property DisplayInTree: Boolean read FDisplayInTree write SetDisplayInTree default True;
    property NameFmt: ecString read FNameFmt write SetNameFmt;
    property GroupFmt: ecString read FGroupFmt write SetGroupFmt;
    property RefToCondEnd: Boolean read FRefToCondEnd write SetRefToCondEnd default False;
    property DynHighlight: TecDynamicHighlight read FDynHighlight write SetDynHighlight default dhNone;
    property HighlightPos: TecHighlightPos read FHighlightPos write SetHighlightPos;
    property DynSelectMin: Boolean read FDynSelectMin write SetDynSelectMin default False;
    property CancelNextRules: Boolean read FCancelNextRules write SetCancelNextRules default False;
    property DrawStaple: Boolean read FDrawStaple write SetDrawStaple default False;
    property GroupIndex: integer read FGroupIndex write FGroupIndex default 0;
    property CollapseFmt: ecString read FCollapseFmt write SetCollapseFmt;
    property OnBlockCheck: TOnBlockCheck read FOnBlockCheck write FOnBlockCheck;
    property SelfClose: Boolean read FSelfClose write SetSelfClose default False;
    // New in v2.20
    property NoEndRule: Boolean read FNoEndRule write SetNoEndRule default False;
    property GrammaRuleName: string read FGrammaRuleName write SetGrammaRuleName;
    property TokenType: integer read FTokenType write SetTokenType default -1;
    property TreeItemStyle: string read GetTreeItemStyle write SetTreeItemStyle;
    property TreeGroupStyle: string read GetTreeGroupStyle write SetTreeGroupStyle;
    property TreeItemImage: integer read FTreeItemImage write SetTreeItemImage default -1;
    property TreeGroupImage: integer read FTreeGroupImage write SetTreeGroupImage default -1;
    // New in 2.40
    property Pen: TPen read FPen write SetPen;
    property UseCustomPen: Boolean read FUseCustomPen write SetUseCustomPen default False;
    property IgnoreAsParent: Boolean read FIgnoreAsParent write SetIgnoreAsParent;
    // New in 2.50
    property AutoCloseMode: TecAutoCloseMode read FAutoCloseMode write SetAutoCloseMode default acmDisabled;
    property AutoCloseText: ecString read FAutoCloseText write SetAutoCloseText;
  end;

  TecBlockRuleCollection = class(TSyntCollection)
  private
    function GetItem(Index: integer): TecTagBlockCondition;
  public
    constructor Create;
    function Add: TecTagBlockCondition;
    property Items[Index: integer]: TecTagBlockCondition read GetItem; default;
  end;

  // Token identification rule
  TecTokenRule = class(TRuleCollectionItem)
  private
    FRegExpr: TecRegExpr;
    FTokenType: integer;
    FOnMatchToken: TOnMatchToken;
    FColumnTo: integer;
    FColumnFrom: integer;
    function GetExpression: ecString;
    procedure SetExpression(const Value: ecString);
    procedure SetTokenType(const Value: integer);
    procedure SetColumnFrom(const Value: integer);
    procedure SetColumnTo(const Value: integer);
  protected
    procedure AssignTo(Dest: TPersistent); override;
    function GetItemBaseName: string; override;
    function GetIsInvalid: Boolean; override;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
    function Match(const Source: ecString; Pos: integer): integer;
  published
    property TokenType: integer read FTokenType write SetTokenType default 0;
    property Expression: ecString read GetExpression write SetExpression;
    property ColumnFrom: integer read FColumnFrom write SetColumnFrom;
    property ColumnTo: integer read FColumnTo write SetColumnTo;
    property OnMatchToken: TOnMatchToken read FOnMatchToken write FOnMatchToken;
  end;

  TecTokenRuleCollection = class(TSyntCollection)
  private
    function GetItem(Index: integer): TecTokenRule;
  public
    constructor Create;
    function Add: TecTokenRule;
    property Items[Index: integer]: TecTokenRule read GetItem; default;
  end;

  TecSubAnalyzerRule = class(TRuleCollectionItem)
  private
    FStartRegExpr: TecRegExpr;
    FEndRegExpr: TecRegExpr;
    FSyntAnalyzer: TecSyntAnalyzer;
    FFromTextBegin: Boolean;
    FToTextEnd: Boolean;
    FIncludeBounds: Boolean;
    function GetEndExpression: string;
    function GetStartExpression: string;
    procedure SetEndExpression(const Value: string);
    procedure SetStartExpression(const Value: string);
    procedure SetSyntAnalyzer(const Value: TecSyntAnalyzer);
    procedure SetFromTextBegin(const Value: Boolean);
    procedure SetToTextEnd(const Value: Boolean);
    procedure SetIncludeBounds(const Value: Boolean);
  protected
    procedure AssignTo(Dest: TPersistent); override;
    function GetItemBaseName: string; override;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
    function MatchStart(const Source: ecString; Pos: integer): integer;
    function MatchEnd(const Source: ecString; Pos: integer): integer;
  published
    property StartExpression: string read GetStartExpression write SetStartExpression;
    property EndExpression: string read GetEndExpression write SetEndExpression;
    property SyntAnalyzer: TecSyntAnalyzer read FSyntAnalyzer write SetSyntAnalyzer;
    property FromTextBegin: Boolean read FFromTextBegin write SetFromTextBegin default False;
    property ToTextEnd: Boolean read FToTextEnd write SetToTextEnd default False;
    property IncludeBounds: Boolean read FIncludeBounds write SetIncludeBounds default False;
  end;

  TecSubAnalyzerRules = class(TSyntCollection)
  private
    function GetItem(Index: integer): TecSubAnalyzerRule;
  public
    constructor Create;
    function Add: TecSubAnalyzerRule;
    property Items[Index: integer]: TecSubAnalyzerRule read GetItem; default;
  end;

  { TecCodeTemplate }

  TecCodeTemplate = class(TCollectionItem)
  private
    FName: string;
    FDescription: string;
    FAdvanced: boolean;
    FCode: TStrings;
  protected
    function GetDisplayName: string; override;
  public
    constructor Create(Collection: TCollection); override;
    destructor Destroy; override;
  published
    property Name: string read FName write FName;
    property Description: string read FDescription write FDescription;
    property Advanced: Boolean read FAdvanced write FAdvanced;
    property Code: TStrings read FCode;
  end;

  TecCodeTemplates = class(TOwnedCollection)
  private
    function GetItem(Index: integer): TecCodeTemplate;
  public
    constructor Create(AOwner: TPersistent);
    function Add: TecCodeTemplate;
    property Items[Index: integer]: TecCodeTemplate read GetItem; default;
  end;

  TecChangeFixer = class
  private
    FList: TList;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Clear;
    procedure Add(Pos, Count: integer);
    function CurToOld(CurPos: integer): integer;
    function OldToCur(OldPos: integer): integer;
    function UpOldNoChange: integer;
  end;

// *******************************************************************
//  Parser classes
//
// *******************************************************************


// *******************************************************************
//  Syntax analizer for single client
//            container of description objects
// *******************************************************************
  TecParserResults = class(TTokenHolder)
  private
    FSrcProc: TATStringBuffer;
    FClient: IecSyntClient;
    FOwner: TecSyntAnalyzer;
    FFinished: Boolean;

    FSubLexerBlocks: TList;     // Sub Lexer Text Ranges
    FTagList: TRangeList;       // List of tokens
    FCurState: integer;
    FStateChanges: TList;
    function GetLastPos(const Source: ecString): integer;
    function ExtractTag(const Source: ecString; var FPos: integer; IsIdle: Boolean): Boolean;
    function GetTags(Index: integer): TecSyntToken;
    function GetSubLexerRangeCount: integer;
    function GetSubLexerRange(Index: integer): TecSubLexerRange;
  protected
    function GetTokenCount: integer; override;
    function GetTokenStr(Index: integer): ecString; override;
    function GetTokenType(Index: integer): integer; override;
    procedure CloseAtEnd(StartTagIdx: integer); virtual; abstract;
  protected
    FLastAnalPos: integer;
    procedure Finished; virtual;
    function IsEnabled(Rule: TRuleCollectionItem; OnlyGlobal: Boolean): Boolean; virtual;
    procedure ApplyStates(Rule: TRuleCollectionItem);
    procedure SaveState;
    procedure RestoreState;
  public
    constructor Create(AOwner: TecSyntAnalyzer; SrcProc: TATStringBuffer; const AClient: IecSyntClient); virtual;
    destructor Destroy; override;
    procedure Clear; virtual;

    function AnalyzerAtPos(Pos: integer): TecSyntAnalyzer;
    function ParserStateAtPos(TokenIndex: integer): integer;

    property Owner: TecSyntAnalyzer read FOwner;
    property IsFinished: Boolean read FFinished;
    property TagStr[Index: integer]: ecString read GetTokenStr;
    property TagCount: integer read GetTokenCount;
    property Tags[Index: integer]: TecSyntToken read GetTags; default;
    property SubLexerRangeCount: integer read GetSubLexerRangeCount;
    property SubLexerRanges[Index: integer]: TecSubLexerRange read GetSubLexerRange;
    property ParserState: integer read FCurState write FCurState;
  end;

  TecClientSyntAnalyzer = class(TecParserResults)
  private
    FLineBreaks: TList;

    FRanges: TSortedList;
    FOpenedBlocks: TSortedList;    // Opened ranges (without end)

    FBreakIdle: Boolean;
    FIdleProc: Boolean;
    FIdleTimer: TTimer;

    FSavedTags: TRangeList;            // saved tokens
    FChanges: TecChangeFixer;            // fixes all changes before objects will be updated
    FLineBreakRanges: TRangeCollection;// ranges maked with line breaks
    FDataAccess: TCriticalSection;
    FNextTokIndex: integer;
    FStartSepRangeAnal: integer;
    FDisableIdleAppend: Boolean;
    FRepeateAnalysis: Boolean;
    function GetRangeCount: integer;
    function GetRanges(Index: integer): TecTextRange;
    function GetTagPos(Index: integer): TPoint;
    function GetOpened(Index: integer): TecTextRange;
    function GetOpenedCount: integer;
    procedure SetDisableIdleAppend(const Value: Boolean);
  protected
    procedure AddLineBreak(lb: TecLineBreak);
    procedure AddRange(Range: TecTextRange);
    function HasOpened(Rule: TRuleCollectionItem; Parent: TecTagBlockCondition; Strict: Boolean): Boolean;
    function IsEnabled(Rule: TRuleCollectionItem; OnlyGlobal: Boolean): Boolean; override;
    procedure Finished; override;
    procedure IntIdleAppend(Sender: TObject);
    procedure CloseAtEnd(StartTagIdx: integer); override;

  public
    constructor Create(AOwner: TecSyntAnalyzer; SrcProc: TATStringBuffer; const AClient: IecSyntClient); override;
    destructor Destroy; override;
    procedure Clear; override;
    procedure ChangedAtPos(APos: integer);
    function GetLineBreak(Line: integer): TecLineBreakRange;
    function TokenAtPos(Pos: integer): integer;
    function PriorTokenAt(Pos: integer): integer;
    function NextTokenAt(Pos: integer): integer;

    function GetRangeBound(Range: TecTextRange): TPoint;
    function GetColRangeBound(Range: TecTextRange): TPoint;
    function RangeAtPos(APos: integer): TecTextRange;
    function RangeIdxAtPos(APos: integer): integer;
    function NearestRangeAtPos(APos: integer): TecTextRange;
    function NearestRangeIdxAtPos(APos: integer): integer;

    function RangeFormat(const FmtStr: ecString; Range: TecTextRange): ecString;
    function GetRangeName(Range: TecTextRange): ecString;
    function GetRangeGroup(Range: TecTextRange): ecString;
    function GetCollapsedText(Range: TecTextRange): ecString;
    function GetAutoCloseText(Range: TecTextRange; const Indent: string): ecString;

    procedure TextChanged(Pos, Count: integer);
    procedure TryAppend(APos: integer);   // Tries to analyze to APos
    procedure AppendToPos(APos: integer); // Requires analyzed to APos
    procedure Analyze(ResetContent: Boolean = True); // Requires analyzed all text
    procedure IdleAppend;                 // Start idle analysis

    procedure CompleteAnalysis;

    procedure Lock;
    procedure Unlock;

    function CloseRange(Cond: TecTagBlockCondition; RefTag: integer): Boolean;
    function CreateLineBreak(Rule: TecTagBlockCondition; RefTag: integer): Boolean;
    function DetectTag(Rule: TecTagBlockCondition; RefTag: integer): Boolean;

    property OpenCount: integer read GetOpenedCount;
    property Opened[Index: integer]: TecTextRange read GetOpened;

    property LineBreaks: TList read FLineBreaks;
    property RangeCount: integer read GetRangeCount;
    property Ranges[Index: integer]: TecTextRange read GetRanges;
    property TagPos[Index: integer]: TPoint read GetTagPos;
    property DisableIdleAppend: Boolean read FDisableIdleAppend write SetDisableIdleAppend;
  end;

// *******************************************************************
//  Syntax analizer
//            container of syntax rules
// *******************************************************************

  TLoadableComponent = class(TComponent)
  private
    FSkipNewName: Boolean;
    FFileName: string;
    FIgnoreAll: Boolean;
    FSaving: Boolean;
  protected
    procedure OnReadError(Reader: TReader; const Message: string;
                          var Handled: Boolean); virtual;
    function NotStored: Boolean;
  public
    procedure SaveToFile(const FileName: string); virtual;
    procedure SaveToStream(Stream: TStream); virtual;
    procedure LoadFromFile(const FileName: string); virtual;
    procedure LoadFromResourceID(Instance: Cardinal; ResID: Integer; ResType: string); virtual;
    procedure LoadFromResourceName(Instance: Cardinal; const ResName: string; ResType: string); virtual;
    procedure LoadFromStream(const Stream: TStream); virtual;
  protected
    procedure SetName(const NewName: TComponentName); override;
    property FileName: string read FFileName write LoadFromFile;
  end;

  TParseTokenEvent = procedure(Client: TecParserResults; const Text: ecString; Pos: integer;
      var TokenLength: integer; var Rule: TecTokenRule) of object;

  TecSyntAnalyzer = class(TLoadableComponent)
  private
    FClientList: TList;
    FMasters: TList;      // Master lexer, i.e. lexers that uses it
    FOnChange: TNotifyEvent;
    FSampleText: TStrings;
    FFormats: TecStylesCollection;
    FTokenRules: TecTokenRuleCollection;
    FBlockRules: TecBlockRuleCollection;
    FCodeTemplates: TecCodeTemplates;
    FExtentions: string;
    FLexerName: string;
    FCoping: Boolean;
    FSkipSpaces: Boolean;
    FSubAnalyzers: TecSubAnalyzerRules;
    FTokenTypeNames: TStrings;
    FFullRefreshSize: integer;

    FMarkedBlock: TecSyntaxFormat;
    FMarkedBlockName: string;
    FSearchMatch: TecSyntaxFormat;
    FSearchMatchName: string;
    FCurrentLine: TecSyntaxFormat;
    FCurrentLineName: string;
    FDefStyle: TecSyntaxFormat;
    FDefStyleName: string;
    FCollapseStyle: TecSyntaxFormat;
    FCollapseStyleName: string;
    FNotes: TStrings;
    FInternal: boolean;
    FRestartFromLineStart: Boolean;
    FParseEndOfLine: Boolean;
    FGrammaParser: TGrammaAnalyzer;
    FLineComment: ecString;
    FCharset: TFontCharSet;
    FSeparateBlocks: integer;
    FAlwaysSyncBlockAnal: Boolean;   // Indicates that blocks analysis may after tokens
    FOnGetCollapseRange: TBoundDefEvent;
    FOnCloseTextRange: TBoundDefEvent;
    FIdleAppendDelayInit: Cardinal;
    FIdleAppendDelay: Cardinal;
    FOnParseToken: TParseTokenEvent;
    procedure SetSampleText(const Value: TStrings);
    procedure FormatsChanged(Sender: TCollection; Item: TSyntCollectionItem);
    procedure TokenRuleChanged(Sender: TCollection; Item: TSyntCollectionItem);
    procedure BlocksChanged(Sender: TCollection; Item: TSyntCollectionItem);
    procedure SubLexRuleChanged(Sender: TCollection; Item: TSyntCollectionItem);
    procedure SetBlockRules(const Value: TecBlockRuleCollection);
    procedure SetCodeTemplates(const Value: TecCodeTemplates);
    procedure SetTokenRules(const Value: TecTokenRuleCollection);
    procedure SetFormats(const Value: TecStylesCollection);
    function GetUniqueName(const Base: string): string;
    procedure SetSkipSpaces(const Value: Boolean);
    procedure SetSubAnalyzers(const Value: TecSubAnalyzerRules);
    procedure SetTokenTypeNames(const Value: TStrings);

    function GetStyleName(const AName: string; const AStyle: TecSyntaxFormat): string;
    procedure SetMarkedBlock(const Value: TecSyntaxFormat);
    function GetMarkedBlockName: string;
    procedure SetMarkedBlockName(const Value: string);
    procedure SetSearchMatch(const Value: TecSyntaxFormat);
    function GetSearchMatchStyle: string;
    procedure SetSearchMatchStyle(const Value: string);
    procedure SetCurrentLine(const Value: TecSyntaxFormat);
    function GetCurrentLineStyle: string;
    procedure SetCurrentLineStyle(const Value: string);
    procedure SetNotes(const Value: TStrings);
    procedure SetInternal(const Value: boolean);
    procedure SetRestartFromLineStart(const Value: Boolean);
    procedure SetParseEndOfLine(const Value: Boolean);
    procedure TokenNamesChanged(Sender: TObject);
    procedure CompileGramma;
    procedure SetGrammar(const Value: TGrammaAnalyzer);
    procedure GrammaChanged(Sender: TObject);
    procedure SetDefStyle(const Value: TecSyntaxFormat);
    function GetDefaultStyleName: string;
    procedure SetDefaultStyleName(const Value: string);
    procedure SetLineComment(const Value: ecString);
    procedure DetectBlockSeparate;
    procedure SetAlwaysSyncBlockAnal(const Value: Boolean);
    function GetCollapseStyleName: string;
    procedure SetCollapseStyleName(const Value: string);
    procedure SetCollapseStyle(const Value: TecSyntaxFormat);
    function GetSeparateBlocks: Boolean;
  protected
    function GetToken(Client: TecParserResults; const Source: ecString;
                       APos: integer; OnlyGlobal: Boolean): TecSyntToken; virtual;
    procedure HighlightKeywords(Client: TecParserResults; const Source: ecString;
                       OnlyGlobal: Boolean); virtual;
    procedure SelectTokenFormat(Client: TecParserResults; const Source: ecString;
                       OnlyGlobal: Boolean; N: integer = -1); virtual;
    procedure Loaded; override;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure Change; dynamic;
    property SeparateBlockAnalysis: Boolean read GetSeparateBlocks;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    function AddClient(const Client: IecSyntClient; SrcProc: TATStringBuffer): TecClientSyntAnalyzer;
    procedure ClearClientContents;
    procedure UpdateClients;

    procedure AddMasterLexer(SyntAnal: TecSyntAnalyzer);
    procedure RemoveMasterLexer(SyntAnal: TecSyntAnalyzer);

    property MarkedBlock: TecSyntaxFormat read FMarkedBlock write SetMarkedBlock;
    property SearchMatch: TecSyntaxFormat read FSearchMatch write SetSearchMatch;
    property CurrentLine: TecSyntaxFormat read FCurrentLine write SetCurrentLine;
    property DefStyle: TecSyntaxFormat read FDefStyle write SetDefStyle;
    property CollapseStyle: TecSyntaxFormat read FCollapseStyle write SetCollapseStyle;
  published
    property Formats: TecStylesCollection read FFormats write SetFormats;
    property TokenRules: TecTokenRuleCollection read FTokenRules write SetTokenRules;
    property BlockRules: TecBlockRuleCollection read FBlockRules write SetBlockRules;
    property CodeTemplates: TecCodeTemplates read FCodeTemplates write SetCodeTemplates;
    property SubAnalyzers: TecSubAnalyzerRules read FSubAnalyzers write SetSubAnalyzers;
    property SampleText: TStrings read FSampleText write SetSampleText;

    property TokenTypeNames: TStrings read FTokenTypeNames write SetTokenTypeNames;
    property Gramma: TGrammaAnalyzer read FGrammaParser write SetGrammar;

    property MarkedBlockStyle: string read GetMarkedBlockName write SetMarkedBlockName;
    property SearchMatchStyle: string read GetSearchMatchStyle write SetSearchMatchStyle;
    property CurrentLineStyle: string read GetCurrentLineStyle write SetCurrentLineStyle;
    property DefaultStyleName: string read GetDefaultStyleName write SetDefaultStyleName;
    property CollapseStyleName: string read GetCollapseStyleName write SetCollapseStyleName;

    property Extentions: string read FExtentions write FExtentions;
    property LexerName: string read FLexerName write FLexerName;

    property SkipSpaces: Boolean read FSkipSpaces write SetSkipSpaces default True;
    property FullRefreshSize: integer read FFullRefreshSize write FFullRefreshSize default 0;
    property Notes: TStrings read FNotes write SetNotes;
    property Internal: boolean read FInternal write SetInternal default False;
    property RestartFromLineStart: Boolean read FRestartFromLineStart write SetRestartFromLineStart default False;
    property ParseEndOfLine: Boolean read FParseEndOfLine write SetParseEndOfLine default False;
    property LineComment: ecString read FLineComment write SetLineComment;
    property Charset: TFontCharSet read FCharset write FCharset; //AT
    property AlwaysSyncBlockAnal: Boolean read FAlwaysSyncBlockAnal write SetAlwaysSyncBlockAnal default False;
    property IdleAppendDelay: Cardinal read FIdleAppendDelay write FIdleAppendDelay default 200;
    property IdleAppendDelayInit: Cardinal read FIdleAppendDelayInit write FIdleAppendDelayInit default 50;

    property OnChange: TNotifyEvent read FOnChange write FOnChange;
    property OnGetCollapseRange: TBoundDefEvent read FOnGetCollapseRange write FOnGetCollapseRange;
    property OnCloseTextRange: TBoundDefEvent read FOnCloseTextRange write FOnCloseTextRange;
    property OnParseToken: TParseTokenEvent read FOnParseToken write FOnParseToken;
  end;

  TLibSyntAnalyzer = class(TecSyntAnalyzer)
  protected
    FParent: TecSyntaxManager;
//    function GetChildParent: TComponent; override;
//    procedure SetName(const NewName: TComponentName); override;
    procedure SetParentComponent(Value: TComponent); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure LoadFromStream(const Stream: TStream); override;
    function GetParentComponent: TComponent; override;
    function HasParent: Boolean; override;
  end;

  TecSyntaxManager = class(TLoadableComponent)
  private
    FOnChange: TNotifyEvent;
    FList: TList;
    FCurrentLexer: TecSyntAnalyzer;
    FOnLexerChanged: TNotifyEvent;
    FModified: Boolean;
    function GeItem(Index: integer): TecSyntAnalyzer;
    function GetCount: integer;
    procedure SetCurrentLexer(const Value: TecSyntAnalyzer);
  protected
    procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    procedure Changed; dynamic;
    procedure OnReadError(Reader: TReader; const Message: string;
                          var Handled: Boolean); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure LoadFromFile(const FileName: string); override;
    procedure SaveToFile(const FileName: string); override;
    function FindAnalyzer(const LexerName: string): TecSyntAnalyzer;
    function AddAnalyzer: TecSyntAnalyzer;
    procedure Clear;
    procedure Move(CurIndex, NewIndex: Integer);

    property AnalyzerCount: integer read GetCount;
    property Analyzers[Index: integer]: TecSyntAnalyzer read GeItem;
    property FileName;
    property CurrentLexer: TecSyntAnalyzer read FCurrentLexer write SetCurrentLexer;
    property Modified: Boolean read FModified write FModified;
  published
    property OnLexerChanged: TNotifyEvent read FOnLexerChanged write FOnLexerChanged stored NotStored;
    property OnChange: TNotifyEvent read FOnChange write FOnChange stored NotStored;
  end;

  TecSyntStyles = class(TLoadableComponent)
  private
    FStyles: TecStylesCollection;
    procedure SetStyles(const Value: TecStylesCollection);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Styles: TecStylesCollection read FStyles write SetStyles;
  end;

implementation

uses
  SysUtils, Forms, Dialogs,
  Math,
  ecSysUtils;

const
  SecDefaultTokenTypeNames = 'Unknown' + #13#10 +
                             'Comment' + #13#10 +
                             'Id'      + #13#10 +
                             'Symbol'  + #13#10 +
                             'String'  + #13#10 +
                             'Number'  + #13#10 +
                             'Preprocessor';

//local copy of ecUpCase. it is faster, uses AnsiChar UpCase.
function ecUpCase(ch: WideChar): char;
begin
  Result:= system.UpCase(char(ch));
end;

procedure SetDefaultModifiers(RE: TecRegExpr);
begin
  RE.ModifierI := True;
  RE.ModifierG := True;
  RE.ModifierS := False;
  RE.ModifierM := True;
  RE.ModifierX := True;
  RE.ModifierR := False;
end;

{ TecSyntToken }

constructor TecSyntToken.Create(ARule: TRuleCollectionItem; AStartPos,
  AEndPos: integer);
begin
  inherited Create(AStartPos, AEndPos);
  FRule := ARule;
  FTokenType := TecTokenRule(ARule).TokenType;
end;

function TecSyntToken.GetStr(const Source: ecString): ecString;
begin
  Result := Copy(Source, StartPos + 1, EndPos - StartPos);
end;

function TecSyntToken.GetStyle: TecSyntaxFormat;
begin
  if Rule = nil then Result := nil
   else Result := Rule.Style;
end;

{ TecTextRange }

constructor TecTextRange.Create(AStartIdx, AStartPos: integer);
begin
  inherited Create;
  FStart := AStartIdx;
  FStartPos := AStartPos;
  FEnd := -1;
  FEndCondIndex := -1;
  FIndex := -1;
end;

function TecTextRange.GetIsClosed: Boolean;
begin
  Result := FEnd <> -1;
end;

function TecTextRange.GetKey: integer;
begin
  Result := FStartPos;
end;

function TecTextRange.GetLevel: integer;
var prn: TecTextRange;
begin
  prn := Parent;
  Result := 0;
  while prn <> nil do
   begin
     inc(Result);
     prn := prn.Parent;
   end;
end;

function TecTextRange.IsParent(Range: TecTextRange): Boolean;
begin
  if Range = FParent then Result := True else
    if Assigned(FParent) then Result := FParent.IsParent(Range)
      else Result := False;
end;

{ TSyntCollectionItem }

procedure TSyntCollectionItem.AssignTo(Dest: TPersistent);
begin
  if Dest is TSyntCollectionItem then
   begin
    (Dest as TSyntCollectionItem).DisplayName := DisplayName;
    (Dest as TSyntCollectionItem).Enabled := Enabled;
   end;
end;

constructor TSyntCollectionItem.Create(Collection: TCollection);
var NewName: string;
    n: integer;
begin
  inherited;
  FEnabled := True;
  if Collection = nil then Exit;
  n := 1;
  repeat
    NewName  := GetItemBaseName + ' ' + IntToStr(n);
    inc(n);
  until TSyntCollection(Collection).ItemByName(NewName) = nil;
  FName := NewName;
end;

function TSyntCollectionItem.GetDisplayName: string;
begin
  Result := FName;
end;

function TSyntCollectionItem.GetIsInvalid: Boolean;
begin
  Result := False;
end;

function TSyntCollectionItem.GetItemBaseName: string;
begin
  Result := 'Item';
end;

procedure TSyntCollectionItem.Loaded;
begin

end;

procedure TSyntCollectionItem.SetDisplayName(const Value: string);
var i: integer;
begin
  if Collection <> nil then
  for i := 0 to Collection.Count - 1 do
   if Collection.Items[i] <> Self then
    if Collection.Items[i].DisplayName = Value then
      Exit;
  FName := Value;
end;

procedure TSyntCollectionItem.SetEnabled(const Value: Boolean);
begin
  if FEnabled <> Value then
   begin
     FEnabled := Value;
     Changed(False);
   end;
end;

{ TSyntCollection }

constructor TSyntCollection.Create(ItemClass: TCollectionItemClass);
begin
  if not ItemClass.InheritsFrom(TSyntCollectionItem) then
   raise Exception.Create('Allow only TSyntCollectionItem Class');
  inherited;
end;

function TSyntCollection.GetItems(Index: integer): TSyntCollectionItem;
begin
  Result := (inherited Items[Index]) as TSyntCollectionItem;
end;

function TSyntCollection.GetOwner: TPersistent;
begin
  Result := FSyntOwner;
end;

function TSyntCollection.GetUniqueName(const Base: string): string;
var n: integer;
begin
  Result := Base;
  n := 0;
  while ItemByName(Result) <> nil do
   begin
    Inc(n);
    Result := Base + IntToStr(n);
   end;
end;

function TSyntCollection.ItemByName(const AName: string): TSyntCollectionItem;
var i: integer;
begin
  for i := 0 to Count - 1 do
   if Items[i].DisplayName = AName then
    begin
      Result := Items[i] as TSyntCollectionItem;
      Exit;
    end;
  Result := nil;
end;

procedure TSyntCollection.Loaded;
var i: integer;
begin
  for i := 0 to Count - 1 do
   Items[i].Loaded;
end;

procedure TSyntCollection.Update(Item: TCollectionItem);
begin
  inherited;
  if Assigned(FOnChange) then FOnChange(Self, TSyntCollectionItem(Item));
end;

function TSyntCollection.ValidItem(Item: TSyntCollectionItem): Boolean;
var i: integer;
begin
  Result := True;
  if Item <> nil then
   for i := 0 to Count - 1 do
    if Items[i] = Item then Exit;
  Result := False;
end;

{ TecSyntaxFormat }

constructor TecSyntaxFormat.Create(Collection: TCollection);
var i: integer;
begin
  FIsBlock := False;
  FFont := TFont.Create;
  FFont.Name := 'Courier New';
  FFont.Size := 10;
  FBgColor := clNone;
  FVertAlign := vaCenter;
  FFormatType := ftFontAttr;
  for i := 0 to 3 do
   begin
    FBorderTypes[i] := blNone;
    FBorderColors[i] := clBlack;
   end;
  FFormatFlags := [ffBold, ffItalic, ffUnderline, ffStrikeOut, ffReadOnly,
                   ffHidden, ffFontName, ffFontSize, ffFontCharset, ffVertAlign];
  inherited;
  FFont.OnChange := FontChanged;
end;

destructor TecSyntaxFormat.Destroy;
begin
  FreeAndNil(FFont);
  inherited;
end;

procedure TecSyntaxFormat.AssignTo(Dest: TPersistent);
var i: integer;
begin
  inherited;
  if Dest is TecSyntaxFormat then
   with Dest as TecSyntaxFormat do
    begin
      FBgColor := Self.BgColor;
      FFont.Assign(Self.Font);
      FVertAlign := Self.FVertAlign;
      FIsBlock := Self.FIsBlock;
      FFormatType := Self.FFormatType;
      Hidden := Self.Hidden;
      ReadOnly := Self.ReadOnly;
      MultiLineBorder := Self.MultiLineBorder;
      FChangeCase := Self.ChangeCase;
      for i := 0 to 3 do
       begin
        FBorderTypes[i] := Self.FBorderTypes[i];
        FBorderColors[i] := Self.FBorderColors[i];
       end;
      FFormatFlags := Self.FFormatFlags;
    end;
end;

procedure TecSyntaxFormat.SetBgColor(const Value: TColor);
begin
  FBgColor := Value;
  Change;
end;

procedure TecSyntaxFormat.SetFont(const Value: TFont);
begin
  FFont.Assign(Value);
  Change;
end;

procedure TecSyntaxFormat.FontChanged(Sender: TObject);
begin
  Change;
end;

procedure TecSyntaxFormat.SetVertAlign(const Value: TecVertAlignment);
begin
  FVertAlign := Value;
  Change;
end;

function TecSyntaxFormat.GetItemBaseName: string;
begin
  Result := 'Style';
end;

procedure TecSyntaxFormat.SetFormatType(const Value: TecFormatType);
begin
  FFormatType := Value;
  Change;
end;

procedure TecSyntaxFormat.Change;
begin
  Changed(False);
  if Assigned(FOnChange) then FOnChange(Self);
end;

procedure TecSyntaxFormat.SetHidden(const Value: Boolean);
begin
  FHidden := Value;
  Change;
end;

function TecSyntaxFormat.GetBorderColor(Index: Integer): TColor;
begin
  if (Index >= 0) and (Index <= 3) then
    Result := FBorderColors[Index]
  else
    Result := clBlack;
end;

function TecSyntaxFormat.GetBorderType(Index: Integer): TecBorderLineType;
begin
  if (Index >= 0) and (Index <= 3) then
    Result := FBorderTypes[Index]
  else
    Result := blNone;
end;

procedure TecSyntaxFormat.SetBorderColor(Index: Integer;
  const Value: TColor);
begin
  if (Index >= 0) and (Index <= 3) then
   begin
    FBorderColors[Index] := Value;
    Change;
   end;
end;

procedure TecSyntaxFormat.SetBorderType(Index: Integer;
  const Value: TecBorderLineType);
begin
  if (Index >= 0) and (Index <= 3) then
   begin
    FBorderTypes[Index] := Value;
    Change;
   end;
end;

procedure TecSyntaxFormat.SetMultiLineBorder(const Value: Boolean);
begin
  FMultiLineBorder := Value;
  Change;
end;

procedure TecSyntaxFormat.SetReadOnly(const Value: Boolean);
begin
  FReadOnly := Value;
  Change;
end;

procedure TecSyntaxFormat.SetChangeCase(const Value: TecChangeCase);
begin
  FChangeCase := Value;
  Change;
end;

function TecSyntaxFormat.HasBorder: Boolean;
var i: integer;
begin
  for i := 0 to 3 do
   if FBorderTypes[i] <> blNone then
     begin
      Result := True;
      Exit;
     end;
  Result := False;
end;

procedure TecSyntaxFormat.SetFormatFlags(const Value: TecFormatFlags);
begin
  if FFormatFlags <> Value then
    begin
      FFormatFlags := Value;
      Change;
    end;
end;

procedure TecSyntaxFormat.ApplyTo(Canvas: TCanvas; AllowChangeFont: Boolean);
var fs: TFontStyles;
  procedure SwitchFontFlag(ff: TecFormatFlag; f: TFontStyle);
  begin
    if ff in FormatFlags then
      if f in Font.Style then Include(fs, f)
       else Exclude(fs, f);
  end;
begin
  if not Enabled then Exit;

  if BgColor <> clNone then
    Canvas.Brush.Color := BgColor;

  if FormatType = ftBackGround then Exit else
   begin
     if Font.Color <> clNone then
       Canvas.Font.Color := Font.Color;
     if FormatType <> ftColor then
      begin
       fs := Canvas.Font.Style;
       SwitchFontFlag(ffBold, fsBold);
       SwitchFontFlag(ffItalic, fsItalic);
       SwitchFontFlag(ffUnderline, fsUnderline);
       SwitchFontFlag(ffStrikeOut, fsStrikeOut);
       if Canvas.Font.Style <> fs then
         Canvas.Font.Style := fs;
       if (FormatType = ftCustomFont) and AllowChangeFont then
        begin
          if ffFontName in FormatFlags then
            Canvas.Font.Name := Font.Name;
          if ffFontCharset in FormatFlags then
            Canvas.Font.Charset := Font.Charset;
          if ffFontSize in FormatFlags then
            Canvas.Font.Size := Font.Size;
        end;
      end;
   end;
end;

function TecSyntaxFormat.IsEqual(Other: TecSyntaxFormat): Boolean;
begin
  Result := (BgColor = Other.BgColor) and
            (FormatType = Other.FormatType) and
            (FormatFlags = Other.FormatFlags) and
            (Hidden = Other.Hidden) and
            (ReadOnly = Other.ReadOnly) and
            (ChangeCase = Other.ChangeCase) and
            (VertAlignment = Other.VertAlignment);
  if Result and (FormatType <> ftBackGround) then
    begin
      Result := Font.Color = Other.Font.Color;
      if Result and (FormatType <> ftColor) then
        begin
          Result := Font.Style = Other.Font.Style;
          if Result and (FormatType <> ftFontAttr) then
            begin
              Result := (not (ffFontName in FormatFlags) or
                         (Font.Name = Other.Font.Name))
                        and
                        (not (ffFontSize in FormatFlags) or
                         (Font.Size = Other.Font.Size))
                        and
                        (not (ffFontCharSet in FormatFlags) or
                         (Font.Charset = Other.Font.Charset));
            end;
        end;
    end;
end;

procedure TecSyntaxFormat.Merge(Over: TecSyntaxFormat);
var fs: TFontStyles;
  procedure SwitchFontFlag(ff: TecFormatFlag; f: TFontStyle);
  begin
    if ff in Over.FormatFlags then
      begin
        Include(FFormatFlags, ff);
        if f in Over.Font.Style then Include(fs, f)
         else Exclude(fs, f);
      end;
  end;
begin
  if ffVertAlign in Over.FormatFlags then
    VertAlignment := Over.VertAlignment;
  if ffHidden in Over.FormatFlags then
    Hidden := Over.Hidden;
  if ffReadOnly in Over.FormatFlags then
    ReadOnly := Over.ReadOnly;
  if Over.BgColor <> clNone then
    BgColor := Over.BgColor;
  if Over.ChangeCase <> ccNone then
    ChangeCase := Over.ChangeCase;
  if Over.FormatType <> ftBackGround then
    begin
      if Over.Font.Color <> clNone then
        Font.Color := Over.Font.Color;
      if Over.FormatType <> ftColor then
        begin
          fs := Font.Style;
          SwitchFontFlag(ffBold, fsBold);
          SwitchFontFlag(ffItalic, fsItalic);
          SwitchFontFlag(ffUnderline, fsUnderline);
          SwitchFontFlag(ffStrikeOut, fsStrikeOut);
          Font.Style := fs;
          if Over.FormatType <> ftFontAttr then
            begin
              if ffFontName in Over.FormatFlags then
                Font.Name := Over.Font.Name;
              if ffFontCharset in Over.FormatFlags then
                Font.Charset := Over.Font.Charset;
              if ffFontSize in Over.FormatFlags then
                Font.Size := Over.Font.Size;
            end;
        end;
    end;
  FormatFlags := FormatFlags + Over.FormatFlags;
end;

function TecSyntaxFormat.GetHidden: Boolean;
begin
  Result := FHidden and (ffHidden in FFormatFlags);
end;

procedure TecSyntaxFormat.Intersect(Over: TecSyntaxFormat);
begin
  FormatFlags := Over.FormatFlags * FormatFlags;
  if Over.FormatType < FormatType then
    FormatType := Over.FormatType;

  if (ffVertAlign in FormatFlags) and
     (VertAlignment <> Over.VertAlignment) then
    FormatFlags := FormatFlags - [ffVertAlign];

  if (ffReadOnly in FormatFlags) and
     (ReadOnly <> Over.ReadOnly) then
    FormatFlags := FormatFlags - [ffReadOnly];

  if (ffHidden in FormatFlags) and
     (Hidden <> Over.Hidden) then
    FormatFlags := FormatFlags - [ffHidden];

  if Over.ChangeCase <> ChangeCase then
    ChangeCase := ccNone;

  if BgColor <> Over.BgColor then
    BgColor := clNone;

  if FormatType = ftBackGround then Exit;

  if Font.Color <> Over.Font.Color then
    Font.Color := clNone;

  if FormatType = ftColor then Exit;

  if (ffBold in FormatFlags) and
     ((fsBold in Font.Style) <> (fsBold in Over.Font.Style)) then
    FormatFlags := FormatFlags - [ffBold];

  if (ffItalic in FormatFlags) and
     ((fsItalic in Font.Style) <> (fsItalic in Over.Font.Style)) then
    FormatFlags := FormatFlags - [ffItalic];

  if (ffUnderline in FormatFlags) and
     ((fsUnderline in Font.Style) <> (fsUnderline in Over.Font.Style)) then
    FormatFlags := FormatFlags - [ffUnderline];

  if (ffStrikeOut in FormatFlags) and
     ((fsStrikeOut in Font.Style) <> (fsStrikeOut in Over.Font.Style)) then
    FormatFlags := FormatFlags - [ffStrikeOut];

  if FormatType = ftFontAttr then Exit;

  if (ffFontName in FormatFlags) and
     (not SameText(Font.Name, Over.Font.Name)) then
    FormatFlags := FormatFlags - [ffFontName];

  if (ffFontSize in FormatFlags) and
     (Font.Size <> Over.Font.Size) then
    FormatFlags := FormatFlags - [ffFontSize];

  if (ffFontCharset in FormatFlags) and
     (Font.Charset <> Over.Font.Charset) then
    FormatFlags := FormatFlags - [ffFontCharset];
end;

{ TecStylesCollection }

function TecStylesCollection.Add: TecSyntaxFormat;
begin
  Result := (inherited Add) as TecSyntaxFormat;
end;

constructor TecStylesCollection.Create;
begin
  inherited Create(TecSyntaxFormat);
end;

function TecStylesCollection.GetItem(Index: integer): TecSyntaxFormat;
begin
  Result := (inherited Items[Index]) as TecSyntaxFormat;
end;

function TecStylesCollection.Synchronize(Source: TecStylesCollection): integer;
var j: integer;
    f: TecSyntaxFormat;
begin
  Result := 0;
  for j := 0 to Count - 1 do
   begin
     f := TecSyntaxFormat(Source.ItemByName(Items[j].DisplayName));
     if f <> nil then
      begin
        Inc(Result);
        Items[j].Assign(f);
      end;
   end;
end;

{ TecSingleTagCondition }

procedure TecSingleTagCondition.AssignTo(Dest: TPersistent);
var dst: TecSingleTagCondition;
begin
  if Dest is TecSingleTagCondition then
   begin
     dst := Dest as TecSingleTagCondition;
     dst.CondType := CondType;
     dst.TokenTypes := TokenTypes;
     dst.FTagList.Assign(FTagList);
     dst.IgnoreCase := IgnoreCase;
   end;
end;

function TecSingleTagCondition.CheckToken(const Source: ecString;
  Token: TecSyntToken): Boolean;
var s: ecString;
    i, N: integer;
    RE: TecRegExpr;
begin
  Result := False;
  if FTokenTypes <> 0 then
   begin
    Result := ((1 shl Token.TokenType) and FTokenTypes) <> 0;
    if FCondType = tcSkip then Exit;
    if not Result then
    case FCondType of
      tcStrictMask, tcMask, tcEqual: Exit;
      tcNotEqual:
        begin
          Result := True;
          Exit;
        end;
    end;
   end;
  if FTagList.Count > 0 then
   begin
    s := Token.GetStr(Source);
    s := Trim(s); //AT
    if FCondType in [tcMask, tcStrictMask] then
     begin
       RE := TecRegExpr.Create;
       SetDefaultModifiers(RE);
       try
         for i := 0 to FTagList.Count - 1 do
          begin
            RE.Expression := FTagList[i];
            if FCondType = tcMask then
              Result := RE.MatchLength(s, 1) > 0
            else
              begin
                N := 1;
                Result := RE.Match(s, N);
                if Result then
                  Result := N > Length(S);
              end;

            if Result then break;
          end;
       except
       end;
       FreeAndNil(RE);
     end else
     begin
       Result := FTagList.IndexOf(s) <> -1;
       if FCondType = tcNotEqual then Result := not Result;
     end;
   end else Result := FCondType <> tcNotEqual;
end;

constructor TecSingleTagCondition.Create(Collection: TCollection);
begin
  inherited;
  FCondType := tcEqual;
  FTagList := TzStringList.Create;
  TzStringList(FTagList).Sorted := true;
  TzStringList(FTagList).Delimiter := ' ';
  TzStringList(FTagList).Duplicates := dupIgnore;
  TzStringList(FTagList).CaseSensitive := True;
  TzStringList(FTagList).OnChange := TagListChanged;
  {$IFDEF EC_VCL6_UP}
  TzStringList(FTagList).QuoteChar := ' ';
  {$ENDIF}
end;

destructor TecSingleTagCondition.Destroy;
begin
  FreeAndNil(FTagList);
  inherited;
end;

procedure TecSingleTagCondition.SetIgnoreCase(const Value: Boolean);
begin
  TzStringList(FTagList).CaseSensitive := not Value;
end;

function TecSingleTagCondition.GetIgnoreCase: Boolean;
begin
  Result := not TzStringList(FTagList).CaseSensitive;
end;

procedure TecSingleTagCondition.SetTagList(const Value: TStrings);
begin
  TzStringList(FTagList).DelimitedText := Value.Text;
  Changed(False);
end;

procedure TecSingleTagCondition.SetTokenTypes(const Value: DWORD);
begin
  FTokenTypes := Value;
  Changed(False);
end;

procedure TecSingleTagCondition.SetCondType(const Value: TecTagConditionType);
begin
  FCondType := Value;
  Changed(False);
end;

procedure TecSingleTagCondition.TagListChanged(Sender: TObject);
begin
  Changed(False);
end;

{ TecConditionCollection }

function TecConditionCollection.Add: TecSingleTagCondition;
begin
  Result := (inherited Add) as TecSingleTagCondition;
end;

constructor TecConditionCollection.Create(AOwner: TecTagBlockCondition);
begin
  inherited Create(TecSingleTagCondition);
  FOwner := AOwner;
  PropName := 'Conditions';
end;

function TecConditionCollection.GetItem(Index: integer): TecSingleTagCondition;
begin
  Result := (inherited Items[Index]) as TecSingleTagCondition;
end;

function TecConditionCollection.GetOwner: TPersistent;
begin
  Result := FOwner;
end;

procedure TecConditionCollection.Update(Item: TCollectionItem);
begin
  inherited;
  if Assigned(FOnChange) then FOnChange(Self);
end;

{ TecTagBlockCondition }

constructor TecTagBlockCondition.Create(Collection: TCollection);
begin
  inherited;
  FConditions := TecConditionCollection.Create(Self);
  FConditions.OnChange := ConditionsChanged;
  FBlockType := btRangeStart;
  FLinePos := lbTop;
  FDisplayInTree := True;
//  FHighlightPos := cpBound;
  FTokenType := -1;
  FTreeItemImage := -1;
  FTreeGroupImage := -1;
  FPen := TPen.Create;
  FPen.OnChange := ConditionsChanged;
end;

procedure TecTagBlockCondition.AssignTo(Dest: TPersistent);
var dst: TecTagBlockCondition;
begin
  inherited;
  if Dest is TecTagBlockCondition then
   begin
     dst := Dest as TecTagBlockCondition;
     dst.ConditionList := ConditionList;
     dst.FIdentIndex := IdentIndex;
     dst.FLinePos := LinePos;
     dst.FBlockOffset := BlockOffset;
     dst.FBlockType := BlockType;
     dst.BlockEnd := BlockEnd;
     dst.FEndOfTextClose := FEndOfTextClose;
     dst.FNotCollapsed := FNotCollapsed;
     dst.FSameIdent := FSameIdent;
     dst.Highlight := Highlight;
     dst.InvertColors := InvertColors;
     dst.DisplayInTree := DisplayInTree;
     dst.NameFmt := NameFmt;
     dst.GroupFmt := GroupFmt;
     dst.RefToCondEnd := RefToCondEnd;
     dst.DynHighlight := DynHighlight;
     dst.HighlightPos := HighlightPos;
     dst.DynSelectMin := DynSelectMin;
     dst.CancelNextRules := CancelNextRules;
     dst.DrawStaple := DrawStaple;
     dst.GroupIndex := GroupIndex;
     dst.OnBlockCheck := OnBlockCheck;
     dst.CollapseFmt := CollapseFmt;
     dst.FSelfClose := SelfClose;
     dst.FNoEndRule := NoEndRule;
     dst.GrammaRuleName := GrammaRuleName;
     dst.TokenType := TokenType;
     dst.TreeItemStyle := TreeItemStyle;
     dst.TreeGroupStyle := TreeGroupStyle;
     dst.TreeItemImage := TreeItemImage;
     dst.TreeGroupImage := TreeGroupImage;
     dst.Pen := Pen;
     dst.UseCustomPen := UseCustomPen;
     dst.IgnoreAsParent := IgnoreAsParent;
     dst.AutoCloseText := AutoCloseText;
     dst.AutoCloseMode := AutoCloseMode;
   end;
end;

function TecTagBlockCondition.Check(const Source: ecString;
  Tags: TecClientSyntAnalyzer; N: integer; var RefIdx: integer): Boolean;
var i, offs, idx, skipped, skip_cond: integer;
begin
  Result := False;
  offs := CheckOffset;
  skipped := 0;
  skip_cond := 0;
  i := 0;
  while i < ConditionList.Count do
  begin
   idx := N - 1 - i - offs - skipped + skip_cond;
   if (ConditionList[i].CondType = tcSkip) and (i < ConditionList.Count - 1)
      and (ConditionList[i+1].CondType <> tcSkip) then
     begin
      inc(i);
      inc(skip_cond);
      while (idx >= 0) and not ConditionList[i].CheckToken(Source, Tags[idx]) do
       begin
         if not ConditionList[i - 1].CheckToken(Source, Tags[idx]) then
           Exit;
         dec(idx);
         inc(skipped);
       end;
      if idx < 0 then Exit;
     end;
   with ConditionList[i] do
    if (idx < 0) or not CheckToken(Source, Tags[idx]) then Exit;
   inc(i);
  end;

  Result := ConditionList.Count > 0;
//  if FRefToCondEnd then
  RefIdx := N - ConditionList.Count - offs - skipped + skip_cond;
//  else
//    RefIdx := N - 1 - offs;
end;

destructor TecTagBlockCondition.Destroy;
begin
  FreeAndNil(FConditions);
  FreeAndNil(FPen);
  inherited;
end;

function TecTagBlockCondition.GetItemBaseName: string;
begin
  Result := 'Tag block rule';
end;

function TecTagBlockCondition.GetBlockEndName: string;
var FSynt: TecSyntAnalyzer;
begin
  FSynt := TSyntCollection(Collection).SyntOwner;
  if not Assigned(FSynt) then Exit;

  if csLoading in FSynt.ComponentState then
    Result := FBlockEndName
  else
   if Assigned(FBlockEndCond) then
    Result := FBlockEndCond.DisplayName
   else
    Result := '';
end;

procedure TecTagBlockCondition.SetBlockEndName(const Value: string);
var FSynt: TecSyntAnalyzer;
begin
  FSynt := TSyntCollection(Collection).SyntOwner;
  if not Assigned(FSynt) then Exit;
  if csLoading in FSynt.ComponentState then
    FBlockEndName := Value
  else
    FBlockEndCond := TecTagBlockCondition(TecBlockRuleCollection(Collection).ItemByName(Value));
  Changed(False);
end;

procedure TecTagBlockCondition.Loaded;
var FSynt: TecSyntAnalyzer;
begin
  inherited;
  FSynt := TSyntCollection(Collection).SyntOwner;
  if not Assigned(FSynt) then Exit;
  if FBlockEndName <> '' then
    FBlockEndCond := TecTagBlockCondition(FSynt.FBlockRules.ItemByName(FBlockEndName));
  if FTreeItemStyle <> '' then
    FTreeItemStyleObj := TecSyntaxFormat(FSynt.Formats.ItemByName(FTreeItemStyle));
  if FTreeGroupStyle <> '' then
    FTreeGroupStyleObj := TecSyntaxFormat(FSynt.Formats.ItemByName(FTreeGroupStyle));
end;

function TecTagBlockCondition.CheckOffset: integer;
begin
  Result := 0;
  if FRefToCondEnd then Exit;
  if FIdentIndex < 0 then Result := -FIdentIndex;
  if (FBlockOffset < 0) and (FBlockOffset < FIdentIndex) then
    Result := -FBlockOffset;
end;

procedure TecTagBlockCondition.SetBlockType(const Value: TecTagBlockType);
begin
  FBlockType := Value;
  if FBlockType in [btTagDetect, btLineBreak] then
   begin
     FBlockOffset := 0;
     FBlockEndCond := nil;
   end;
  Changed(False);
end;

procedure TecTagBlockCondition.SetConditions(
  const Value: TecConditionCollection);
begin
  FConditions.Assign(Value);
end;

procedure TecTagBlockCondition.ConditionsChanged(Sender: TObject);
begin
  Changed(False);
end;

procedure TecTagBlockCondition.SetBlockEndCond(
  const Value: TecTagBlockCondition);
begin
  if FBlockEndCond <> Value then
    begin
      FBlockEndCond := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetLinePos(const Value: TecLineBreakPos);
begin
  if FLinePos <> Value then
    begin
      FLinePos := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetIdentIndex(const Value: integer);
begin
  if FIdentIndex <> Value then
    begin
      FIdentIndex := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetBlockOffset(const Value: integer);
begin
  if FBlockOffset <> Value then
    begin
      FBlockOffset := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetEndOfTextClose(const Value: Boolean);
begin
  if FEndOfTextClose <> Value then
    begin
      FEndOfTextClose := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetNotCollapsed(const Value: Boolean);
begin
  if FNotCollapsed <> Value then
    begin
      FNotCollapsed := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetSameIdent(const Value: Boolean);
begin
  if FSameIdent <> Value then
    begin
      FSameIdent := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetHighlight(const Value: Boolean);
begin
  if FHighlight <> Value then
    begin
      FHighlight := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetInvertColors(const Value: Boolean);
begin
  if FInvertColors <> Value then
    begin
      FInvertColors := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetDisplayInTree(const Value: Boolean);
begin
  if FDisplayInTree <> Value then
    begin
      FDisplayInTree := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetCancelNextRules(const Value: Boolean);
begin
  if FCancelNextRules <> Value then
    begin
      FCancelNextRules := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetDynHighlight(
  const Value: TecDynamicHighlight);
begin
  if FDynHighlight <> Value then
    begin
      FDynHighlight := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetDynSelectMin(const Value: Boolean);
begin
  if FDynSelectMin <> Value then
    begin
      FDynSelectMin := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetGroupFmt(const Value: ecString);
begin
  if FGroupFmt <> Value then
    begin
      FGroupFmt := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetHighlightPos(const Value: TecHighlightPos);
begin
  if FHighlightPos <> Value then
    begin
      FHighlightPos := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetNameFmt(const Value: ecString);
begin
  if FNameFmt <> Value then
    begin
      FNameFmt := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetRefToCondEnd(const Value: Boolean);
begin
  if FRefToCondEnd <> Value then
    begin
      FRefToCondEnd := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetDrawStaple(const Value: Boolean);
begin
  if FDrawStaple <> Value then
    begin
      FDrawStaple := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetCollapseFmt(const Value: ecString);
begin
  if FCollapseFmt <> Value then
    begin
      FCollapseFmt := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetSelfClose(const Value: Boolean);
begin
  if FSelfClose <> Value then
    begin
      FSelfClose := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetNoEndRule(const Value: Boolean);
begin
  if FNoEndRule <> Value then
    begin
      FNoEndRule := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetGrammaRuleName(const Value: string);
begin
  if FGrammaRuleName <> Value then
   begin
    FGrammaRuleName := Value;
    FGrammaRule :=
      TSyntCollection(Collection).SyntOwner.Gramma.ParserRuleByName(Value);
   end;
end;

procedure TecTagBlockCondition.SetTokenType(const Value: integer);
begin
  if FTokenType <> Value then
    begin
      FTokenType := Value;
      Changed(False);
    end;
end;

function TecTagBlockCondition.GetTreeItemStyle: string;
begin
  Result := ValidStyleName(FTreeItemStyle, FTreeItemStyleObj);
end;

procedure TecTagBlockCondition.SetTreeItemStyle(const Value: string);
begin
  ValidSetStyle(Value, FTreeItemStyle, FTreeItemStyleObj);
end;

function TecTagBlockCondition.GetTreeGroupStyle: string;
begin
  Result := ValidStyleName(FTreeGroupStyle, FTreeGroupStyleObj);
end;

procedure TecTagBlockCondition.SetTreeGroupStyle(const Value: string);
begin
  ValidSetStyle(Value, FTreeGroupStyle, FTreeGroupStyleObj);
end;

procedure TecTagBlockCondition.SetTreeGroupImage(const Value: integer);
begin
  if FTreeGroupImage <> Value then
    begin
      FTreeGroupImage := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetTreeItemImage(const Value: integer);
begin
  if FTreeItemImage <> Value then
    begin
      FTreeItemImage := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetPen(const Value: TPen);
begin
  FPen.Assign(Value);
end;

procedure TecTagBlockCondition.SetUseCustomPen(const Value: Boolean);
begin
  if FUseCustomPen <> Value then
    begin
      FUseCustomPen := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetIgnoreAsParent(const Value: Boolean);
begin
  if FIgnoreAsParent <> Value then
    begin
      FIgnoreAsParent := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetAutoCloseText(Value: ecString);
begin
  if Value = sLineBreak then
    Value := '';
  if FAutoCloseText <> Value then
    begin
      FAutoCloseText := Value;
      Changed(False);
    end;
end;

procedure TecTagBlockCondition.SetAutoCloseMode(const Value: TecAutoCloseMode);
begin
  if FAutoCloseMode <> Value then
    begin
      FAutoCloseMode := Value;
      Changed(False);
    end;
end;

{ TecBlockRuleCollection }

function TecBlockRuleCollection.Add: TecTagBlockCondition;
begin
  Result := inherited Add as TecTagBlockCondition;
end;

constructor TecBlockRuleCollection.Create;
begin
  inherited Create(TecTagBlockCondition);
end;

function TecBlockRuleCollection.GetItem(Index: integer): TecTagBlockCondition;
begin
  Result := inherited Items[Index] as TecTagBlockCondition;
end;

{ TecTokenRule }

procedure TecTokenRule.AssignTo(Dest: TPersistent);
var dst: TecTokenRule;
begin
  inherited;
  if Dest is TecTokenRule then
   begin
    dst := Dest as TecTokenRule;
    dst.FTokenType := FTokenType;
    dst.FRegExpr.Expression := Expression;
    dst.OnMatchToken := OnMatchToken;
    dst.ColumnFrom := ColumnFrom;
    dst.ColumnTo := ColumnTo;
   end;
end;

constructor TecTokenRule.Create(Collection: TCollection);
begin
  inherited;
  FBlock := nil;
  FFormat := nil;
  FRegExpr := TecRegExpr.Create;
  SetDefaultModifiers(FRegExpr);
end;

destructor TecTokenRule.Destroy;
begin
  FreeAndNil(FRegExpr);
  inherited;
end;

function TecTokenRule.GetExpression: ecString;
begin
  Result := FRegExpr.Expression;
end;

function TecTokenRule.GetIsInvalid: Boolean;
begin
  Result := FRegExpr.IsInvalid;
end;

function TecTokenRule.GetItemBaseName: string;
begin
  Result := 'Token rule';
end;

function TecTokenRule.Match(const Source: ecString; Pos: integer): integer;
begin
 try
  Result := FRegExpr.MatchLength(Source, Pos);
 except
  Result := 0;
 end;
end;

procedure TecTokenRule.SetColumnFrom(const Value: integer);
begin
  if FColumnFrom <> Value then
    begin
      FColumnFrom := Value;
      Changed(False);
    end;
end;

procedure TecTokenRule.SetColumnTo(const Value: integer);
begin
  if FColumnTo <> Value then
    begin
      FColumnTo := Value;
      Changed(False);
    end;
end;

procedure TecTokenRule.SetExpression(const Value: ecString);
begin
  try
    FRegExpr.Expression := Value;
  except
    Application.HandleException(Self);
  end;
  Changed(False);
end;

procedure TecTokenRule.SetTokenType(const Value: integer);
begin
  if FTokenType <> Value then
    begin
      FTokenType := Value;
      Changed(False);
    end;
end;

{ TRuleCollectionItem }

function TRuleCollectionItem.ValidStyleName(const AStyleName: string;
  AStyle: TecSyntaxFormat): string;
var FSynt: TecSyntAnalyzer;
begin
  FSynt := TSyntCollection(Collection).SyntOwner;
  Result := '';
  if not Assigned(FSynt) then Exit;

  if csLoading in FSynt.ComponentState then
    Result := AStyleName
  else
   if Assigned(AStyle) then
    Result := AStyle.DisplayName;
end;

function TRuleCollectionItem.ValidSetStyle(const AStyleName: string;
  var AStyleField: string; var AStyle: TecSyntaxFormat): string;
var FSynt: TecSyntAnalyzer;
begin
  Result := '';
  FSynt := TSyntCollection(Collection).SyntOwner;
  if not Assigned(FSynt) then Exit;
  if csLoading in FSynt.ComponentState then
    AStyleField := AStyleName
  else
    AStyle := TecSyntaxFormat(FSynt.FFormats.ItemByName(AStyleName));
  Changed(False);
end;

function TRuleCollectionItem.GetStyleName: string;
begin
  Result := ValidStyleName(FStyleName, FFormat);
end;

procedure TRuleCollectionItem.SetStyleName(const Value: string);
begin
  ValidSetStyle(Value, FStyleName, FFormat);
end;

procedure TRuleCollectionItem.Loaded;
var FSynt: TecSyntAnalyzer;
begin
  FSynt := TSyntCollection(Collection).SyntOwner;
  if not Assigned(FSynt) then Exit;
  if FStyleName <> '' then
    FFormat := TecSyntaxFormat(FSynt.FFormats.ItemByName(FStyleName));
  if FBlockName <> '' then
    FBlock := TecTagBlockCondition(FSynt.BlockRules.ItemByName(FBlockName));
end;

function TRuleCollectionItem.GetBlockName: string;
var FSynt: TecSyntAnalyzer;
begin
  FSynt := TSyntCollection(Collection).SyntOwner;
  if not Assigned(FSynt) then Exit;

  if csLoading in FSynt.ComponentState then
    Result := FBlockName
  else
   if Assigned(FBlock) then
    Result := FBlock.DisplayName
   else
    Result := '';
end;

procedure TRuleCollectionItem.SetBlockName(const Value: string);
var FSynt: TecSyntAnalyzer;
begin
  FSynt := TSyntCollection(Collection).SyntOwner;
  if not Assigned(FSynt) then Exit;
  if csLoading in FSynt.ComponentState then
    FBlockName := Value
  else
   begin
//    FBlock := TecTagBlockCondition(FSynt.BlockRules.ItemByName(Value));
    FBlock := TecTagBlockCondition(FSynt.BlockRules.ItemByName(Value));
    Changed(False);
   end;
end;

procedure TRuleCollectionItem.AssignTo(Dest: TPersistent);
var dst: TRuleCollectionItem;
begin
  inherited;
  if Dest is TRuleCollectionItem then
    begin
      dst := Dest as TRuleCollectionItem;
      dst.StyleName := StyleName;
      dst.BlockName := BlockName;
      dst.StrictParent := StrictParent;
      dst.NotParent := NotParent;
      dst.AlwaysEnabled := AlwaysEnabled;
      dst.StatesAbsent := StatesAbsent;
      dst.StatesAdd := StatesAdd;
      dst.StatesRemove := StatesRemove;
      dst.StatesPresent := StatesPresent;
    end;
end;

procedure TRuleCollectionItem.SetNotParent(const Value: Boolean);
begin
  if FNotParent <> Value then
    begin
      FNotParent := Value;
      Changed(False);
    end;
end;

procedure TRuleCollectionItem.SetStrictParent(const Value: Boolean);
begin
  if FStrictParent <> Value then
    begin
      FStrictParent := Value;
      Changed(False);
    end;
end;

procedure TRuleCollectionItem.SetAlwaysEnabled(const Value: Boolean);
begin
  if FAlwaysEnabled <> Value then
    begin
      FAlwaysEnabled := Value;
      Changed(False);
    end;
end;

function TRuleCollectionItem.GetSyntOwner: TecSyntAnalyzer;
begin
  Result := TSyntCollection(Collection).SyntOwner;
end;

procedure TRuleCollectionItem.SetStatesAdd(const Value: integer);
begin
  if FStatesAdd <> Value then
    begin
      FStatesAdd := Value;
      Changed(False);
    end;
end;

procedure TRuleCollectionItem.SetStatesAbsent(const Value: integer);
begin
  if FStatesAbsent <> Value then
    begin
      FStatesAbsent := Value;
      Changed(False);
    end;
end;

procedure TRuleCollectionItem.SetStatesRemove(const Value: integer);
begin
  if FStatesRemove <> Value then
    begin
      FStatesRemove := Value;
      Changed(False);
    end;
end;

procedure TRuleCollectionItem.SetStatesPresent(const Value: integer);
begin
  if FStatesPresent <> Value then
    begin
      FStatesPresent := Value;
      Changed(False);
    end;
end;

{ TecTokenRuleCollection }

function TecTokenRuleCollection.Add: TecTokenRule;
begin
  Result := inherited Add as TecTokenRule;
end;

constructor TecTokenRuleCollection.Create;
begin
  inherited Create(TecTokenRule);
end;

function TecTokenRuleCollection.GetItem(Index: integer): TecTokenRule;
begin
  Result := inherited Items[Index] as TecTokenRule;
end;

{ TecParserResults }

constructor TecParserResults.Create(AOwner: TecSyntAnalyzer;
  SrcProc: TATStringBuffer; const AClient: IecSyntClient);
begin
  inherited Create;
  if SrcProc = nil then
   raise Exception.Create('Source procedure not passed from syntax server');
  FOwner := AOwner;
  FSrcProc := SrcProc;
  FClient := AClient;
  FTagList := TRangeList.Create(False);
  FSubLexerBlocks := TObjectList.Create;
  FOwner.FClientList.Add(Self);
  FCurState := 0;
  FStateChanges := TObjectList.Create;
end;

destructor TecParserResults.Destroy;
begin
  FOwner.FClientList.Remove(Self);
  FreeAndNil(FTagList);
  FreeAndNil(FSubLexerBlocks);
  FreeAndNil(FStateChanges);
  inherited;
end;

procedure TecParserResults.Clear;
begin
  FTagList.Clear;
  FSubLexerBlocks.Clear;
  FStateChanges.Clear;
  FCurState := 0;
end;

procedure TecParserResults.Finished;
//var i: integer;
begin
//  if FFinished then Exit;
  FFinished := True;
  // Performs Gramma parsing
//  AnalyzeGramma;
end;

function TecParserResults.IsEnabled(Rule: TRuleCollectionItem;
  OnlyGlobal: Boolean): Boolean;
begin
  Result := Rule.Enabled and (not OnlyGlobal or Rule.AlwaysEnabled) and
            ((Rule.StatesPresent = 0) or ((FCurState and Rule.StatesPresent) = Rule.StatesPresent)) and
            ((Rule.StatesAbsent = 0) or ((FCurState and Rule.StatesAbsent) = 0));
end;

function TecParserResults.GetTokenCount: integer;
begin
  Result := FTagList.Count;
end;

function TecParserResults.GetTags(Index: integer): TecSyntToken;
begin
  Result := TecSyntToken(FTagList[Index]);
end;

function TecParserResults.GetTokenStr(Index: integer): ecString;
begin
  if (Index>=0) and (Index<TagCount) then //AT
    with Tags[Index] do
      Result := FSrcProc.SubString(StartPos + 1, EndPos - StartPos)
  else
    Result := '';
end;

function TecParserResults.GetLastPos(const Source: ecString): integer;
begin
  if FTagList.Count = 0 then Result := 1 else
    Result := TecSyntToken(FTagList[FTagList.Count - 1]).EndPos + 1;
  if FLastAnalPos > Result then Result := FLastAnalPos;
end;

procedure TecParserResults.SaveState;
var b: Boolean;
begin
 if FStateChanges.Count = 0 then
   b := FCurState <> 0
 else
   b := FCurState <> TRange(FStateChanges.Last).EndPos;
 if b then
   FStateChanges.Add(TRange.Create(FTagList.Count, FCurState));
end;

// True if end of the text
function TecParserResults.ExtractTag(const Source: ecString; var FPos: integer; IsIdle: Boolean): Boolean;
var N: integer;
    p: TecSyntToken;
    own: TecSyntAnalyzer;

   // Select current lexer
   procedure GetOwner;
   var i, N: integer;
   begin
    own := FOwner;
    for i := FSubLexerBlocks.Count - 1 downto 0 do
     with TecSubLexerRange(FSubLexerBlocks[i]) do
       if FPos > StartPos then
        if EndPos = -1 then
          begin
            // try close sub lexer
    //        if Rule.ToTextEnd then N := 0 else
            N := Rule.MatchEnd(Source, FPos);
            if N > 0 then
             begin
               if Rule.IncludeBounds then
                 begin // New mode in v2.35
                   FEndPos := FPos - 1 + N;
                   FCondEndPos := FEndPos;
                   own := Rule.SyntAnalyzer;
                 end else
                 begin
                   FEndPos := FPos - 1;
                   FCondEndPos := FEndPos + N;
                 end;
               // Close ranges which belongs to this sub-lexer range
               CloseAtEnd(FTagList.PriorAt(StartPos));
             end else
             begin
               own := Rule.SyntAnalyzer;
               Exit;
             end;
          end else
       if FPos < EndPos then
         begin
               own := Rule.SyntAnalyzer;
               Exit;
         end;
   end;

   procedure CheckIntersect;
   var i: integer;
   begin
    for i := FSubLexerBlocks.Count - 1 downto 0 do
     with TecSubLexerRange(FSubLexerBlocks[i]) do
      if (p.EndPos > StartPos) and (p.StartPos < StartPos) then
       begin
        p.FEndPos := StartPos;
        Exit;
       end;
   end;

   function CanOpen(Rule: TecSubAnalyzerRule): Boolean;
   var N: integer;
       sub: TecSubLexerRange;
   begin
     Result := IsEnabled(Rule, False) and (Rule.SyntAnalyzer <> nil);
     if not Result then Exit;
     Result := Rule.FromTextBegin and (FPos = 1);
     if Result then N := 0 else
                    N := Rule.MatchStart(Source, FPos);
     Result := Result or (N > 0);
     if not Result then Exit;
     // To prevent repeated opening
     if FSubLexerBlocks.Count > 0 then
       if (TecSubLexerRange(FSubLexerBlocks.Last).EndPos = FPos - 1) and
          (TecSubLexerRange(FSubLexerBlocks.Last).Rule = Rule) then Exit;

     ApplyStates(Rule);
     sub := TecSubLexerRange.Create(0,0);
     sub.FRule := Rule;
     sub.FCondStartPos := FPos - 1;
     if Rule.IncludeBounds then
       sub.FStartPos := FPos - 1
     else
       sub.FStartPos := FPos + N - 1;
     sub.FEndPos := -1;
     sub.FCondEndPos := -1;
     FSubLexerBlocks.Add(sub);
   end;


   procedure TryOpenSubLexer;
   var i: integer;
   begin
     for i := 0 to own.SubAnalyzers.Count - 1 do
      if CanOpen(own.SubAnalyzers[i]) then Exit;
     if own <> FOwner then
      for i := 0 to FOwner.SubAnalyzers.Count - 1 do
       if FOwner.SubAnalyzers[i].AlwaysEnabled and CanOpen(FOwner.SubAnalyzers[i]) then Exit;
   end;

begin
  GetOwner;
  TryOpenSubLexer;
  if own.SkipSpaces then
    begin
     if own.ParseEndOfLine then N := SkipSpacesNoLineBreak(Source, FPos)
      else N := SkipSpaces(Source, FPos);
    end
   else if FPos > Length(Source) then N := -1 else N := 0;
  TryOpenSubLexer;
  GetOwner;

  Result := N = -1;
  if Result then Exit;

  p := FOwner.GetToken(Self, Source, FPos, own <> FOwner);
  if (own <> FOwner) and (p = nil) then
    p := own.GetToken(Self, Source, FPos, False);
  if p = nil then  // no token
   begin
     Inc(FPos);
   end else
   begin
    CheckIntersect;
    SaveState;
    FTagList.Add(p);
    if not FOwner.SeparateBlockAnalysis then
     begin
      FOwner.SelectTokenFormat(Self, Source, own <> FOwner);
      if own <> FOwner then
        own.SelectTokenFormat(Self, Source, False);
     end else
//    if not IsIdle then
     begin  // Only for first iteration of analysis
      FOwner.HighlightKeywords(Self, Source, own <> FOwner);
      if own <> FOwner then
        own.HighlightKeywords(Self, Source, False);
     end;
    FPos := p.EndPos + 1;
   end;
   FLastAnalPos := FPos;
end;

function TecParserResults.AnalyzerAtPos(Pos: integer): TecSyntAnalyzer;
var i: integer;
begin
 Result := FOwner;
 if Pos >= 0 then
 for i := 0 to FSubLexerBlocks.Count - 1 do
  with TecSubLexerRange(FSubLexerBlocks[i]) do
   if Pos < StartPos then Break else
    if (EndPos = -1) or (Pos < EndPos) then
      Result := Rule.SyntAnalyzer;
end;

function TecParserResults.GetSubLexerRangeCount: integer;
begin
  Result := FSubLexerBlocks.Count;
end;

function TecParserResults.GetSubLexerRange(Index: integer): TecSubLexerRange;
begin
  Result := TecSubLexerRange(FSubLexerBlocks[Index]);
end;

function TecParserResults.GetTokenType(Index: integer): integer;
begin
  Result := Tags[Index].TokenType;
end;

type
  TTextChangeInfo = class
    FPos: integer;
    FCount: integer;
  public
    constructor Create(Pos, Count: integer);
  end;

constructor TTextChangeInfo.Create(Pos, Count: integer);
begin
  inherited Create;
  FPos := Pos;
  FCount := Count;
end;

procedure TecParserResults.ApplyStates(Rule: TRuleCollectionItem);
begin
  if Rule.StatesRemove <> 0 then
    FCurState := FCurState and not Rule.StatesRemove;
  if Rule.StatesAdd <> 0 then
    FCurState := FCurState or Rule.StatesAdd;
end;

procedure TecParserResults.RestoreState;
var i: integer;
begin
  for i := FStateChanges.Count - 1 downto 0 do
    if TRange(FStateChanges.Last).StartPos >= TagCount then
      FStateChanges.Delete(FStateChanges.Count - 1)
    else
      Break;
  if FStateChanges.Count > 0 then
    FCurState := TRange(FStateChanges.Last).EndPos
  else
    FCurState := 0;
end;

function TecParserResults.ParserStateAtPos(TokenIndex: integer): integer;
var i: integer;
begin
   for i := FStateChanges.Count - 1 downto 0 do
     if TRange(FStateChanges[i]).StartPos <= TokenIndex then
       begin
         Result := TRange(FStateChanges[i]).EndPos;
         Exit;
       end;
   Result := 0;
end;

{ TecChangeFixer }

constructor TecChangeFixer.Create;
begin
  inherited Create;
  FList := TObjectList.Create;
end;

destructor TecChangeFixer.Destroy;
begin
  FreeAndNil(FList);
  inherited;
end;

procedure TecChangeFixer.Clear;
begin
  FList.Clear;
end;

procedure TecChangeFixer.Add(Pos, Count: integer);
begin
  FList.Add(TTextChangeInfo.Create(Pos, Count));
end;

function TecChangeFixer.CurToOld(CurPos: integer): integer;
var i: integer;
begin
  for i := FList.Count - 1 downto 0 do
   with TTextChangeInfo(FList[i]) do
    if CurPos > FPos then
     if (FCount > 0) and (FPos + FCount > CurPos) then CurPos := FPos
      else Dec(CurPos, FCount);
  Result := CurPos;
end;

function TecChangeFixer.OldToCur(OldPos: integer): integer;
var i: integer;
begin
  for i := 0 to FList.Count - 1 do
   with TTextChangeInfo(FList[i]) do
    if OldPos >= FPos then
     if (FCount < 0) and (FPos - FCount > OldPos) then OldPos := FPos
      else Inc(OldPos, FCount);
  Result := OldPos;
end;

function TecChangeFixer.UpOldNoChange: integer;
var i, t: integer;
begin
  Result := 0;
  for i := 0 to FList.Count - 1 do
   with TTextChangeInfo(FList[i]) do
    begin
      if FPos < Result then
       if (FCount < 0) and (FPos - FCount > Result) then Result := FPos
        else Inc(Result, FCount);
      if FCount > 0 then t := FPos + FCount
       else t := FPos;
      if Result < t then Result := t;
    end;
end;


{ TecClientSyntAnalyzer }

constructor TecClientSyntAnalyzer.Create(AOwner: TecSyntAnalyzer; SrcProc: TATStringBuffer;
  const AClient: IecSyntClient);
begin
//  FFinishEvent := TEvent.Create(nil, True, False, '');
  FDataAccess := TCriticalSection.Create;

  inherited Create( AOwner, SrcProc, AClient);
  FRanges := TSortedList.Create(True);
  FLineBreaks := TObjectList.Create;
  FOpenedBlocks := TSortedList.Create(False);

  FChanges := TecChangeFixer.Create;
  FSavedTags := TRangeList.Create;
  FLineBreakRanges := TRangeCollection.Create;

  FIdleTimer := TTimer.Create(nil);
  FIdleTimer.OnTimer := IntIdleAppend;
  FIdleTimer.Enabled := False;
  FIdleTimer.Interval := 100;

  IdleAppend;
end;

destructor TecClientSyntAnalyzer.Destroy;
begin
  SafeDestroying(Self);

  if Assigned(FIdleTimer) then
    FIdleTimer.Enabled := False;
  FreeAndNil(FIdleTimer);

  FreeAndNil(FChanges);
  FreeAndNil(FSavedTags);
  FreeAndNil(FLineBreakRanges);
  FreeAndNil(FRanges);
  FreeAndNil(FLineBreaks);
  FreeAndNil(FOpenedBlocks);
  FreeAndNil(FDataAccess);
  inherited;
end;

procedure TecClientSyntAnalyzer.Clear;
begin
  Lock;
  try
    inherited;
    FRepeateAnalysis := False;
    FTagList.Clear;
    FRanges.Clear;
    FLineBreaks.Clear;
    FOpenedBlocks.Clear;
    FChanges.Clear;
    FSavedTags.Clear;
    FLineBreakRanges.Clear;

    FFinished := False;
    FBreakIdle := True;
    FLastAnalPos := 0;
    FStartSepRangeAnal := 0;
  finally
    Unlock;
  end;
  IdleAppend;
end;

procedure TecClientSyntAnalyzer.AddRange(Range: TecTextRange);
begin
  Range.FIndex := FRanges.Count;
  FRanges.Add(Range);
  if FOpenedBlocks.Count > 0 then
    Range.FParent := TecTextRange(FOpenedBlocks[FOpenedBlocks.Count - 1]);
  if Range.FEnd = -1 then
    FOpenedBlocks.Add(Range);
end;

function IndentOf(const S: ecString): Integer;
var
  i: Integer;
begin
  Result:= 0;
  for i:= 1 to Length(S) do
    case S[i] of
      ' ': Inc(Result);
      #9: Inc(Result, 4);
      else Break;
   end;
end;

function TecClientSyntAnalyzer.CloseRange(Cond: TecTagBlockCondition; RefTag: integer): Boolean;
var j: integer;
    b: boolean;
begin
  for j := FOpenedBlocks.Count - 1 downto 0 do
   with TecTextRange(FOpenedBlocks[j]) do
     if Assigned(FRule) then
       begin
         if Cond.BlockType = btRangeStart then
           b := Cond.SelfClose and (FRule = Cond)
         else
           b := (FRule.FBlockEndCond = Cond) or (FRule = Cond.FBlockEndCond);
         if b then
           begin
             if Cond.SameIdent and not SameText(TagStr[RefTag - Cond.IdentIndex] , TagStr[FIdent]) then Continue;
             FEnd := RefTag - Cond.BlockOffset;
             if (FRule = Cond) and (FEnd > 0) then Dec(FEnd); // for self closing
             FEndCondIndex := RefTag;
             if Assigned(Owner.OnCloseTextRange) then
               Owner.OnCloseTextRange(Self, TecTextRange(FOpenedBlocks[j]), FStart, FEnd);
             FOpenedBlocks.Delete(j);
             Result := True;
             Exit;
           end;
       end;
  Result := False;
end;

function TecClientSyntAnalyzer.HasOpened(Rule: TRuleCollectionItem; Parent: TecTagBlockCondition; Strict: Boolean): Boolean;
var i: integer;
    prn: TecTagBlockCondition;
begin
  if Strict then
    begin
      if FOpenedBlocks.Count > 0 then
        begin
          i := FOpenedBlocks.Count - 1;
          prn := TecTextRange(FOpenedBlocks[i]).Rule;
          if (Rule is TecTagBlockCondition) and TecTagBlockCondition(Rule).SelfClose and (prn = Rule) then
            Dec(i);
          repeat
            if i < 0 then
              begin
                Result := False;
                Exit;
              end;
            prn := TecTextRange(FOpenedBlocks[i]).Rule;
            Dec(i);
          until not prn.IgnoreAsParent;
          Result := prn = Parent;
        end else Result := Parent = nil;
    end
  else
    begin
      Result := True;
      if Parent = nil then Exit;
      for i := FOpenedBlocks.Count - 1 downto 0 do
        if TecTextRange(FOpenedBlocks[i]).Rule = Parent then
          Exit;
      Result := False;
    end;
end;

procedure TecClientSyntAnalyzer.Finished;
var i: integer;
begin
  if FFinished then Exit;
  inherited Finished;

  // Close SubLexers at the End of Text
  for i := FSubLexerBlocks.Count - 1 downto 0 do
   with TecSubLexerRange(FSubLexerBlocks[i]) do
    if (EndPos = -1) and Rule.ToTextEnd then
     begin
       FEndPos := FSrcProc.TextLength{ - 1};
       FCondEndPos := FEndPos;
     end;

  // Close blocks at the end of text
  CloseAtEnd(0);

  FRepeateAnalysis := True;
end;

procedure TecClientSyntAnalyzer.IntIdleAppend(Sender: TObject);
var FPos, tmp, i: integer;
    own: TecSyntAnalyzer;
begin
  if FIdleProc or FDisableIdleAppend then Exit;
  FIdleTimer.Enabled := False;
  FBreakIdle := False;
  FIdleProc := True;
  FPos := 0;
  try
    while not FBreakIdle and not FFinished do
    begin
     tmp := GetLastPos(FSrcProc.FText);
     if tmp > FPos then FPos := tmp;
     if ExtractTag(FSrcProc.FText, FPos, True) then
      begin
       if FOwner.SeparateBlockAnalysis then
        for i := FStartSepRangeAnal + 1 to TagCount do
         begin
          own := Tags[i - 1].Rule.SyntOwner;
          FOwner.SelectTokenFormat(Self, FSrcProc.FText, own <> FOwner, i);
          if own <> FOwner then
            own.SelectTokenFormat(Self, FSrcProc.FText, False, i);
          if SafeProcessMessages(Self) <> 0 then
            Exit; // Exit if analyzer is destroyed after processing messages
          if FBreakIdle then
            begin
              FIdleProc := False;
              Exit; // Exit when breaking
            end;
         end;
       Finished;
//       Exit;
      end else
      begin
        if SafeProcessMessages(Self) <> 0 then
          Exit;   // Exit if analyzer is destroyed after processing messages
      end;
    end;
  except
    //MessageBox(0, 'Error while idle', 'Error', MB_OK);
  end;
  FIdleProc := False;
end;

procedure TecClientSyntAnalyzer.IdleAppend;
begin
  if not FFinished then
   begin
     FIdleTimer.Enabled := False;
     if FRepeateAnalysis then
       FIdleTimer.Interval := Owner.IdleAppendDelay
     else
       FIdleTimer.Interval := Owner.IdleAppendDelayInit;
     FIdleTimer.Enabled := True;
   end;
end;

procedure TecClientSyntAnalyzer.AppendToPos(APos: integer);
var FPos: integer;
begin
  if Length(FSrcProc.FText) = 0 then Exit;
  if FFinished then Exit;
  FPos := GetLastPos(FSrcProc.FText);
  while FPos - 1 <= APos + 1 do
   begin
     if ExtractTag(FSrcProc.FText, FPos, False) then
      begin
       if not FOwner.SeparateBlockAnalysis then
         Finished else
       if not FIdleProc then
          IdleAppend; //IntIdleAppend(nil)
       Break;
      end;
   end;
end;

procedure TecClientSyntAnalyzer.TryAppend(APos: integer);
var sPos, sPos1: integer;
begin
  if FSavedTags.Count = 0 then AppendToPos(APos) else
   begin
    sPos1 := FChanges.OldToCur(TecSyntToken(FSavedTags[0]).StartPos);
    sPos := FChanges.UpOldNoChange;// + 1;
    if sPos1 > sPos then
       sPos := sPos1;
    if (sPos <> 0) and (sPos < APos) then AppendToPos(sPos)
     else AppendToPos(APos);
   end;
end;

procedure TecClientSyntAnalyzer.ChangedAtPos(APos: integer);
var i, N: integer;

 procedure CleanRangeList(List: TSortedList; IsClosed: Boolean);
 var i: integer;
 begin
   for i := List.Count - 1 downto 0 do
    with TecTextRange(List[i]) do
     if (FCondIndex >= N) or (FStart >= N) or IsClosed and
        ((FEndCondIndex >= N) or (FEnd >= N)) then
      List.Delete(i);
 end;

begin
{ if FSrcProc.TextLength <= Owner.FullRefreshSize then
  begin
   Clear;
   Exit;
  end;}

 Lock;
 try
   FFinished := False;
   Dec(APos);
   FBreakIdle := True;
   FIdleTimer.Enabled := False;
   if FSrcProc.TextLength <= Owner.FullRefreshSize then
    begin
      APos := 0;
    end else
   if Owner.RestartFromLineStart then
    begin
      N := FSrcProc.StrToCaret(APos + 1).Y;
      APos := min(APos, FSrcProc.CaretToStr(Classes.Point(0, N)));
    end;

   // Check sub lexer ranges
   for i := FSubLexerBlocks.Count - 1 downto 0 do
    with TecSubLexerRange(FSubLexerBlocks[i]) do
     if APos < StartPos then
      begin
        if APos > CondStartPos then APos := CondStartPos;
        FSubLexerBlocks.Delete(i);  // remove sub lexer
      end else
     if APos < CondEndPos then
      begin
        if APos > EndPos then APos := EndPos;
        FEndPos := -1;       // open sub lexer
        FCondEndPos := -1;
      end;
   // Remove tokens
   if FSavedTags.Count = 0 then
     FTagList.ClearFromPos(APos, FSavedTags)
   else
     FTagList.ClearFromPos(APos);
   FLastAnalPos := 0;   // Reset current position
   N := FTagList.Count;
   FStartSepRangeAnal := N;
   // Remove text ranges from service containers
   CleanRangeList(FOpenedBlocks, False);
   // Remove text ranges from main storage
   for i := FRanges.Count - 1 downto 0 do
    with TecTextRange(FRanges[i]) do
     if (FCondIndex >= N) or (FStart >= N) then FRanges.Delete(i)  else
      if (FEndCondIndex >= N) or (FEnd >= N) then
       begin
         FEnd := -1;
         FEndCondIndex := -1;
         FOpenedBlocks.Add(FRanges[i]);
       end;

   // Remove separators
   for i := FLineBreaks.Count - 1 downto 0 do
    if TecLineBreak(FLineBreaks[i]).RefIdx >= N then
      FLineBreaks.Delete(i);

   // Restore parser state
   RestoreState;
 finally
   Unlock;
 end;
 IdleAppend;
end;

function TecClientSyntAnalyzer.TokenAtPos(Pos: integer): integer;
begin
  AppendToPos(Pos);
  Result := FTagList.RangeAt(Pos);
end;

function TecClientSyntAnalyzer.PriorTokenAt(Pos: integer): integer;
begin
  Result := FTagList.PriorAt(Pos);
end;

procedure TecClientSyntAnalyzer.AddLineBreak(lb: TecLineBreak);
begin
  FLineBreaks.Add(lb);
end;

function TecClientSyntAnalyzer.GetRangeBound(Range: TecTextRange): TPoint;
begin
  Result.X := TecSyntToken(FTagList[Range.FStart]).StartPos;
  if Range.FEnd = - 1 then Result.Y := Result.X
   else Result.Y := TecSyntToken(FTagList[Range.FEnd]).EndPos;
end;

function TecClientSyntAnalyzer.GetColRangeBound(Range: TecTextRange): TPoint;
var sIdx, eIdx: integer;
begin
  sIdx := Range.StartIdx;
  eIdx := Range.EndIdx;
  if Assigned(Owner.OnGetCollapseRange) then
    Owner.OnGetCollapseRange(Self, Range, sIdx, eIdx);

  Result.X := Tags[sIdx].StartPos;
  if eIdx = -1 then Result.Y := Result.X
   else Result.Y := Tags[eIdx].EndPos;
end;

function TecClientSyntAnalyzer.RangeAtPos(APos: integer): TecTextRange;
begin
  Result := TecTextRange(FRanges.GetAt(APos));
end;

function TecClientSyntAnalyzer.RangeIdxAtPos(APos: integer): integer;
begin
  Result := FRanges.GetIndexAt(APos);
end;

function TecClientSyntAnalyzer.NearestRangeAtPos(APos: integer): TecTextRange;
var idx: integer;
begin
  idx := NearestRangeIdxAtPos(APos);
  if idx <> -1 then Result := Ranges[idx]
    else Result := nil;
{  idx := FRanges.PriorAt(APos);
  if idx <> -1 then
  for i := idx downto 0 do
   with TecTextRange(FRanges[i]) do
    if (EndIdx <> -1) and (Tags[EndIdx].EndPos >= APos) then
     begin
       Result := TecTextRange(FRanges[i]);
       Exit;
     end;
  Result := nil;}
end;

function TecClientSyntAnalyzer.NearestRangeIdxAtPos(APos: integer): integer;
begin
  Result := FRanges.PriorAt(APos);
  if Result <> -1 then
    while Result >= 0 do
     with TecTextRange(FRanges[Result]) do
      if (EndIdx <> -1) and (Tags[EndIdx].EndPos >= APos) then
        Exit
      else
        Dec(Result);
end;

function TecClientSyntAnalyzer.GetRangeCount: integer;
begin
  Result := FRanges.Count;
end;

function TecClientSyntAnalyzer.GetRanges(Index: integer): TecTextRange;
begin
  Result := TecTextRange(FRanges[Index]);
end;

procedure TecClientSyntAnalyzer.Analyze(ResetContent: Boolean);
var OldSep: integer;
begin
  if IsFinished then Exit;
  if ResetContent then
    begin
      OldSep := FOwner.FSeparateBlocks;
      FOwner.FSeparateBlocks := 2; // disanle separation analysis
      Clear;
      AppendToPos(Length(FSrcProc.FText));
      FOwner.FSeparateBlocks := OldSep;
    end else
    begin
      AppendToPos(Length(FSrcProc.FText));
    end;
end;

procedure TecClientSyntAnalyzer.CompleteAnalysis;
var own: TecSyntAnalyzer;
    i: integer;
begin
  AppendToPos(Length(FSrcProc.FText));
  if FOwner.SeparateBlockAnalysis then
    for i := FStartSepRangeAnal + 1 to TagCount do
     begin
      own := Tags[i - 1].Rule.SyntOwner;
      FOwner.SelectTokenFormat(Self, FSrcProc.FText, own <> FOwner, i);
      if own <> FOwner then
        own.SelectTokenFormat(Self, FSrcProc.FText, False, i);
      FBreakIdle := True;
      Finished;
     end;
end;

function TecClientSyntAnalyzer.RangeFormat(const FmtStr: ecString;
  Range: TecTextRange): ecString;
var i, j, idx, N, to_idx: integer;
    rng: TecTextRange;
    LineMode: integer;

{ HAW: hans@werschner.de [Oct'07] ......... additions to formatting token parts ......

     I have added syntax to each single ".... %xyz ...." clause processing

     a) %(S|E)P*(L|Z)?[0-9]+  may be expanded to

        %(S|E)P*([\[]<token>[\]]<offset>?)?

          where <token> is a specific token that is "searched from the specified
          starting point (S for first token in the range , or E for the last token)
          towards the respective range end (up- or downwards). The search-direction
          is kept in the variable "rngdir" which is set in the "S" , "E" decision.

          example:  line(s) is/are ".... for x = 1 to 12 do ...... end ;  ..."
                                         0   1 2 3 4  5  6  ...    27  28

          range-start = "for", range-end = "end"

          then "...%s[to] ..." will skip forward to the token "to" (with index 4).

          The token values are searched on a "asis" basis, there is no case-insensitivity
          option yet.

          A "numeric number following the token value will define an <offset> relative
          to the found token.

          For this clause, the variable "idx" is not set by taking the static
          numeric value as in "...%s2 ..." , instead the "found token index" is kept.

          For "%S..." the search starts at idx=0 up to max 28.     ---> rngdir = +1;
          For "%E..." the search starts at idx=28 downto min 0.    ---> rngdir = -1;

          The options L or Z introduced in V2.35 will not combine with the new (range)
          specifying options --> somebody else may find a use for such extended ranges.

          Notes:  Avoid to search for tokens that can occur at multiple places (for
                  example a ";" between statements).

                  The above syntax is simple as it allows to identify the

                    block-start-tokens      "for x = 1 to 12 do"

                    block-body                anything after block-start tokens up to

                    block-end-tokens        "end ;"

                  but many syntax formats do not trivially support this separation.

                  The current implementation does not provide the information where
                  "block-start", "block-body" and "block-end" are beginning/ending.
                  A "%B0..." for the "block-body" portion and a "ignore block-body
                  tokens" option may be nice !?

     b) any such clause (either absolute or given by token value) can "start a token
        range" by additionally specifying:

          %(S|E)...~(S|E)P*[0-9]+

        or

          %(S|E)...~(S|E)P*([\[]<token>[\]]<offset>?)?

        or

          %(S|E)...~[\[]<token>[\]]

        The first form uses the static index specification to define the end-range:

          "%s0~s3"        results in "for x = 1"            (tokens 0, 1, ... 3)

        The 2nd form uses the new syntax to "search for an end-token beginning at the
        starting range index (idx) up- or down-wards.

          "%s0~s[do]"     results in "for x = 1 to 12 do"   (tokens 0, 1, ... 6)

          if a search is not satisfied, the complete range up to "e0" is taken.

          Because of the same "S", the search starts with "TagStr[idx]" ...

          "s0~e[do]"      results in the same string, but starts at the final "end"
                          of the block and scanning downwards.

          Caution: This may produce WRONG results if nested loops are scanned !

                   I could not find a valid representation of "range-start" token-
                   streams, the range-body alone and/or the range-end token-stream.

                   Such information may be helpful to better display blocks and/or
                   collapse display of the "block-body" alone.

        The 3rd form is an abbreviation where the S/E indicators are taken to be
        identical as the starting point

          "S1~[do]1"      results in "x = 1 to 12"          (tokens 1, 2, ... 5)

                          The <offset> "1" will here skip back by 1 from the found
                          token "do".

        The range-end is kept in the variable "to_idx".

        The "token-value" to search for can not be whitespace #00..#20. Leading and
        trailing whitespace withing the "...[vvvvvvv] ..." enclosed by [ and ]
        characters sequence is removed before searching. the "vvvvvv" can contain
        escaped characters like "... [\]] ..." to allow "[" and/or "]" to be part
        of the value. The \r, \n, \f ...escapes are not supported here.

        The token accumulation simply (?) takes all tokens from "idx" ... "to_idx"
        and builds a string by appending all tokens with ONE " " (blank) as separating
        delimiter. There is no process to keep the original token positions within
        the source line(s) and any whitepace including cr/lf's there. This may be an
        addition but I currently do not see a need for it.

     c) "ranges as specified above may accumulate many tokens and it may be desirable
        to "limit" the result string.

        This can be done by using another operand syntax

          %(S|E)...~[0-9]+

        or

          %(S|E)...~(S|E)([\[]<token>[\]]<offset>?)?~[0-9]+

        or

          %(S|E)...~[\[]<token>[\]]~[0-9]+

        In all three forms the "~" is immediately followed by a numeric value which
        is interpreted as

          "maximum number of tokens in the substituted string", if the range takes
          MORE than this maximum

        The value is internally kept in the variable "rngmax" below.

        When the result string is accumulated (taking all tokens between "idx" up-
        resp. down-to "to_idx") the number of appended tokens can not go beyond "rngmax".

        If this happens the result will be created in the form "t-1 t-2 -- t-max ..." with
        the ellipsis string " ..."  appended.

     d) There is another addition to the token clause syntax which is NOT YET operational:

        I am not too happy that the collapsed string displayed is completely in "grey"
        colour. As I want to have some control over "highlighting" in this string also,
        I tried to add some style-reference information to the token clause.

        *** I currently do not yet understand HOW to activate such a style within
            the results of this formatting, but I will TRY !!

        OK, the added syntax for styles for future use ;-)

          .... %:<style>:(S|E) ....

        where the <style> is the alphanumeric name of a style as defined in the lexer
        definition and this style-name is "enclosed" with the ":" characters.

        The code keeps the found style-name in the variable "rngstyle" for any later
        use.

        This addition only assigns styles to the token substitution parts, but not to any
        text outside and/or the total "collapsed" text formatting. This may be some
        enhancement to define in the lexer GUI.

    Hans L. Werschner, Oct '07
}
var rngstyle: string;                      // HAW: add style identifier to range expression
    rngtoken, rngResult: string;           //      a few more vars
    swp_idx, rngdir, rngoffset, rngmax: integer;
    to_rng: TecTextRange;

function RangeNumber( const FmtStrNumber: string; var gotnbr: integer ): boolean;
begin
    N := 0; Result := false;
    while (j + N) <= length( FmtStrNumber ) do
      if (FmtStrNumber[j + N] >= '0') and (FmtStrNumber[j + N] <= '9') or (N = 0) and
         ((FmtStrNumber[j + N] = '+') or (FmtStrNumber[j + N] = '-'))
         then inc(N) else Break;
    if  N > 0  then  begin
      gotnbr := StrToInt( copy( FmtStrNumber, j, N ) );
      inc( j, N );
      Result := true;
    end;
end;

//var S_: string;
begin
  idx := 0;
  Result := FmtStr;
  try

   // HAW: obsolete -> to_idx := Length(Result);
   //      the variable "j" is now always pointing to the next character to process.
   //      Only during numeric sub-operand scan, the "N" will keep the found digits
   //      count. After such number, the var "j" is immediately adjusted again.

   for i := Length(Result) - 1 downto 1 do
    if Result[i] = '%' then
    begin
     j := i + 1;

     rngstyle := '';                  // HAW: keep style name
     if  Result[j] = ':' then  begin  // HAW: begin of embedded style name
       inc( j );
       while  (j <= length( Result ))  and  (Result[j] <> ':') do begin
         if  Result[j] > ' '  then
           rngstyle := rngstyle+Result[j];
         inc( j );
       end;
       if  (j > length( Result ))  or  (Result[j] <> ':')  then
         continue;
       inc( j );
       // now we have a style name, and can resume after "%:ssssss:"
     end;

     rng    := Range;
     rngdir := 1;                     // HAW: positive increment (for "%..e..")
                                      //      negative for "..e.." clauses
     rngmax := 1000000000;            // HAW: allow a great amount of tokens

     while ecUpCase(Result[j]) = 'P' do
      begin
       rng := rng.Parent;
       if (rng = nil) or (j = Length(Result)) then Continue;
       inc(j);
      end;

     case ecUpCase(Result[j]) of
       'S': idx := rng.StartIdx + rng.Rule.BlockOffset;
       'E': begin  rngdir := -1;      // HAW: mark downwards direction
                   if (rng.EndIdx <> -1) and Assigned(rng.Rule.BlockEndCond) then
                     idx := rng.EndIdx + rng.Rule.BlockEndCond.BlockOffset
                   else
                     idx := 1000000000;
            end;
       else continue;
     end;
     inc(j);

     case ecUpCase(Result[j]) of // <== v2.35
       'L': LineMode := 1; // from start of line
       'Z': LineMode := 2; // to end of line
       else LineMode := 0;
     end;
     if LineMode <> 0 then Inc(j);

     // HAW: check for "...s[token]..." instead of numeric index
     if  LineMode = 0  then
       if  (j < length( Result ))  and  (Result[j] = '[')  then  begin
         inc( j );  rngtoken := '';
         while  (j < length( Result ))  and  (Result[j] <> ']')  do  begin
           if  Result[j] = '\' then  inc( j );
           rngtoken := rngtoken + Result[j];  inc( j );
         end;
         if  j > length( Result ) then
           continue;
         while  (rngtoken <> '')  and  (rngtoken[length( rngtoken )] < ' ')  do
           rngtoken := copy( rngtoken, 1, length( rngtoken )-1 );
         while  (rngtoken <> '')  and  (rngtoken[1] < ' ')  do
           rngtoken := copy( rngtoken, 2, length( rngtoken )-1 );
         if  rngtoken = ''  then
           continue;
         inc( j );
         if  rngdir > 0 then  begin  // upwards search
           while  idx <= (rng.EndIdx + rng.Rule.BlockEndCond.BlockOffset) do  begin
             if  rngtoken = TagStr[idx]  then  break;
             inc( idx );
           end;
         end  else
         if  rngdir < 0 then         // downwards search
           while  idx >= (rng.StartIdx + rng.Rule.BlockOffset) do  begin
             if  rngtoken = TagStr[idx]  then  break;
             dec( idx );
           end;
         rngdir := 0;    // allow for missing <offset>
       end;
     if  not RangeNumber( Result, rngoffset )  then  begin
       if  rngdir <> 0 then
         Continue;
     end  else
       idx := idx - rngoffset;

     to_idx := idx;
     to_rng := rng;

     // HAW: now allow an explicit "to_idx" range by using "%from-idx~to-idx"
     if  (j < length( Result ))  and  (Result[j] = '~')  then
       // a numeric value alone sets just the maximum tokens to use
       if (Result[j+1] >= '0') and (Result[j+1] <= '9')  then  begin  // only positive values !
         to_idx := to_rng.EndIdx + to_rng.Rule.BlockEndCond.BlockOffset;
         LineMode := 3;
       end else
       begin

         if  LineMode <> 0  then  // not a good combination
           continue;
         // ... otherwise we have a real end-token clause
         inc( j );  // skip over the [

         rngdir := 1;
         if  Result[j] <> '['   then  begin
           // to_rng := Range;  // be sure that we start with the range itself
           while ecUpCase(Result[j]) = 'P' do
            begin
             to_rng := rng.Parent;
             if (to_rng = nil) or (j = Length(Result)) then Continue;
             inc(j);
            end;

           case ecUpCase(Result[j]) of
             'S': to_idx := to_rng.StartIdx + to_rng.Rule.BlockOffset;
             'E': begin
                    rngdir := -1;       // HAW: mark downwards direction
                    if (to_rng.EndIdx <> -1) and Assigned(to_rng.Rule.BlockEndCond) then
                     to_idx := to_rng.EndIdx + to_rng.Rule.BlockEndCond.BlockOffset
                    else
                     to_idx := 1000000000;
                  end;
             else continue;
           end;
           inc(j);
         end;
         if  (j < length( Result ))  and  (Result[j] = '[')  then  begin
           inc( j );  rngtoken := '';
           while  (j < length( Result ))  and  (Result[j] <> ']')  do  begin
             if  Result[j] = '\' then  inc( j );
             rngtoken := rngtoken + Result[j];  inc( j );
           end;
           if  j > length( Result ) then
             continue;
           while  (rngtoken <> '')  and  (rngtoken[length( rngtoken )] < ' ')  do
             rngtoken := copy( rngtoken, 1, length( rngtoken )-1 );
           while  (rngtoken <> '')  and  (rngtoken[1] < ' ')  do
             rngtoken := copy( rngtoken, 2, length( rngtoken )-1 );
           if  rngtoken = ''  then
             continue;
           inc( j );
           if  rngdir > 0 then  begin  // upwards search
             while  to_idx <= (rng.EndIdx + rng.Rule.BlockEndCond.BlockOffset) do  begin
               if  rngtoken = TagStr[to_idx]  then  break;
               inc( to_idx );
             end;
           end  else
           if  rngdir < 0 then         // downwards search
             while  to_idx >= (rng.StartIdx + rng.Rule.BlockOffset) do  begin
               if  rngtoken = TagStr[to_idx]  then  break;
               dec( to_idx );
             end;
           rngdir := 0;  // allow for missing <offset>
         end;
         if  not RangeNumber( Result, rngoffset )  then  begin
           if  rngdir <> 0 then
             Continue;
         end  else
           to_idx := to_idx - rngoffset;

         LineMode := 3;  // enforce new mode as we have an explicit range
       end;

     if  (j < length( Result ))  and
         (Result[j] = '~')         and
         (Result[j+1] >= '0') and (Result[j+1] <= '9')  // only positive values !
     then  begin  // we hav a "maximum" range value attached
       inc( j );
       if  not RangeNumber( Result, rngmax ) then
         Continue;
     end;
     // HAW: ... end of added range checking ,
     //      variable "j" points to first character AFTER all clauses
     Delete(Result, i, j - i);

     if (idx >= 0) and (idx < FTagList.Count) then
       case LineMode of
         0: Insert(TagStr[idx], Result, i);
         1: begin
              N := FSrcProc.StrToCaret(Tags[idx].StartPos).Y;
              N := FSrcProc.LineIndex(N);
              to_idx := Tags[idx].EndPos;
              Insert(FSrcProc.SubString(N, to_idx - N + 1), Result, i);
            end;
         2: begin
              N := FSrcProc.StrToCaret(Tags[idx].EndPos).Y;
              to_idx := FSrcProc.LineIndex(N) + FSrcProc.LineLength(N);
              N:= Tags[idx].StartPos + 1;
              Insert(FSrcProc.SubString(N, to_idx - N + 1), Result, i); //AT: needed "to_idx-N+1" to fix missing last chr in tree in Cuda
            end;
         // HAW: new mode = 3 --- explicit range  idx...to_idx
         3: if  (to_idx >= 0)  and  (to_idx < FTagList.Count)  then  begin
              if  to_idx < idx  then  begin
                swp_idx := idx;  idx := to_idx;  to_idx := swp_idx;
              end;
              rngResult := '';
              while  idx <= to_idx  do  begin
                if  rngmax <= 0   then  begin
                  rngResult := rngResult+' ...';
                  break;
                end;
                if  (rngResult <> '') and (idx > 0) and (Tags[idx-1].EndPos <> Tags[idx].StartPos) then //MZ fix
                  rngResult := rngResult + ' ';
                rngResult := rngResult + TagStr[idx];
                inc( idx );  dec( rngmax );
              end;
              Insert(rngResult, Result, i);
            end;
         // HAW: ... end of token range accumulation mode
      end;

      // HAW: I am currently not sure how to handle the "stylename" property here
      //      ... will be added, when available
    end;
   Exit;
  except
    Result := '';
  end;
end;

function TecClientSyntAnalyzer.GetRangeName(Range: TecTextRange): ecString;
begin
  Result := '';
  if Assigned(Range.Rule) and (Range.Rule.NameFmt <> '') then
     Result := RangeFormat(Range.Rule.NameFmt, Range);
  if Result = '' then
   Result := TagStr[Range.IdentIdx];
end;

function TecClientSyntAnalyzer.GetRangeGroup(Range: TecTextRange): ecString;
begin
  Result := RangeFormat(Range.Rule.GroupFmt, Range);
end;

function TecClientSyntAnalyzer.GetCollapsedText(Range: TecTextRange): ecString;
begin
  Result := RangeFormat(Range.Rule.CollapseFmt, Range);
end;

function TecClientSyntAnalyzer.IsEnabled(Rule: TRuleCollectionItem; OnlyGlobal: Boolean): Boolean;
begin
  Result := inherited IsEnabled(Rule, OnlyGlobal) and
      (HasOpened(Rule, Rule.Block, Rule.StrictParent) xor Rule.NotParent);
end;

function TecClientSyntAnalyzer.GetLineBreak(Line: integer): TecLineBreakRange;
var List: TList;

  function DoLine(Line: integer; Top: Boolean): TecLineBreakRange;
  var i, sp, ep: integer;
  begin
    Result := nil;
    if (Line >= FSrcProc.Count) or (Line < 0) then Exit;
    sp := FSrcProc.LineIndex(Line) - 1;
    ep := FChanges.CurToOld(sp + FSrcProc.LineSpace(Line));
    sp := FChanges.CurToOld(sp);
    FLineBreakRanges.GetRangesAtRange(List, sp, ep);
    for i := List.Count - 1 downto 0 do
     with TecLineBreakRange(List[i]) do
      if Top and (StartPos >= sp) and (Rule.LinePos = lbTop) or
         not Top and (EndPos < ep) and (Rule.LinePos = lbBottom) then
       begin
         Result := TecLineBreakRange(List[i]);
         Exit;
       end;
  end;

begin
  List := TList.Create;
  try
    Result := DoLine(Line, True);
    if not Assigned(Result) then
      Result := DoLine(Line - 1, False);
  finally
    FreeAndNil(List);
  end;
end;

procedure TecClientSyntAnalyzer.TextChanged(Pos, Count: integer); //AT: Line, LineChange were not used, deled
begin
  if Pos = -1 then Clear else
    begin
      ChangedAtPos(Pos);
      FChanges.Add(Pos, Count);
    end;
end;

function TecClientSyntAnalyzer.GetTagPos(Index: integer): TPoint;
var ln_pos, i: integer;
begin
  Result := FSrcProc.StrToCaret(Tags[Index].StartPos);
  ln_pos := FSrcProc.LineIndex(Result.y) - 1;
  Result.X := 0;
  for i := Index - 1 downto 0 do
   if Tags[i].EndPos > ln_pos then Result.X := Result.X + 1
    else Exit;
end;

function TecClientSyntAnalyzer.NextTokenAt(Pos: integer): integer;
begin
  Result := FTagList.NextAt(Pos);
end;

procedure TecClientSyntAnalyzer.Lock;
begin
  FDataAccess.Enter;
end;

procedure TecClientSyntAnalyzer.Unlock;
begin
  FDataAccess.Leave;
end;

function TecClientSyntAnalyzer.GetOpened(Index: integer): TecTextRange;
begin
  Result := TecTextRange(FOpenedBlocks[Index]);
end;

function TecClientSyntAnalyzer.GetOpenedCount: integer;
begin
  Result := FOpenedBlocks.Count;
end;

function TecClientSyntAnalyzer.GetAutoCloseText(Range: TecTextRange;
  const Indent: string): ecString;
var St: TStringList; //AT stringlist instead of ecStringList
    i: integer;
begin
  St := TStringList.Create;
  try
    St.Text := UTF8Encode(RangeFormat(Range.Rule.AutoCloseText, Range));
    for i := 0 to St.Count - 1 do
      St[i] := Indent + St[i];
    Result := UTF8Decode(St.Text);
    if Length(Result) > 2 then
      Delete(Result, Length(Result) - 1, 2);
  finally
    FreeAndNil(St);
  end;
end;

procedure TecClientSyntAnalyzer.SetDisableIdleAppend(const Value: Boolean);
begin
  if FDisableIdleAppend <> Value then
    begin
      FDisableIdleAppend := Value;
      if not IsFinished then
        IntIdleAppend(nil);
    end;
end;

function TecClientSyntAnalyzer.CreateLineBreak(Rule: TecTagBlockCondition;
  RefTag: integer): Boolean;
var lb: TecLineBreak;
begin
  lb := TecLineBreak.Create;
  lb.FRefTag := RefTag;
  lb.FRule := Rule;
  if Rule.LinePos = lbBottom then
    lb.FLine := FSrcProc.StrToCaret(Tags[RefTag].EndPos).Y + 1
  else
    lb.FLine := FSrcProc.StrToCaret(Tags[RefTag].StartPos).Y;
  AddLineBreak(lb);
  Result := True;
end;

function TecClientSyntAnalyzer.DetectTag(Rule: TecTagBlockCondition;
  RefTag: integer): Boolean;
begin
  Tags[RefTag].FRule := Rule;
  if Rule.TokenType >= 0 then
    Tags[RefTag].FTokenType := Rule.TokenType;
  Result := True;
end;

procedure TecClientSyntAnalyzer.CloseAtEnd(StartTagIdx: integer);
const
  cSpecIndentID = 20;
    //special number for "Group index" lexer property, which activates indent-based folding for a rule
  cSpecTokenStart: char = '1';
    //special char - must be first of token's type name (e.g. "1keyword");
    //Also such tokens must contain spaces+tabs at the beginning (use parser regex like "^[\x20\x09]*\w+")
var i, j, Ind: integer;
    Range: TecTextRange;
    s: string;
begin
  for i := FOpenedBlocks.Count - 1 downto 0 do
   begin
    Range := TecTextRange(FOpenedBlocks[i]);
    if Range.FRule.EndOfTextClose and
       ((StartTagIdx = 0) or (Range.StartIdx >= StartTagIdx)) then
     begin
       Range.FEnd := TagCount - 1;
       if Range.FRule.GroupIndex = cSpecIndentID then
       begin
         Ind := IndentOf(TagStr[Range.StartIdx]);
         for j := Range.StartIdx+1 to TagCount-1 do
         begin
           s := Owner.TokenTypeNames[Tags[j].FTokenType];
           if (s[1] = cSpecTokenStart) and (IndentOf(TagStr[j]) <= Ind) then
           begin
             Range.FEnd := j-1;
             Break
           end;
         end;
       end;
       FOpenedBlocks.Delete(i);
     end;
   end;
end;

{ TecSyntAnalyzer }

constructor TecSyntAnalyzer.Create(AOwner: TComponent);
begin
  inherited;
  FClientList := TList.Create;
  FMasters := TList.Create;
  FSampleText := TStringList.Create;
  FTokenTypeNames := TStringList.Create;
  FTokenTypeNames.Text := SecDefaultTokenTypeNames;
  TStringList(FTokenTypeNames).OnChange := TokenNamesChanged;

  FFormats := TecStylesCollection.Create;
  FFormats.OnChange := FormatsChanged;
  FFormats.SyntOwner := Self;

  FTokenRules := TecTokenRuleCollection.Create;
  FTokenRules.OnChange := TokenRuleChanged;
  FTokenRules.SyntOwner := Self;

  FBlockRules := TecBlockRuleCollection.Create;
  FBlockRules.OnChange := BlocksChanged;
  FBlockRules.SyntOwner := Self;

  FSubAnalyzers := TecSubAnalyzerRules.Create;
  FSubAnalyzers.SyntOwner := Self;
  FSubAnalyzers.OnChange := SubLexRuleChanged;

  FMarkedBlock := FFormats.Add as TecSyntaxFormat;
  FMarkedBlock.BgColor := clHighlight;
  FMarkedBlock.Font.Color := clHighlightText;
  FMarkedBlock.FormatType := ftColor;
  FMarkedBlock.DisplayName := 'Marked block';
  FMarkedBlock.FIsBlock := True;

  FCodeTemplates := TecCodeTemplates.Create(Self);
  FSkipSpaces := True;

  FNotes := TStringList.Create;

  FGrammaParser := TGrammaAnalyzer.Create;
  FGrammaParser.OnChange := GrammaChanged;

  FIdleAppendDelayInit := 50;
  FIdleAppendDelay := 200;
end;

destructor TecSyntAnalyzer.Destroy;
begin
  FBlockRules.OnChange := nil;
  FTokenRules.OnChange := nil;
  FFormats.OnChange := nil;
  FSubAnalyzers.OnChange := nil;
  TStringList(FTokenTypeNames).OnChange:= nil;
  FGrammaParser.OnChange := nil;

  FreeAndNil(FFormats);
  FreeAndNil(FMasters);
  FreeAndNil(FSampleText);
  FreeAndNil(FBlockRules);
  FreeAndNil(FTokenRules);
  FreeAndNil(FCodeTemplates);
  FreeAndNil(FTokenTypeNames);
  FreeAndNil(FSubAnalyzers);
  FreeAndNil(FNotes);
  FreeAndNil(FGrammaParser);
  inherited;
  FreeAndNil(FClientList);
end;

procedure TecSyntAnalyzer.Assign(Source: TPersistent);
var Src: TecSyntAnalyzer;
    i: integer;
begin
  if not (Source is TecSyntAnalyzer) then Exit;
  Src := Source as TecSyntAnalyzer;
//  ClearClientContents;
  FCoping := True;
  try
    FAlwaysSyncBlockAnal := Src.FAlwaysSyncBlockAnal;
    Extentions := Src.Extentions;
    LexerName := Src.LexerName;
    SkipSpaces := Src.SkipSpaces;
    SampleText := Src.SampleText;
    FullRefreshSize := Src.FullRefreshSize;
    Formats := Src.Formats;
    MarkedBlockStyle := Src.MarkedBlockStyle;
    SearchMatchStyle := Src.SearchMatchStyle;
    CurrentLineStyle := Src.CurrentLineStyle;
    DefaultStyleName := Src.DefaultStyleName;
    CollapseStyleName := Src.CollapseStyleName;
    BlockRules := Src.BlockRules;
    TokenRules := Src.TokenRules;
    CodeTemplates := Src.CodeTemplates;
    SubAnalyzers := Src.SubAnalyzers;
//    DefaultStyle := Src.DefaultStyle;
    TokenTypeNames := Src.TokenTypeNames;
    Notes := Src.Notes;
    Internal := Src.Internal;
    Gramma := Src.Gramma;
    RestartFromLineStart := Src.RestartFromLineStart;
    ParseEndOfLine := Src.ParseEndOfLine;
    LineComment := Src.LineComment;
    FIdleAppendDelayInit := Src.FIdleAppendDelayInit;
    FIdleAppendDelay := Src.FIdleAppendDelay;
    for i := 0 to BlockRules.Count - 1 do
     begin
       BlockRules[i].BlockEnd := Src.BlockRules[i].BlockEnd;
       BlockRules[i].BlockName := Src.BlockRules[i].BlockName;
     end;
  finally
    FCoping := False;
    ClearClientContents;
  end;
  UpdateClients;
  for i := 0 to FClientList.Count - 1 do
   TecClientSyntAnalyzer(FClientList[i]).IdleAppend;
end;

procedure TecSyntAnalyzer.HighlightKeywords(Client: TecParserResults;
  const Source: ecString; OnlyGlobal: Boolean);
var i, N, ki, RefIdx: integer;
    Accept: Boolean;
begin
  N := Client.TagCount;
  for i := 0 to FBlockRules.Count - 1 do
   with FBlockRules[i] do
    if Enabled and (BlockType = btTagDetect) and
       (Block = nil) and (FGrammaRule = nil) then
      begin
       if OnlyGlobal and not AlwaysEnabled then Continue;
       RefIdx := 0;
       Accept := Check(Source, TecClientSyntAnalyzer(Client), N, RefIdx);
       if Assigned(OnBlockCheck) then
         OnBlockCheck(FBlockRules[i], TecClientSyntAnalyzer(Client), Source, RefIdx, Accept);
       if Accept then
         begin
           if FRefToCondEnd then ki := RefIdx - IdentIndex
             else ki := N - 1 - CheckOffset - IdentIndex;
           TecClientSyntAnalyzer(Client).Tags[ki].FRule := FBlockRules[i];
           if TokenType >= 0 then
              TecClientSyntAnalyzer(Client).Tags[ki].FTokenType := TokenType;
           if CancelNextRules then Exit;   // 2.27
         end;
      end;
end;

procedure TecSyntAnalyzer.SelectTokenFormat(Client: TecParserResults;
            const Source: ecString; OnlyGlobal: Boolean; N: integer);
var i, li, ki, strt, RefIdx: integer;
    Range: TecTextRange;
    Accept: Boolean;
    RClient: TecClientSyntAnalyzer;
  function CheckIndex(Idx: integer): Boolean;
  begin
   Result := (Idx >= 0) and (Idx < N);
  end;
begin
  if N = -1 then
    N := Client.TagCount;
  if not (Client is TecClientSyntAnalyzer)  then Exit;
  RClient := TecClientSyntAnalyzer(Client);
  RClient.FStartSepRangeAnal := N + 1;
  try
    for i := 0 to FBlockRules.Count - 1 do
      with FBlockRules[i] do
       if not SeparateBlockAnalysis or (BlockType <> btTagDetect) or
          (Block = nil) or (FGrammaRule = nil) then
       if Client.IsEnabled(FBlockRules[i], OnlyGlobal) then
        begin
          RefIdx := 0;
          if FGrammaRule <> nil then
           begin
             RefIdx := FGrammaParser.TestRule(N - 1, FGrammaRule, Client);
             Accept := RefIdx <> -1;
           end else
             Accept := Check(Source, RClient, N, RefIdx);

          if Assigned(OnBlockCheck) then
            OnBlockCheck(FBlockRules[i], RClient, Source, RefIdx, Accept);

          if Accept then
          begin
           Client.ApplyStates(FBlockRules[i]);
           if FRefToCondEnd then strt := RefIdx
             else strt := N - 1 - CheckOffset;
      //    strt := N - 1 - CheckOffset;
           ki := strt - IdentIndex;
           if CheckIndex(ki) then
            case BlockType of
               btTagDetect: // Tag detection
                 if not RClient.DetectTag(FBlockRules[i], ki) then
                   Continue;
               btRangeStart: // Start of block
                begin
                  if FBlockRules[i].SelfClose then
                    RClient.CloseRange(FBlockRules[i], strt);
                  li := strt - BlockOffset;
                  if CheckIndex(li) then
                   begin
                    Range := TecTextRange.Create(li, RClient.Tags[li].StartPos);
                    Range.FIdent := ki;
                    Range.FRule := FBlockRules[i];
                    Range.FCondIndex := N - 1;
                    if NoEndRule then
                     begin
                      Range.FEnd := N - 1 - CheckOffset;
                      Range.FEndCondIndex := N - 1;
                      Range.FStart := RefIdx - BlockOffset;
                     end;
                    RClient.AddRange(Range);
                   end;
                end;
               btRangeEnd:  // End of block
                 if not RClient.CloseRange(FBlockRules[i], strt) then
                   Continue;
               btLineBreak:
                 if not RClient.CreateLineBreak(FBlockRules[i], ki) then
                   Continue;
            end;
           if CancelNextRules then Break;
          end;
        end;
  except
    Application.HandleException(Self);
  end;
end;

function TecSyntAnalyzer.AddClient(const Client: IecSyntClient;
                         SrcProc: TATStringBuffer): TecClientSyntAnalyzer;
begin
  Result := TecClientSyntAnalyzer.Create(Self, SrcProc, Client);
end;

procedure TecSyntAnalyzer.SetSampleText(const Value: TStrings);
begin
  FSampleText.Assign(Value);
end;

function TecSyntAnalyzer.GetToken(Client: TecParserResults; const Source: ecString;
                              APos: integer; OnlyGlobal: Boolean): TecSyntToken;
var i, N, lp: integer;
    Rule: TecTokenRule;
begin
  if Assigned(FOnParseToken) then
    begin
      N := 0;
      Rule := nil;
      FOnParseToken(Client, Source, APos, N, Rule);
      if Assigned(Rule) then
        Result := TecSyntToken.Create(Rule, APos - 1, APos + N - 1)
      else
        Result := nil;
      Exit;
    end;

  lp := 0;
  for i := 0 to FTokenRules.Count - 1 do
    begin
      Rule := FTokenRules[i];
      if Client.IsEnabled(Rule, OnlyGlobal) then
        with Rule do
          begin
            if (ColumnFrom > 0) or (ColumnTo > 0) then
              begin
               if lp = 0 then lp := Client.FSrcProc.StrToCaret(APos - 1).X + 1;
               if (ColumnFrom > 0) and (lp < ColumnFrom) or
                  (ColumnTo > 0) and (lp > ColumnTo) then
                  Continue;
              end;
            N := Match(Source, APos);
            if Assigned(OnMatchToken) then
              OnMatchToken(Rule, Client, Source, APos, N);
            if N > 0 then
              begin
                Client.ApplyStates(Rule);
                Result := TecSyntToken.Create(Rule, APos - 1, APos + N - 1);
                Exit;
              end;
          end;
    end;
  Result := nil;
end;

procedure TecSyntAnalyzer.FormatsChanged(Sender: TCollection; Item: TSyntCollectionItem);
var i: integer;
begin
  ClearClientContents;
  if Item = nil then
   begin
    if not FFormats.ValidItem(FMarkedBlock) then FMarkedBlock := nil;
    if not FFormats.ValidItem(FCurrentLine) then FCurrentLine := nil;
    if not FFormats.ValidItem(FDefStyle) then FDefStyle := nil;
    if not FFormats.ValidItem(FSearchMatch) then FSearchMatch := nil;
    for i := 0 to FBlockRules.Count - 1 do
     begin
      if not FFormats.ValidItem(FBlockRules[i].Style) then FBlockRules[i].Style := nil;
      if not FFormats.ValidItem(FBlockRules[i].TreeItemStyleObj) then FBlockRules[i].FTreeItemStyleObj := nil;
      if not FFormats.ValidItem(FBlockRules[i].TreeGroupStyleObj) then FBlockRules[i].FTreeGroupStyleObj := nil;
     end;
    for i := 0 to FTokenRules.Count - 1 do
     if not FFormats.ValidItem(FTokenRules[i].Style) then FTokenRules[i].Style := nil;
    for i := 0 to FSubAnalyzers.Count - 1 do
     if not FFormats.ValidItem(FSubAnalyzers[i].Style) then FSubAnalyzers[i].Style := nil;
   end;
//  UpdateClients;
  Change;
end;

procedure TecSyntAnalyzer.BlocksChanged(Sender: TCollection;
  Item: TSyntCollectionItem);
var i: integer;
begin
  ClearClientContents;
  if Item = nil then
   begin
    for i := 0 to FBlockRules.Count - 1 do
     begin
      if not FBlockRules.ValidItem(FBlockRules[i].Block) then FBlockRules[i].Block := nil;
      if not FBlockRules.ValidItem(FBlockRules[i].BlockEndCond) then FBlockRules[i].BlockEndCond := nil;
     end;
    for i := 0 to FTokenRules.Count - 1 do
     if not FBlockRules.ValidItem(FTokenRules[i].Block) then FTokenRules[i].Block := nil;
    for i := 0 to FSubAnalyzers.Count - 1 do
     if not FSubAnalyzers.ValidItem(FSubAnalyzers[i].Block) then FSubAnalyzers[i].Block := nil;
   end;
//  UpdateClients;
  Change;
end;

procedure TecSyntAnalyzer.ClearClientContents;
var i:integer;
begin
  if FCoping then Exit;
  FCoping := True;
  try
    for i := 0 to FClientList.Count - 1 do
     with TecClientSyntAnalyzer(FClientList[i]) do
      begin
        Clear;
        IdleAppend;
      end;
    for i := 0 to FMasters.Count - 1 do
      TecSyntAnalyzer(FMasters[i]).ClearClientContents;
  finally
    FCoping := False;
  end;
  UpdateClients;
end;

procedure TecSyntAnalyzer.UpdateClients;
var i:integer;
begin
  if FCoping then Exit;
  FCoping := True;
  try
    for i := 0 to FClientList.Count - 1 do
     with TecClientSyntAnalyzer(FClientList[i]) do
       if FClient <> nil then
         FClient.FormatChanged;
    for i := 0 to FMasters.Count - 1 do
      TecSyntAnalyzer(FMasters[i]).UpdateClients;
  finally
    FCoping := False;
  end;
end;

procedure TecSyntAnalyzer.Loaded;
var i: integer;
begin
  inherited;
  MarkedBlockStyle := FMarkedBlockName;
  SearchMatchStyle := FSearchMatchName;
  CurrentLineStyle := FCurrentLineName;
  CollapseStyleName := FCollapseStyleName;
  DefaultStyleName := FDefStyleName;

  FFormats.Loaded;
  FBlockRules.Loaded;
  FTokenRules.Loaded;
  FSubAnalyzers.Loaded;
  CompileGramma;
  DetectBlockSeparate;
  for i := 0 to FMasters.Count - 1 do
    TecSyntAnalyzer(FMasters[i]).DetectBlockSeparate;
end;

procedure TecSyntAnalyzer.SetBlockRules(const Value: TecBlockRuleCollection);
begin
  FBlockRules.Assign(Value);
  ClearClientContents;
end;

procedure TecSyntAnalyzer.SetCodeTemplates(const Value: TecCodeTemplates);
begin
  FCodeTemplates.Assign(Value);
  ClearClientContents;
end;

procedure TecSyntAnalyzer.SetTokenRules(const Value: TecTokenRuleCollection);
begin
  FTokenRules.Assign(Value);
  ClearClientContents;
end;

procedure TecSyntAnalyzer.SetFormats(const Value: TecStylesCollection);
begin
  FFormats.Assign(Value);
end;

function TecSyntAnalyzer.GetUniqueName(const Base: string): string;
var n: integer;
begin
  n := 1;
  if Owner = nil then Result := Base + '1' else
  repeat
   Result := Base + IntToStr(n);
   inc(n);
  until Owner.FindComponent(Result) = nil;
end;

procedure TecSyntAnalyzer.SetSkipSpaces(const Value: Boolean);
begin
  if FSkipSpaces <> Value then
   begin
     FSkipSpaces := Value;
     ClearClientContents;
   end;
end;

procedure TecSyntAnalyzer.SetSubAnalyzers(const Value: TecSubAnalyzerRules);
begin
  FSubAnalyzers.Assign(Value);
  ClearClientContents;
end;

procedure TecSyntAnalyzer.Notification(AComponent: TComponent;
  Operation: TOperation);
var i: integer;
begin
  inherited;
  if (Operation = opRemove)  and (AComponent <> Self) and (aComponent is TecSyntAnalyzer) and
     Assigned(FSubAnalyzers) and Assigned(FMasters) then
   begin
     for i := 0 to FSubAnalyzers.Count - 1 do
      if FSubAnalyzers[i].FSyntAnalyzer = AComponent then
       FSubAnalyzers[i].FSyntAnalyzer := nil;
     FMasters.Remove(AComponent);
   end;
end;

procedure TecSyntAnalyzer.SubLexRuleChanged(Sender: TCollection;
  Item: TSyntCollectionItem);
begin
  DetectBlockSeparate;
  ClearClientContents;
  Change;
end;

procedure TecSyntAnalyzer.AddMasterLexer(SyntAnal: TecSyntAnalyzer);
begin
  if Assigned(SyntAnal) and (SyntAnal <> Self) and
     (FMasters.IndexOf(SyntAnal) = -1) then
   begin
     FMasters.Add(SyntAnal);
     SyntAnal.FreeNotification(Self);
   end;
end;

procedure TecSyntAnalyzer.RemoveMasterLexer(SyntAnal: TecSyntAnalyzer);
begin
  FMasters.Remove(SyntAnal);
end;

procedure TecSyntAnalyzer.TokenRuleChanged(Sender: TCollection;
  Item: TSyntCollectionItem);
begin
  DetectBlockSeparate;
  ClearClientContents;
  Change;
end;

procedure TecSyntAnalyzer.SetTokenTypeNames(const Value: TStrings);
begin
  FTokenTypeNames.Assign(Value);
end;

procedure TecSyntAnalyzer.Change;
begin
  if Assigned(FOnChange) then FOnChange(Self);
end;

procedure TecSyntAnalyzer.SetSearchMatch(const Value: TecSyntaxFormat);
begin
  if FSearchMatch = Value then Exit;
  FSearchMatch := Value;
  UpdateClients;
  Change;
end;

procedure TecSyntAnalyzer.SetMarkedBlock(const Value: TecSyntaxFormat);
begin
  if FMarkedBlock = Value then Exit;
  FMarkedBlock := Value;
  UpdateClients;
  Change;
end;

procedure TecSyntAnalyzer.SetCurrentLine(const Value: TecSyntaxFormat);
begin
  if FCurrentLine = Value then Exit;
  FCurrentLine := Value;
  UpdateClients;
  Change;
end;

procedure TecSyntAnalyzer.SetDefStyle(const Value: TecSyntaxFormat);
begin
  if FDefStyle = Value then Exit;
  FDefStyle := Value;
  UpdateClients;
  Change;
end;

function TecSyntAnalyzer.GetStyleName(const AName: string; const AStyle: TecSyntaxFormat): string;
begin
  if csLoading in ComponentState then
    Result := AName
  else
   if Assigned(AStyle) then
    Result := AStyle.DisplayName
   else
    Result := '';
end;

function TecSyntAnalyzer.GetMarkedBlockName: string;
begin
  Result := GetStyleName(FMarkedBlockName, FMarkedBlock);
end;

procedure TecSyntAnalyzer.SetMarkedBlockName(const Value: string);
begin
  if csLoading in ComponentState then
    FMarkedBlockName := Value
  else
    MarkedBlock := TecSyntaxFormat(FFormats.ItemByName(Value));
end;

function TecSyntAnalyzer.GetSearchMatchStyle: string;
begin
  Result := GetStyleName(FSearchMatchName, FSearchMatch);
end;

procedure TecSyntAnalyzer.SetSearchMatchStyle(const Value: string);
begin
  if csLoading in ComponentState then
    FSearchMatchName := Value
  else
    FSearchMatch := TecSyntaxFormat(FFormats.ItemByName(Value));
end;

function TecSyntAnalyzer.GetCurrentLineStyle: string;
begin
  Result := GetStyleName(FCurrentLineName, FCurrentLine);
end;

procedure TecSyntAnalyzer.SetCurrentLineStyle(const Value: string);
begin
  if csLoading in ComponentState then
    FCurrentLineName := Value
  else
    FCurrentLine := TecSyntaxFormat(FFormats.ItemByName(Value));
end;

function TecSyntAnalyzer.GetDefaultStyleName: string;
begin
  Result := GetStyleName(FDefStyleName, FDefStyle);
end;

procedure TecSyntAnalyzer.SetDefaultStyleName(const Value: string);
begin
  if csLoading in ComponentState then
    FDefStyleName := Value
  else
    FDefStyle := TecSyntaxFormat(FFormats.ItemByName(Value));
end;

procedure TecSyntAnalyzer.SetNotes(const Value: TStrings);
begin
  FNotes.Assign(Value);
end;

procedure TecSyntAnalyzer.SetInternal(const Value: boolean);
begin
  FInternal := Value;
end;

procedure TecSyntAnalyzer.SetRestartFromLineStart(const Value: Boolean);
begin
  FRestartFromLineStart := Value;
end;

procedure TecSyntAnalyzer.SetParseEndOfLine(const Value: Boolean);
begin
  if FParseEndOfLine <> Value then
    begin
      FParseEndOfLine := Value;
      ClearClientContents;
    end;
end;

procedure TecSyntAnalyzer.CompileGramma;
var i: integer;
begin
  FGrammaParser.CompileGramma(FTokenTypeNames);
  for i := 0 to FBlockRules.Count - 1 do
    FBlockRules[i].FGrammaRule :=
     FGrammaParser.ParserRuleByName(FBlockRules[i].FGrammaRuleName);
end;

procedure TecSyntAnalyzer.TokenNamesChanged(Sender: TObject);
begin
  CompileGramma;
  Change;
end;

procedure TecSyntAnalyzer.SetGrammar(const Value: TGrammaAnalyzer);
begin
  FGrammaParser.Assign(Value);
  CompileGramma;
end;

procedure TecSyntAnalyzer.GrammaChanged(Sender: TObject);
begin
  CompileGramma;
end;

procedure TecSyntAnalyzer.SetLineComment(const Value: ecString);
begin
  FLineComment := Value;
end;

function TecSyntAnalyzer.GetSeparateBlocks: Boolean;
  function HasStateModif(List: TCollection): Boolean;
  var i: integer;
  begin
    for i := 0 to List.Count - 1 do
      with TRuleCollectionItem(List.Items[i]) do
        if (StatesAdd <> 0) or (StatesRemove <> 0) then
          begin
            Result := True;
            Exit;
          end;
    Result := False;
  end;
var i: integer;
begin
  if FSeparateBlocks = 0 then
    begin
      Result := not FAlwaysSyncBlockAnal and
                not HasStateModif(FBlockRules) and
                not HasStateModif(FSubAnalyzers);
      if Result then
        for i := 0 to TokenRules.Count - 1 do
          if TokenRules[i].Block <> nil then
            begin
              Result := False;
              Break;
            end;
      if Result then
        for i := 0 to SubAnalyzers.Count - 1 do
          if (SubAnalyzers[i].SyntAnalyzer <> nil) and
             not SubAnalyzers[i].SyntAnalyzer.SeparateBlockAnalysis then
            begin
              Result := False;
              Break;
            end;
      if Result then
        FSeparateBlocks := 1
      else
        FSeparateBlocks := 2;
    end
  else
    Result := FSeparateBlocks = 1;
end;

procedure TecSyntAnalyzer.DetectBlockSeparate;
begin
  FSeparateBlocks := 0;
end;

procedure TecSyntAnalyzer.SetAlwaysSyncBlockAnal(const Value: Boolean);
begin
  FAlwaysSyncBlockAnal := Value;
  if FAlwaysSyncBlockAnal and SeparateBlockAnalysis then
   begin
    DetectBlockSeparate;
    ClearClientContents;
   end;
end;

function TecSyntAnalyzer.GetCollapseStyleName: string;
begin
  Result := GetStyleName(FCollapseStyleName, FCollapseStyle);
end;

procedure TecSyntAnalyzer.SetCollapseStyleName(const Value: string);
begin
  if csLoading in ComponentState then
    FCollapseStyleName := Value
  else
    FCollapseStyle := TecSyntaxFormat(FFormats.ItemByName(Value));
end;

procedure TecSyntAnalyzer.SetCollapseStyle(const Value: TecSyntaxFormat);
begin
  if FCollapseStyle <> Value then
    begin
      FCollapseStyle := Value;
      UpdateClients;
      Change;
    end;
end;

{ TecCodeTemplate }

constructor TecCodeTemplate.Create(Collection: TCollection);
begin
  inherited;
  FName:= '';
  FDescription:= '';
  FAdvanced:= false;
  FCode:= TStringList.Create;
end;

destructor TecCodeTemplate.Destroy;
begin
  FreeAndNil(FCode);
  inherited;
end;

function TecCodeTemplate.GetDisplayName: string;
begin
  Result := FName;
end;


{ TecCodeTemplates }

function TecCodeTemplates.Add: TecCodeTemplate;
begin
  Result := TecCodeTemplate(inherited Add);
end;

constructor TecCodeTemplates.Create(AOwner: TPersistent);
begin
  inherited Create(AOwner, TecCodeTemplate);
end;

function TecCodeTemplates.GetItem(Index: integer): TecCodeTemplate;
begin
  Result := TecCodeTemplate(inherited Items[Index]);
end;

{ TecSyntaxManager }

function TecSyntaxManager.AddAnalyzer: TecSyntAnalyzer;
begin
  Result := TLibSyntAnalyzer.Create(Owner);
  Result.Name := Result.GetUniqueName('SyntAnal');
  Result.SetParentComponent(Self);
  FModified := True;
end;

procedure TecSyntaxManager.Clear;
begin
  while FList.Count > 0 do
  begin
    TObject(FList[0]).Free;
  end;

  Changed;
  FModified := True;
end;

constructor TecSyntaxManager.Create(AOwner: TComponent);
begin
  inherited;
  FList := TList.Create;
  FModified := False;
end;

destructor TecSyntaxManager.Destroy;
begin
  FOnChange := nil;
  Clear;
  FreeAndNil(FList);
  inherited;
end;

procedure TecSyntaxManager.Changed;
begin
  if Assigned(FOnChange) then FOnChange(Self);
end;

function TecSyntaxManager.GeItem(Index: integer): TecSyntAnalyzer;
begin
  Result := TecSyntAnalyzer(FList[Index]);
end;

procedure TecSyntaxManager.GetChildren(Proc: TGetChildProc;
  Root: TComponent);
var i: integer;
begin
  if not (csDestroying in ComponentState) then
   for i := 0 to FList.Count - 1 do
    Proc(TComponent(FList[i]));
end;

function TecSyntaxManager.GetCount: integer;
begin
  Result := FList.Count;
end;

procedure TecSyntaxManager.LoadFromFile(const FileName: string);
begin
  Clear;
  inherited;
  Changed;
  FModified := False;
end;

procedure TecSyntaxManager.SaveToFile(const FileName: string);
begin
  inherited;
  FModified := False;
end;

procedure TecSyntaxManager.Move(CurIndex, NewIndex: Integer);
begin
  FList.Move(CurIndex, NewIndex);
  FModified := True;
end;

procedure TecSyntaxManager.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;
end;

procedure TecSyntaxManager.SetCurrentLexer(const Value: TecSyntAnalyzer);
begin
  if (FCurrentLexer <> Value) and ((Value = nil) or (FList.IndexOf(value) <> -1)) then
   begin
     FCurrentLexer := Value;
   end;
end;

function TecSyntaxManager.FindAnalyzer(
  const LexerName: string): TecSyntAnalyzer;
var i: integer;
begin
  for i := 0 to GetCount - 1 do
   if SameText(Analyzers[i].LexerName, LexerName) then
     begin
      Result := Analyzers[i];
      Exit;
     end;
  Result := nil;
end;

procedure TecSyntaxManager.OnReadError(Reader: TReader;
  const Message: string; var Handled: Boolean);
var S: string;
begin
  if not FIgnoreAll then
   begin
    if AnalyzerCount > 0 then
      S := 'Error in lexer: '+Analyzers[AnalyzerCount - 1].Name +'. '
    else
      S := '';
    S := S + Message;
    inherited OnReadError(Reader, S, Handled);
   end else
  inherited;
end;

{ TLibSyntAnalyzer }

constructor TLibSyntAnalyzer.Create(AOwner: TComponent);
begin
  if Assigned(AOwner) and (AOwner is TecSyntaxManager) then
   inherited Create((AOwner as TecSyntaxManager).Owner)
  else
   inherited Create(AOwner);
end;

destructor TLibSyntAnalyzer.Destroy;
begin
  if FParent <> nil then
   begin
     FParent.FList.Remove(Self);
     FParent := nil;
   end;
  inherited;
end;

function TLibSyntAnalyzer.GetParentComponent: TComponent;
begin
  Result := FParent;
end;

function TLibSyntAnalyzer.HasParent: Boolean;
begin
  Result := True;
end;

procedure TLibSyntAnalyzer.LoadFromStream(const Stream: TStream);
begin
  inherited LoadFromStream(Stream);
end;

procedure TLibSyntAnalyzer.SetParentComponent(Value: TComponent);
begin
  if FParent = Value then Exit;
  if FSkipNewName and (Value = nil) then Exit;
  if FParent <> nil then FParent.FList.Remove(Self);
  if (Value <> nil) and (Value is TecSyntaxManager) then
   begin
     FParent := TecSyntaxManager(Value);
     FParent.FList.Add(Self);
   end else FParent := nil;
end;

{ TLoadableComponent }

var
  CheckExistingName: Boolean = False;

procedure TLoadableComponent.LoadFromFile(const FileName: string);
var
  Stream: TFileStreamUTF8;
begin
  FFileName := FileName; //AT
  Stream := TFileStreamUTF8.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    LoadFromStream(Stream);
  finally
    FreeAndNil(Stream);
  end;
end;

procedure TLoadableComponent.LoadFromResourceID(Instance: Cardinal;
  ResID: Integer; ResType: string);
var
  Stream: TResourceStream;
begin
  Stream := TResourceStream.CreateFromID(Instance, ResID,
    {$IFNDEF EC_DOTNET}Pchar(ResType){$ELSE}ResType{$ENDIF});
  try
    LoadFromStream(Stream);
  finally
    FreeAndNil(Stream);
  end;
end;

procedure TLoadableComponent.LoadFromResourceName(Instance: Cardinal;
  const ResName: string; ResType: string);
var
  Stream: TResourceStream;
begin
  Stream := TResourceStream.Create(Instance, ResName,
    {$IFNDEF EC_DOTNET}Pchar(ResType){$ELSE}ResType{$ENDIF});
  try
    LoadFromStream(Stream);
  finally
    FreeAndNil(Stream);
  end;
end;

procedure TLoadableComponent.LoadFromStream(const Stream: TStream);
begin
  FSkipNewName := True;
  CheckExistingName := True;
  try
    FIgnoreAll := False;
    LoadComponentFromStream(Self, Stream, OnReadError);
  finally
    FSkipNewName := False;
    CheckExistingName := False;
    FFileName := FileName;
 end;
end;

function TLoadableComponent.NotStored: Boolean;
begin
  Result := not FSaving;
end;

procedure TLoadableComponent.OnReadError(Reader: TReader;
  const Message: string; var Handled: Boolean);
begin
//  Handled := True;
  Handled := FIgnoreAll;
  if not Handled then
   case MessageDlg(Message + sLineBreak + 'Ignore this error?', mtError, [mbYes, mbNo, mbAll], 0) of
     mrYes: Handled := True;
     mrAll: begin
              Handled := True;
              FIgnoreAll := True;
            end;
   end;
end;

procedure TLoadableComponent.SaveToFile(const FileName: string);
var
  Stream: TStream;
begin
  Stream := TFileStreamUTF8.Create(FileName, fmCreate);
  try
    SaveToStream(Stream);
  finally
    FreeAndNil(Stream);
  end;
  FFileName := FileName;
end;

procedure TLoadableComponent.SaveToStream(Stream: TStream);
begin
  FSaving := True;
  try
    SaveComponentToStream(Self, Stream);
  finally
    FSaving := False;
  end;
end;

procedure TLoadableComponent.SetName(const NewName: TComponentName);
var Base: string;
    n:integer;
begin
  if not FSkipNewName then
   if CheckExistingName and (Owner.FindComponent(NewName) <> nil) then
    begin
     Base := ClassName;
     Delete(Base, 1, 1);
     n := 1;
     while Owner.FindComponent(Base + IntToStr(n)) <> nil do
      Inc(n);
     inherited SetName(Base + IntToStr(n));
    end
   else inherited;
end;

{ TecSubAnalyzerRule }

constructor TecSubAnalyzerRule.Create(Collection: TCollection);
begin
  inherited;
  FStartRegExpr := TecRegExpr.Create;
  FEndRegExpr := TecRegExpr.Create;
  SetDefaultModifiers(FStartRegExpr);
  SetDefaultModifiers(FEndRegExpr);
end;

destructor TecSubAnalyzerRule.Destroy;
begin
  FreeAndNil(FStartRegExpr);
  FreeAndNil(FEndRegExpr);
  inherited;
end;

procedure TecSubAnalyzerRule.AssignTo(Dest: TPersistent);
begin
  inherited;
  if Dest is TecSubAnalyzerRule then
   with Dest as TecSubAnalyzerRule do
    begin
     StartExpression := Self.StartExpression;
     EndExpression := Self.EndExpression;
     SyntAnalyzer := Self.SyntAnalyzer;
     FromTextBegin := Self.FromTextBegin;
     ToTextEnd := Self.ToTextEnd;
     IncludeBounds := Self.IncludeBounds;
    end;
end;

function TecSubAnalyzerRule.GetEndExpression: string;
begin
  Result := FEndRegExpr.Expression;
end;

function TecSubAnalyzerRule.GetItemBaseName: string;
begin
  Result := 'Sub lexer rule';
end;

function TecSubAnalyzerRule.GetStartExpression: string;
begin
  Result := FStartRegExpr.Expression;
end;

function TecSubAnalyzerRule.MatchStart(const Source: ecString; Pos: integer): integer;
begin
 try
  Result := FStartRegExpr.MatchLength(Source, Pos);
 except
  Result := 0;
 end;
end;

function TecSubAnalyzerRule.MatchEnd(const Source: ecString; Pos: integer): integer;
begin
 try
  Result := FEndRegExpr.MatchLength(Source, Pos);
 except
  Result := 0;
 end;
end;

procedure TecSubAnalyzerRule.SetEndExpression(const Value: string);
begin
  FEndRegExpr.Expression := Value;
  Changed(False);
end;

procedure TecSubAnalyzerRule.SetStartExpression(const Value: string);
begin
  FStartRegExpr.Expression := Value;
  Changed(False);
end;

procedure TecSubAnalyzerRule.SetSyntAnalyzer(const Value: TecSyntAnalyzer);
var own: TecSyntAnalyzer;

  function IsLinked(SAnal: TecSyntAnalyzer): Boolean;
  var i: integer;
  begin
    for i := 0 to Collection.Count - 1 do
     if (Collection.Items[i] <> Self) and ((Collection.Items[i] as TecSubAnalyzerRule).SyntAnalyzer = SAnal) then
      begin
       Result := True;
       Exit;
      end;
    Result := False;
  end;

begin
  if FSyntAnalyzer <> Value then
   begin
     own := (Collection as TSyntCollection).SyntOwner;
     if Assigned(FSyntAnalyzer) and (FSyntAnalyzer <> own) and not IsLinked(FSyntAnalyzer) then
       FSyntAnalyzer.RemoveMasterLexer(own);
     FSyntAnalyzer := Value;
     if Assigned(FSyntAnalyzer) and (FSyntAnalyzer <> own) and not IsLinked(FSyntAnalyzer) then
      FSyntAnalyzer.AddMasterLexer(own);
     Changed(False);
   end;
end;

procedure TecSubAnalyzerRule.SetFromTextBegin(const Value: Boolean);
begin
  FFromTextBegin := Value;
  Changed(False);
end;

procedure TecSubAnalyzerRule.SetToTextEnd(const Value: Boolean);
begin
  FToTextEnd := Value;
  Changed(False);
end;

procedure TecSubAnalyzerRule.SetIncludeBounds(const Value: Boolean);
begin
  FIncludeBounds := Value;
  Changed(False);
end;

{ TecSubAnalyzerRules }

function TecSubAnalyzerRules.Add: TecSubAnalyzerRule;
begin
  Result := TecSubAnalyzerRule(inherited Add);
end;

constructor TecSubAnalyzerRules.Create;
begin
  inherited Create(TecSubAnalyzerRule);
end;

function TecSubAnalyzerRules.GetItem(Index: integer): TecSubAnalyzerRule;
begin
  Result := TecSubAnalyzerRule(inherited Items[Index]);
end;

{ TecSyntStyles }

constructor TecSyntStyles.Create(AOwner: TComponent);
begin
  inherited;
  FStyles := TecStylesCollection.Create;
end;

destructor TecSyntStyles.Destroy;
begin
  FreeAndNil(FStyles);
  inherited;
end;

procedure TecSyntStyles.SetStyles(const Value: TecStylesCollection);
begin
  FStyles.Assign(Value);
end;


initialization
  Classes.RegisterClass(TLibSyntAnalyzer);

end.
