// *
// * 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
{
///
/// The SyntaxDocument is a component that is responsible for Parsing , Folding , Undo / Redo actions and various text actions.
///
public class SyntaxDocument : Component, IEnumerable
{
#region General declarations
private readonly RowList rows = new RowList();
///
///
///
public RowList KeywordQueue = new RowList();
///
/// List of rows that should be parsed
///
public RowList ParseQueue = new RowList();
private UndoBlockCollection captureBlock;
private bool captureMode;
private bool folding = true;
///
/// For public use only
///
private bool isParsed = true;
private bool modified;
private string mSyntaxFile = "";
///
/// Gets or Sets if folding needs to be recalculated
///
public bool NeedResetRows;
///
/// The active parser of the document
///
public IParser Parser = new DefaultParser();
///
/// Tag property , lets the user store custom data in the row.
///
public object Tag;
///
/// Buffer containing undo actions
///
public readonly UndoBuffer UndoBuffer = new UndoBuffer();
///
/// List of rows that is not hidden by folding
///
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
///
/// Event that is raised when there is no more rows to parse
///
public event EventHandler ParsingCompleted;
public event EventHandler UndoBufferChanged = null;
///
/// Raised when the parser is active
///
public event EventHandler Parsing;
///
/// Raised when the document content is changed
///
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;
///
/// Raised when the modified flag has changed
///
public event EventHandler ModifiedChanged;
//----------------------------------------------
///
/// Raised when a row have been parsed
///
public event ParserEventHandler RowParsed;
// public event ParserEventHandler RowAdded;
///
/// Raised when a row have been deleted
///
public event ParserEventHandler RowDeleted;
#endregion
#region PUBLIC PROPERTY MAXUNDOBUFFERSIZE
///
/// Gets or Sets the Maximum number of entries in the undobuffer
///
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
///
///
///
///
public SyntaxDocument(IContainer container) : this()
{
container.Add(this);
InitializeComponent();
}
///
///
///
public SyntaxDocument()
{
Parser.Document = this;
Text = "";
ResetVisibleRows();
Init();
}
///
/// Get or Set the Modified flag
///
public bool Modified
{
get { return modified; }
set
{
modified = value;
OnModifiedChanged();
}
}
///
/// Get or Set the Name of the Syntaxfile to use
///
[DefaultValue("")]
public string SyntaxFile
{
get { return mSyntaxFile; }
set
{
mSyntaxFile = value;
// this.Parser=new Parser_Default();
Parser.Init(value);
Text = Text;
}
}
///
/// Gets or Sets if the document should use folding or not
///
[DefaultValue(true)]
public bool Folding
{
get { return folding; }
set
{
folding = value;
if (!value)
{
foreach (Row r in this)
{
r.Expanded = true;
}
}
ResetVisibleRows();
OnChange();
}
}
///
/// Gets if the document is fully parsed
///
[Browsable(false)]
public bool IsParsed
{
get { return isParsed; }
}
///
/// Returns the row at the specified index
///
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; }
}
///
/// Gets the row count of the document
///
[Browsable(false)]
public int Count
{
get { return rows.Count; }
}
///
/// Gets or Sets the text of the entire document
///
[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();
}
}
///
/// Gets and string array containing the text of all rows.
///
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
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
new System.ComponentModel.Container();
}
#endregion
#region IEnumerable Members
///
///
///
///
public IEnumerator GetEnumerator()
{
return rows.GetEnumerator();
}
#endregion
///
/// For internal use only
///
public void ChangeVersion()
{
Version ++;
if (Version > long.MaxValue - 10)
Version = long.MinValue;
}
///
/// Starts an Undo Capture.
/// This method can be called if you with to collect multiple text operations into one undo action
///
public void StartUndoCapture()
{
captureMode = true;
captureBlock = new UndoBlockCollection();
}
///
/// Ends an Undo capture and pushes the collected actions onto the undostack
///
///
///
public UndoBlockCollection EndUndoCapture()
{
captureMode = false;
AddToUndoList(captureBlock);
return captureBlock;
}
///
/// ReParses the document
///
public void ReParse()
{
Text = Text;
}
///
/// Removes all bookmarks in the document
///
public void ClearBookmarks()
{
foreach (Row r in this)
{
r.Bookmarked = false;
}
InvokeChange();
}
///
/// Removes all breakpoints in the document.
///
public void ClearBreakpoints()
{
foreach (Row r in this)
{
r.Breakpoint = false;
}
InvokeChange();
}
///
/// Call this method to ensure that a specific row is fully parsed
///
///
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);
}
///
/// Call this method to make the SyntaxDocument raise the Changed event
///
public void InvokeChange()
{
OnChange();
}
///
/// Performs a span parse on all rows. No Keyword colorizing
///
public void ParseAll()
{
while (ParseQueue.Count > 0)
ParseSome();
ParseQueue.Clear();
}
///
/// Parses all rows , either a span parse or a full parse with keyword colorizing
///
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();
}
}
///
/// Folds all foldable rows
///
public void FoldAll()
{
ParseAll(false);
foreach (Row r in this)
{
r.Expanded = false;
}
ResetVisibleRows();
OnChange();
}
///
/// UnFolds all foldable rows
///
public void UnFoldAll()
{
ParseAll(false);
foreach (Row r in this)
{
r.Expanded = true;
}
ResetVisibleRows();
OnChange();
}
///
/// Parses a chunk of 1000 rows , this is not thread safe
///
public void ParseSome()
{
ParseSome(1000);
}
///
/// Parse a chunk of rows, this is not thread safe
///
/// The number of rows to parse
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);
}
}
}
///
/// Add a new row with the specified text to the bottom of the document
///
/// Text to add
/// The row that was added
public Row Add(string text)
{
return Add(text, true);
}
///
/// Add a new row with the specified text to the bottom of the document
///
/// Text to add
/// true if and undo action should be added to the undo stack
/// The row that was added
public Row Add(string text, bool StoreUndo)
{
var xtl = new Row();
rows.Add(xtl);
xtl.Document = this;
xtl.Text = text;
return xtl;
}
///
/// Insert a text at the specified row index
///
/// Text to insert
/// Row index where the text should be inserted
/// The row that was inserted
public Row Insert(string text, int index)
{
return Insert(text, index, true);
}
///
/// Insert a text at the specified row index
///
/// Text to insert
/// Row index where the text should be inserted
/// true if and undo action should be added to the undo stack
/// The row that was inserted
public Row Insert(string text, int index, bool storeUndo)
{
var xtl = new Row {Document = this};
rows.Insert(index, xtl);
xtl.Text = text;
if (storeUndo)
{
var undo = new UndoBlock {
Text = text,
};
undo.Position.Y = IndexOf(xtl);
AddToUndoList(undo);
}
//this.ResetVisibleRows ();
return xtl;
}
///
/// Remove a row at specified row index
///
/// index of the row that should be removed
public void Remove(int index)
{
Remove(index, true);
}
public void Remove(int index, bool StoreUndo)
{
Remove(index, StoreUndo, true);
}
///
/// Remove a row at specified row index
///
/// index of the row that should be removed
/// true if and undo action should be added to the undo stack
///
public void Remove(int index, bool storeUndo, bool raiseChanged)
{
Row r = this[index];
if (storeUndo)
{
var ra = new TextRange();
if (index != Count - 1)
{
ra.FirstColumn = 0;
ra.FirstRow = index;
ra.LastRow = index + 1;
ra.LastColumn = 0;
}
else
{
ra.FirstColumn = r.PrevRow.Text.Length;
ra.FirstRow = index - 1;
ra.LastRow = index;
ra.LastColumn = r.Text.Length;
}
PushUndoBlock(UndoAction.DeleteRange, GetRange(ra), ra.FirstColumn, ra.FirstRow);
}
rows.RemoveAt(index);
if (r.InKeywordQueue)
KeywordQueue.Remove(r);
if (r.InQueue)
ParseQueue.Remove(r);
//this.ResetVisibleRows ();
OnRowDeleted(r);
if (raiseChanged)
OnChange();
}
///
/// Deletes a range of text
///
/// the range that should be deleted
public void DeleteRange(TextRange Range)
{
DeleteRange(Range, true);
}
private int ParseRows(Row row)
{
return ParseRows(row, false);
}
private int ParseRows(Row row, bool Keywords)
{
if (!Keywords)
{
int index = IndexOf(row);
int count = 0;
try
{
while (row.InQueue && count < 100)
{
if (index >= 0)
{
if (index > 0)
if (this[index - 1].InQueue)
ParseRow(this[index - 1]);
Parser.ParseRow(index, false);
}
int i = ParseQueue.IndexOf(row);
if (i >= 0)
ParseQueue.RemoveAt(i);
row.InQueue = false;
index++;
count++;
row = this[index];
if (row == null)
break;
}
}
catch {}
return count;
}
else
{
int index = IndexOf(row);
if (index == -1 || row.InKeywordQueue == false)
{
KeywordQueue.Remove(row);
return 0;
}
int count = 0;
try
{
while (row.InKeywordQueue && count < 100)
{
if (index >= 0)
{
if (index > 0)
if (this[index - 1].InQueue)
ParseRow(this[index - 1]);
Parser.ParseRow(index, true);
}
index++;
count++;
row = this[index];
if (row == null)
break;
}
}
catch {}
return count;
}
}
///
/// Forces a row to be parsed
///
/// Row to parse
/// true if keywords and operators should be parsed
public void ParseRow(Row r, bool ParseKeywords)
{
int index = IndexOf(r);
if (index >= 0)
{
if (index > 0)
if (this[index - 1].InQueue)
ParseRow(this[index - 1]);
Parser.ParseRow(index, false);
if (ParseKeywords)
Parser.ParseRow(index, true);
}
int i = ParseQueue.IndexOf(r);
if (i >= 0)
ParseQueue.RemoveAt(i);
r.InQueue = false;
}
///
/// Forces a row to be parsed
///
/// Row to parse
public void ParseRow(Row r)
{
ParseRow(r, false);
}
///
/// Gets the row index of the next bookmarked row
///
/// Start index
/// Index of the next bookmarked row
public int GetNextBookmark(int StartIndex)
{
for (int i = StartIndex + 1; i < Count; i++)
{
Row r = this[i];
if (r.Bookmarked)
return i;
}
for (int i = 0; i < StartIndex; i++)
{
Row r = this[i];
if (r.Bookmarked)
return i;
}
return StartIndex;
}
///
/// Gets the row index of the previous bookmarked row
///
/// Start index
/// Index of the previous bookmarked row
public int GetPreviousBookmark(int StartIndex)
{
for (int i = StartIndex - 1; i >= 0; i--)
{
Row r = this[i];
if (r.Bookmarked)
return i;
}
for (int i = Count - 1; i >= StartIndex; i--)
{
Row r = this[i];
if (r.Bookmarked)
return i;
}
return StartIndex;
}
///
/// Deletes a range of text
///
/// Range to delete
/// true if the actions should be pushed onto the undo stack
public void DeleteRange(TextRange Range, bool StoreUndo)
{
TextRange r = Range;
Modified = true;
if (StoreUndo)
{
string deltext = GetRange(Range);
PushUndoBlock(UndoAction.DeleteRange, deltext, r.FirstColumn, r.FirstRow);
}
if (r.FirstRow == r.LastRow)
{
Row xtr = this[r.FirstRow];
int max = Math.Min(r.FirstColumn, xtr.Text.Length);
string left = xtr.Text.Substring(0, max);
string right = "";
if (xtr.Text.Length >= r.LastColumn)
right = xtr.Text.Substring(r.LastColumn);
xtr.Text = left + right;
}
else
{
if (r.LastRow > Count - 1)
r.LastRow = Count - 1;
Row xtr = this[r.FirstRow];
if (r.FirstColumn > xtr.Text.Length)
{
int diff = r.FirstColumn - xtr.Text.Length;
var ws = new string(' ', diff);
InsertText(ws, xtr.Text.Length, r.FirstRow, true);
//return;
}
string row1 = xtr.Text.Substring(0, r.FirstColumn);
Row xtr2 = this[r.LastRow];
int Max = Math.Min(xtr2.Text.Length, r.LastColumn);
string row2 = xtr2.Text.Substring(Max);
string tot = row1 + row2;
//bool fold=this[r.LastRow].IsCollapsed | this[r.FirstRow].IsCollapsed ;
int start = r.FirstRow;
int end = r.LastRow;
for (int i = end - 1; i >= start; i--)
{
Remove(i, false, false);
}
//todo: DeleteRange error
//this.Insert ( tot ,r.FirstRow,false);
Row row = this[start];
row.Expanded = true;
row.Text = tot;
row.startSpans.Clear();
row.endSpans.Clear();
row.startSpan = null;
row.endSpan = null;
row.Parse();
}
ResetVisibleRows();
OnChange();
}
///
/// Get a range of text
///
/// The range to get
/// string containing the text inside the given range
public string GetRange(TextRange Range)
{
if (Range.FirstRow >= Count)
Range.FirstRow = Count;
if (Range.LastRow >= Count)
Range.LastRow = Count;
if (Range.FirstRow != Range.LastRow)
{
//note:error has been tracked here
Row r1 = this[Range.FirstRow];
int mx = Math.Min(r1.Text.Length, Range.FirstColumn);
string s1 = r1.Text.Substring(mx) + Environment.NewLine;
//if (Range.LastRow >= this.Count)
// Range.LastRow=this.Count -1;
Row r2 = this[Range.LastRow];
if (r2 == null)
return "";
int Max = Math.Min(r2.Text.Length, Range.LastColumn);
string s2 = r2.Text.Substring(0, Max);
var sb = new StringBuilder();
for (int i = Range.FirstRow + 1; i <= Range.LastRow - 1; i++)
{
Row r3 = this[i];
sb.Append(r3.Text + Environment.NewLine);
}
string s3 = sb.ToString();
return s1 + s3 + s2;
}
else
{
Row r = this[Range.FirstRow];
int Max = Math.Min(r.Text.Length, Range.LastColumn);
int Length = Max - Range.FirstColumn;
if (Length <= 0)
return "";
string s = r.Text.Substring(Range.FirstColumn, Max - Range.FirstColumn);
return s;
}
}
///
/// Returns the index of a given row
///
/// row to find
/// Index of the given row
public int IndexOf(Row xtr)
{
return rows.IndexOf(xtr);
}
///
/// Clear all content in the document
///
public void clear()
{
foreach (Row r in rows)
{
OnRowDeleted(r);
}
rows.Clear();
// this.FormatRanges.Clear ();
ParseQueue.Clear();
KeywordQueue.Clear();
UndoBuffer.Clear();
UndoStep = 0;
// this.Add ("");
// ResetVisibleRows();
// this.OnChange ();
}
public void Clear()
{
Text = "";
}
///
/// Inserts a text into the document at a given column,row.
///
/// Text to insert
/// Column
/// Row index
/// TextPoint containing the end of the inserted text
public TextPoint InsertText(string text, int xPos, int yPos)
{
return InsertText(text, xPos, yPos, true);
}
///
/// Inserts a text into the document at a given column,row.
///
/// Text to insert
/// Column
/// Row index
/// true if this action should be pushed onto the undo stack
/// TextPoint containing the end of the inserted text
public TextPoint InsertText(string text, int xPos, int yPos, bool StoreUndo)
{
Modified = true;
Row xtr = this[yPos];
if (xPos > xtr.Text.Length)
{
//virtualwhitespace fix
int Padd = xPos - xtr.Text.Length;
var PaddStr = new string(' ', Padd);
text = PaddStr + text;
xPos -= Padd;
}
string lft = xtr.Text.Substring(0, xPos);
string rgt = xtr.Text.Substring(xPos);
string NewText = lft + text + rgt;
string t = NewText.Replace(Environment.NewLine, "\n");
string[] lines = t.Split('\n');
xtr.Text = lines[0];
Row lastrow = xtr;
//this.Parser.ParsePreviewLine(xtr);
xtr.Parse();
if (!xtr.InQueue)
ParseQueue.Add(xtr);
xtr.InQueue = true;
int i = IndexOf(xtr);
for (int j = 1; j <= lines.GetUpperBound(0); j++)
{
lastrow = Insert(lines[j], j + i, false);
}
if (StoreUndo)
PushUndoBlock(UndoAction.InsertRange, text, xPos, yPos);
ResetVisibleRows();
OnChange();
return new TextPoint(lastrow.Text.Length - rgt.Length, IndexOf(lastrow));
}
private void OnModifiedChanged()
{
if (ModifiedChanged != null)
ModifiedChanged(this, new EventArgs());
}
private void OnChange()
{
if (Change != null)
Change(this, new EventArgs());
}
private void OnRowParsed(Row r)
{
if (RowParsed != null)
RowParsed(this, new RowEventArgs(r));
OnApplyFormatRanges(r);
}
// private void OnRowAdded(Row r)
// {
// if (RowAdded != null)
// RowAdded(this,new RowEventArgs(r));
// }
private void OnRowDeleted(Row r)
{
if (RowDeleted != null)
RowDeleted(this, new RowEventArgs(r));
}
public void PushUndoBlock(UndoAction Action, string text, int x, int y)
{
var undo = new UndoBlock {
Action = Action,
Text = text
};
undo.Position.Y = y;
undo.Position.X = x;
//AddToUndoList(undo);
if (captureMode)
{
captureBlock.Add(undo);
}
else
{
AddToUndoList(undo);
}
}
///
/// Gets a Range from a given text
///
///
///
///
///
public TextRange GetRangeFromText(string text, int xPos, int yPos)
{
string t = text.Replace(Environment.NewLine, "\n");
string[] lines = t.Split("\n".ToCharArray());
var r = new TextRange
{
FirstColumn = xPos,
FirstRow = yPos,
LastRow = (lines.Length - 1 + yPos),
LastColumn = lines[lines.Length - 1].Length
};
if (r.FirstRow == r.LastRow)
r.LastColumn += r.FirstColumn;
return r;
}
public void AddToUndoList(UndoBlock undo)
{
//store the undo action in a actiongroup
var ActionGroup = new UndoBlockCollection {undo};
AddToUndoList(ActionGroup);
}
///
/// Add an action to the undo stack
///
/// action to add
public void AddToUndoList(UndoBlockCollection ActionGroup)
{
UndoBuffer.ClearFrom(UndoStep);
UndoBuffer.Add(ActionGroup);
UndoStep++;
OnUndoBufferChanged();
}
///
/// Perform an undo action
///
/// The position where the caret should be placed
public TextPoint Undo()
{
if (UndoStep == 0)
return new TextPoint(-1, -1);
UndoBlockCollection ActionGroup = UndoBuffer[UndoStep - 1];
UndoBlock undo = ActionGroup[0];
for (int i = ActionGroup.Count - 1; i >= 0; i--)
{
undo = ActionGroup[i];
//TextPoint tp=new TextPoint (undo.Position.X,undo.Position.Y);
switch (undo.Action)
{
case UndoAction.DeleteRange:
InsertText(undo.Text, undo.Position.X, undo.Position.Y, false);
break;
case UndoAction.InsertRange:
{
TextRange r = GetRangeFromText(undo.Text, undo.Position.X, undo.Position.Y);
DeleteRange(r, false);
}
break;
default:
break;
}
}
UndoStep--;
ResetVisibleRows();
//no undo steps left , the document is not dirty
if (UndoStep == 0)
Modified = false;
var tp = new TextPoint(undo.Position.X, undo.Position.Y);
OnUndoBufferChanged();
return tp;
}
public void AutoIndentSegment(Span span)
{
if (span == null)
span = this[0].startSpan;
Row start = span.StartRow;
Row end = span.EndRow;
if (start == null)
start = this[0];
if (end == null)
end = this[Count - 1];
for (int i = start.Index; i <= end.Index; i++)
{
Row r = this[i];
int depth = r.Indent;
string text = r.Text.Substring(r.GetLeadingWhitespace().Length);
var indent = new string('\t', depth);
r.Text = indent + text;
}
ResetVisibleRows();
}
//Returns the span object at the given position
///
/// Gets a span object form a given column , Row index
/// (This only applies if the row is fully parsed)
///
/// Column and Rowindex
/// span object at the given position
public Span GetSegmentFromPos(TextPoint p)
{
Row xtr = this[p.Y];
int CharNo = 0;
if (xtr.Count == 0)
return xtr.startSpan;
Span prev = xtr.startSpan;
foreach (Word w in xtr)
{
if (w.Text.Length + CharNo > p.X)
{
if (CharNo == p.X)
return prev;
return w.Span;
}
CharNo += w.Text.Length;
prev = w.Span;
}
return xtr.endSpan;
}
//the specific word that contains the char in point p
///
/// Gets a Word object form a given column , Row index
/// (this only applies if the row is fully parsed)
///
/// Column and Rowindex
/// Word object at the given position
public Word GetWordFromPos(TextPoint p)
{
Row xtr = this[p.Y];
int CharNo = 0;
Word CorrectWord = null;
foreach (Word w in xtr)
{
if (CorrectWord != null)
{
if (w.Text == "")
return w;
return CorrectWord;
}
if (w.Text.Length + CharNo > p.X || w == xtr[xtr.Count - 1])
{
//return w;
CorrectWord = w;
}
else
{
CharNo += w.Text.Length;
}
}
return CorrectWord;
}
//the specific word that contains the char in point p
///
/// Gets a Word object form a given column , Row index
/// (this only applies if the row is fully parsed)
///
/// Column and Rowindex
/// Word object at the given position
public Word GetFormatWordFromPos(TextPoint p)
{
Row xtr = this[p.Y];
int CharNo = 0;
Word CorrectWord = null;
foreach (Word w in xtr.FormattedWords)
{
if (CorrectWord != null)
{
if (w.Text == "")
return w;
return CorrectWord;
}
if (w.Text.Length + CharNo > p.X || w == xtr[xtr.Count - 1])
{
//return w;
CorrectWord = w;
}
else
{
CharNo += w.Text.Length;
}
}
return CorrectWord;
}
///
/// Call this method to make the document raise the RowParsed event
///
///
public void InvokeRowParsed(Row row)
{
OnRowParsed(row);
}
///
/// Call this method to recalculate the visible rows
///
public void ResetVisibleRows()
{
InternalResetVisibleRows();
}
private void InternalResetVisibleRows()
{
// if (System.DateTime.Now > new DateTime (2002,12,31))
// {
//
// this.rows = new RowList ();
// this.Add ("BETA VERSION EXPIRED");
// VisibleRows = this.rows;
// return;
// }
if (!folding)
{
VisibleRows = rows;
NeedResetRows = false;
}
else
{
NeedResetRows = false;
VisibleRows = new RowList(); //.Clear ();
int RealRow = 0;
for (int i = 0; i < Count; i++)
{
Row r = this[RealRow];
VisibleRows.Add(r);
bool collapsed = false;
if (r.CanFold)
if (r.expansion_StartSpan.Expanded == false)
{
if (r.expansion_StartSpan.EndWord == null) {}
else
{
r = r.Expansion_EndRow; // .expansion_StartSpan.EndRow;
collapsed = true;
}
}
if (!collapsed)
RealRow++;
else
RealRow = IndexOf(r) + 1;
if (RealRow >= Count)
break;
}
}
}
///
/// Converts a Column/Row index position into a char index
///
/// TextPoint where x is column and y is row index
/// Char index in the document text
public int PointToIntPos(TextPoint pos)
{
int y = 0;
int p = 0;
foreach (Row r in this)
{
if (y == pos.Y)
break;
p += r.Text.Length + Environment.NewLine.Length;
y++;
}
return p + Math.Min(pos.X, this[pos.Y].Text.Length);
}
///
/// Converts a char index into a Column/Row index
///
/// Char index to convert
/// Point where x is column and y is row index
public TextPoint IntPosToPoint(int pos)
{
int p = 0;
int y = 0;
foreach (Row r in this)
{
p += r.Text.Length + Environment.NewLine.Length;
if (p > pos)
{
p -= r.Text.Length + Environment.NewLine.Length;
int x = pos - p;
return new TextPoint(x, y);
}
y++;
}
return new TextPoint(-1, -1);
}
///
/// Toggle expansion of a given row
///
///
public void ToggleRow(Row r)
{
if (!folding)
return;
if (r.Expansion_EndRow == null || r.Expansion_StartRow == null)
return;
// if (r.IsCollapsed)
// {
// r.expansion_StartSpan.Expanded = true;
// ExpandRow(r);
// }
// else
// {
// r.expansion_StartSpan.Expanded = false;
// CollapseRow(r);
// }
if (r.CanFold)
r.Expanded = !r.Expanded;
ResetVisibleRows();
OnChange();
}
///
/// Perform an redo action
///
/// The position where the caret should be placed
public TextPoint Redo()
{
if (UndoStep >= UndoBuffer.Count)
return new TextPoint(-1, -1);
UndoBlockCollection ActionGroup = UndoBuffer[UndoStep];
UndoBlock undo = ActionGroup[0];
for (int i = 0; i < ActionGroup.Count; i++)
{
undo = ActionGroup[i];
switch (undo.Action)
{
case UndoAction.InsertRange:
{
InsertText(undo.Text, undo.Position.X, undo.Position.Y, false);
}
break;
case UndoAction.DeleteRange:
{
TextRange r = GetRangeFromText(undo.Text, undo.Position.X, undo.Position.Y);
DeleteRange(r, false);
}
break;
default:
break;
}
}
TextRange ran = GetRangeFromText(undo.Text, undo.Position.X, undo.Position.Y);
UndoStep++;
ResetVisibleRows();
OnUndoBufferChanged();
return new TextPoint(ran.LastColumn, ran.LastRow);
}
public Word GetStartBracketWord(Word Start, Pattern End, Span FindIn)
{
if (Start == null || Start.Pattern == null || Start.Span == null)
return null;
int CurrentRow = Start.Row.Index;
int FirstRow = FindIn.StartRow.Index;
int x = Start.Index;
int count = 0;
while (CurrentRow >= FirstRow)
{
for (int i = x; i >= 0; i--)
{
Word w = this[CurrentRow][i];
if (w.Span == FindIn && w.Type == WordType.Word)
{
if (w.Pattern == Start.Pattern)
count++;
if (w.Pattern == End)
count--;
if (count == 0)
return w;
}
}
if (!Start.Pattern.IsMultiLineBracket)
break;
CurrentRow--;
if (CurrentRow >= 0)
x = this[CurrentRow].Count - 1;
}
return null;
}
public Word GetEndBracketWord(Word Start, Pattern End, Span FindIn)
{
if (Start == null || Start.Pattern == null || Start.Span == null)
return null;
int CurrentRow = Start.Row.Index;
int LastRow = Count - 1;
if (FindIn.EndRow != null)
LastRow = FindIn.EndRow.Index;
int x = Start.Index;
int count = 0;
while (CurrentRow <= LastRow)
{
for (int i = x; i < this[CurrentRow].Count; i++)
{
Word w = this[CurrentRow][i];
if (w.Span == FindIn && w.Type == WordType.Word)
{
if (w.Pattern == Start.Pattern)
count++;
if (w.Pattern == End)
count--;
if (count == 0)
return w;
}
}
if (!Start.Pattern.IsMultiLineBracket)
break;
CurrentRow++;
x = 0;
}
return null;
}
///
/// Sets a syntax file, from an embedded resource.
///
/// The assembly which contains the embedded resource.
/// The name of the resource.
public void SetSyntaxFromEmbeddedResource(Assembly assembly, String resourceName)
{
if (assembly == null)
throw new ArgumentNullException("assembly");
if (string.IsNullOrEmpty(resourceName))
throw new ArgumentNullException("resourceName");
//
// Get the xml from an embedded resource. Load the stream.
//
Stream stream = assembly.GetManifestResourceStream(resourceName);
if (stream != null)
{
stream.Seek(0, SeekOrigin.Begin);
//
// Read stream.
//
var reader = new StreamReader(stream);
String xml = reader.ReadToEnd();
//
// Clean up stream.
//
stream.Close();
//
// Initialize.
//
Parser.Init(SyntaxDefinition.FromSyntaxXml(xml));
Text = Text;
}
}
public void OnApplyFormatRanges(Row row)
{
row.FormattedWords = row.words;
}
}
}