context: // *
  // * Copyright (C) 2008 Roger Alsing : http://www.RogerAlsing.com
  // *
  // * This library is free software; you can redistribute it and/or modify it
  // * under the terms of the GNU Lesser General Public License 2.1 or later, as
  // * published by the Free Software Foundation. See the included license.txt
  // * or http://www.gnu.org/copyleft/lesser.html for details.
  // *
  // *
  
  using System;
  using System.Collections;
  using System.ComponentModel;
  using System.IO;
  using System.Reflection;
  using System.Text;
  using Alsing.SourceCode.SyntaxDocumentParsers;
  
  namespace Alsing.SourceCode
  {
      /// <summary>
      /// The SyntaxDocument is a component that is responsible for Parsing , Folding , Undo / Redo actions and various text actions.
      /// </summary>
      public class SyntaxDocument : Component, IEnumerable
      {
          #region General declarations
  
          private readonly RowList rows = new RowList();
  
          /// <summary>
          /// 
          /// </summary>
          public RowList KeywordQueue = new RowList();
  
          /// <summary>
          /// List of rows that should be parsed
          /// </summary>
          public RowList ParseQueue = new RowList();
  
          private UndoBlockCollection captureBlock;
          private bool captureMode;
          private bool folding = true;
  
          /// <summary>
          /// For public use only
          /// </summary>
          private bool isParsed = true;
  
          private bool modified;
  
          private string mSyntaxFile = "";
  
  
          /// <summary>
          /// Gets or Sets if folding needs to be recalculated
          /// </summary>
          public bool NeedResetRows;
  
   
  
          /// <summary>
          /// The active parser of the document
          /// </summary>
          public IParser Parser = new DefaultParser();
  
          /// <summary>
          /// Tag property , lets the user store custom data in the row.
          /// </summary>
          public object Tag;
  
          /// <summary>
          /// Buffer containing undo actions
          /// </summary>
          public readonly UndoBuffer UndoBuffer = new UndoBuffer();
  
          /// <summary>
          /// List of rows that is not hidden by folding
          /// </summary>
          public RowList VisibleRows = new RowList();
  
          #region PUBLIC PROPERTY UNDOSTEP
  
          private int _UndoStep;
  
          public int UndoStep
          {
              get
              {
                  if (_UndoStep > UndoBuffer.Count)
                      _UndoStep = UndoBuffer.Count;
  
                  return _UndoStep;
              }
              set { _UndoStep = value; }
          }
  
          #endregion
  
          /// <summary>
          /// Event that is raised when there is no more rows to parse
          /// </summary>
          public event EventHandler ParsingCompleted;
  
          public event EventHandler UndoBufferChanged = null;
  
          /// <summary>
          /// Raised when the parser is active
          /// </summary>
          public event EventHandler Parsing;
  
          /// <summary>
          /// Raised when the document content is changed
          /// </summary>
          public event EventHandler Change;
  
          public event RowEventHandler BreakPointAdded;
          public event RowEventHandler BreakPointRemoved;
  
          public event RowEventHandler BookmarkAdded;
          public event RowEventHandler BookmarkRemoved;
  
          protected virtual void OnBreakPointAdded(Row r)
          {
              if (BreakPointAdded != null)
                  BreakPointAdded(this, new RowEventArgs(r));
          }
  
          protected virtual void OnBreakPointRemoved(Row r)
          {
              if (BreakPointRemoved != null)
                  BreakPointRemoved(this, new RowEventArgs(r));
          }
  
          protected virtual void OnBookmarkAdded(Row r)
          {
              if (BookmarkAdded != null)
                  BookmarkAdded(this, new RowEventArgs(r));
          }
  
          protected virtual void OnBookmarkRemoved(Row r)
          {
              if (BookmarkRemoved != null)
                  BookmarkRemoved(this, new RowEventArgs(r));
          }
  
          protected virtual void OnUndoBufferChanged()
          {
              if (UndoBufferChanged != null)
                  UndoBufferChanged(this, EventArgs.Empty);
          }
  
  
          public virtual void InvokeBreakPointAdded(Row r)
          {
              OnBreakPointAdded(r);
          }
  
          public virtual void InvokeBreakPointRemoved(Row r)
          {
              OnBreakPointRemoved(r);
          }
  
          public virtual void InvokeBookmarkAdded(Row r)
          {
              OnBookmarkAdded(r);
          }
  
          public virtual void InvokeBookmarkRemoved(Row r)
          {
              OnBookmarkRemoved(r);
          }
  
  
          //public event System.EventHandler CreateParser;
  
          /// <summary>
          /// Raised when the modified flag has changed
          /// </summary>
          public event EventHandler ModifiedChanged;
  
          //----------------------------------------------
  
          /// <summary>
          /// Raised when a row have been parsed
          /// </summary>
          public event ParserEventHandler RowParsed;
  
          //	public event ParserEventHandler	 RowAdded;
          /// <summary>
          /// Raised when a row have been deleted
          /// </summary>
          public event ParserEventHandler RowDeleted;
  
          #endregion
  
          #region PUBLIC PROPERTY MAXUNDOBUFFERSIZE
  
          /// <summary>
          /// Gets or Sets the Maximum number of entries in the undobuffer
          /// </summary>
          public int MaxUndoBufferSize
          {
              get { return UndoBuffer.MaxSize; }
              set { UndoBuffer.MaxSize = value; }
          }
  
          #endregion
  
          #region PUBLIC PROPERTY VERSION
  
          private long _Version = long.MinValue;
  
          [Browsable(false)]
          [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
          public long Version
          {
              get { return _Version; }
              set { _Version = value; }
          }
  
          #endregion
  
          /// <summary>
          /// 
          /// </summary>
          /// <param name="container"></param>
          public SyntaxDocument(IContainer container) : this()
          {
              container.Add(this);
              InitializeComponent();
          }
  
          /// <summary>
          /// 
          /// </summary>
          public SyntaxDocument()
          {
              Parser.Document = this;
              Text = "";
              ResetVisibleRows();
              Init();
          }
  
          /// <summary>
          /// Get or Set the Modified flag
          /// </summary>
          public bool Modified
          {
              get { return modified; }
              set
              {
                  modified = value;
                  OnModifiedChanged();
              }
          }
  
          /// <summary>
          /// Get or Set the Name of the Syntaxfile to use
          /// </summary>
          [DefaultValue("")]
          public string SyntaxFile
          {
              get { return mSyntaxFile; }
              set
              {
                  mSyntaxFile = value;
                  //	this.Parser=new Parser_Default();
                  Parser.Init(value);
                  Text = Text;
              }
          }
  
          /// <summary>
          /// Gets or Sets if the document should use folding or not
          /// </summary>
          [DefaultValue(true)]
          public bool Folding
          {
              get { return folding; }
              set
              {
                  folding = value;
                  if (!value)
                  {
                      foreach (Row r in this)
                      {
                          r.Expanded = true;
                      }
                  }
                  ResetVisibleRows();
  
                  OnChange();
              }
          }
  
          /// <summary>
          /// Gets if the document is fully parsed
          /// </summary>
          [Browsable(false)]
          public bool IsParsed
          {
              get { return isParsed; }
          }
  
          /// <summary>
          /// Returns the row at the specified index
          /// </summary>
          public Row this[int index]
          {
              get
              {
                  if (index < 0 || index >= rows.Count)
                  {
                      //	System.Diagnostics.Debugger.Break ();
                      return null;
                  }
                  return rows[index];
              }
  
              set { rows[index] = value; }
          }
  
          /// <summary>
          /// Gets the row count of the document
          /// </summary>
          [Browsable(false)]
          public int Count
          {
              get { return rows.Count; }
          }
  
          /// <summary>
          /// Gets or Sets the text of the entire document
          /// </summary>		
          [Browsable(false)]
          //	[RefreshProperties (RefreshProperties.All)]
          [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
          public string Text
          {
              get
              {
                  int i = 0;
                  var sb = new StringBuilder();
  
                  ParseAll(true);
                  foreach (Row tr in rows)
                  {
                      if (i > 0)
                          sb.Append(Environment.NewLine);
                      tr.MatchCase();
                      sb.Append(tr.Text);
                      i++;
                  }
                  return sb.ToString();
              }
  
              set
              {
                  clear();
                  Add("");
                  InsertText(value, 0, 0);
                  UndoBuffer.Clear();
                  UndoStep = 0;
                  Modified = false;
                  isParsed = false;
                  //OnChange();
                  InvokeChange();
              }
          }
  
          /// <summary>
          /// Gets and string array containing the text of all rows.
          /// </summary>
          public string[] Lines
          {
              get
              {
                  return Text.Split("\n".ToCharArray());
              }
              set
              {
                  string s = "";
                  foreach (string sl in value)
                      s += sl + "\n";
                  Text = s.Substring(0, s.Length - 1);
              }
          }
  
          #region Component Designer generated code
  
          /// <summary>
          /// Required method for Designer support - do not modify
          /// the contents of this method with the code editor.
          /// </summary>
          private void InitializeComponent()
          {
              new System.ComponentModel.Container();
          }
  
          #endregion
  
          #region IEnumerable Members
  
          /// <summary>
          /// 
          /// </summary>
          /// <returns></returns>
          public IEnumerator GetEnumerator()
          {
              return rows.GetEnumerator();
          }
  
          #endregion
  
          /// <summary>
          /// For internal use only
          /// </summary>
          public void ChangeVersion()
          {
              Version ++;
              if (Version > long.MaxValue - 10)
                  Version = long.MinValue;
          }
  
          /// <summary>
          /// Starts an Undo Capture.
          /// This method can be called if you with to collect multiple text operations into one undo action
          /// </summary>
          public void StartUndoCapture()
          {
              captureMode = true;
              captureBlock = new UndoBlockCollection();
          }
  
          /// <summary>
          /// Ends an Undo capture and pushes the collected actions onto the undostack
          /// <seealso cref="StartUndoCapture"/>
          /// </summary>
          /// <returns></returns>
          public UndoBlockCollection EndUndoCapture()
          {
              captureMode = false;
              AddToUndoList(captureBlock);
              return captureBlock;
          }
  
          /// <summary>
          /// ReParses the document
          /// </summary>
          public void ReParse()
          {
              Text = Text;
          }
  
          /// <summary>
          /// Removes all bookmarks in the document
          /// </summary>
          public void ClearBookmarks()
          {
              foreach (Row r in this)
              {
                  r.Bookmarked = false;
              }
              InvokeChange();
          }
  
          /// <summary>
          /// Removes all breakpoints in the document.
          /// </summary>
          public void ClearBreakpoints()
          {
              foreach (Row r in this)
              {
                  r.Breakpoint = false;
              }
              InvokeChange();
          }
  
  
          /// <summary>
          /// Call this method to ensure that a specific row is fully parsed
          /// </summary>
          /// <param name="Row"></param>
          public void EnsureParsed(Row Row)
          {
              ParseAll();
              Parser.ParseRow(Row.Index, true);
          }
  
          private void Init()
          {
              var l = new SyntaxDefinition();
              l.mainSpanDefinition = new SpanDefinition(l)
                            {
                                MultiLine = true
                            };
              Parser.Init(l);
          }
  
          /// <summary>
          /// Call this method to make the SyntaxDocument raise the Changed event
          /// </summary>
          public void InvokeChange()
          {
              OnChange();
          }
  
          /// <summary>
          /// Performs a span parse on all rows. No Keyword colorizing
          /// </summary>
          public void ParseAll()
          {
              while (ParseQueue.Count > 0)
                  ParseSome();
  
              ParseQueue.Clear();
          }
  
          /// <summary>
          /// Parses all rows , either a span parse or a full parse with keyword colorizing
          /// </summary>
          public void ParseAll(bool ParseKeywords)
          {
              ParseAll();
              if (ParseKeywords)
              {
                  for (int i = 0; i < Count; i++)
                  {
                      if (this[i].RowState != RowState.AllParsed)
                          Parser.ParseRow(i, true);
                  }
                  ParseQueue.Clear();
                  KeywordQueue.Clear();
              }
          }
  
          /// <summary>
          /// Folds all foldable rows
          /// </summary>
          public void FoldAll()
          {
              ParseAll(false);
              foreach (Row r in this)
              {
                  r.Expanded = false;
              }
              ResetVisibleRows();
              OnChange();
          }
  
          /// <summary>
          /// UnFolds all foldable rows
          /// </summary>
          public void UnFoldAll()
          {
              ParseAll(false);
              foreach (Row r in this)
              {
                  r.Expanded = true;
              }
              ResetVisibleRows();
              OnChange();
          }
  
  
          /// <summary>
          /// Parses a chunk of 1000 rows , this is not thread safe
          /// </summary>
          public void ParseSome()
          {
              ParseSome(1000);
          }
  
          /// <summary>
          /// Parse a chunk of rows, this is not thread safe
          /// </summary>
          /// <param name="RowCount">The number of rows to parse</param>
          public void ParseSome(int RowCount)
          {
              if (ParseQueue.Count > 0)
              {
                  isParsed = false;
                  int i = 0;
                  while (i < RowCount && ParseQueue.Count > 0)
                  {
                      Row row = ParseQueue[0];
                      i += ParseRows(row);
                  }
  
                  if (NeedResetRows)
                      ResetVisibleRows();
  
                  if (Parsing != null)
                      Parsing(this, new EventArgs());
              }
              else
              {
                  if (!isParsed && !Modified)
                  {
                      isParsed = true;
  
                      foreach (Row r in this)
                      {
                          if (r.expansion_StartSpan != null && r.Expansion_EndRow != null)
                          {
                              if (r.expansion_StartSpan.Scope.DefaultExpanded == false)
                                  r.Expanded = false;
                          }
                      }
                      ResetVisibleRows();
                      if (ParsingCompleted != null)
                          ParsingCompleted(this, new EventArgs());
                  }
              }
  
              if (ParseQueue.Count == 0 && KeywordQueue.Count > 0)
              {
  //				Console.WriteLine (this.KeywordQueue.Count.ToString ());
                  int i = 0;
                  while (i < RowCount/20 && KeywordQueue.Count > 0)
                  {
                      Row row = KeywordQueue[0];
                      i += ParseRows(row, true);
                  }
              }
          }
  
  
          /// <summary>
          /// Add a new row with the specified text to the bottom of the document
          /// </summary>
          /// <param name="text">Text to add</param>
          /// <returns>The row that was added</returns>		
          public Row Add(string text)
          {
              return Add(text, true);
          }
  
          /// <summary>
          /// Add a new row with the specified text to the bottom of the document
          /// </summary>
          /// <param name="text">Text to add</param>
          /// <param name="StoreUndo">true if and undo action should be added to the undo stack</param>
          /// <returns>The row that was added</returns>
          public Row Add(string text, bool StoreUndo)
          {
  
gt:             var xtl = new Row();