using System;
using System.Linq;
using Unity.CodeEditor.Document;
namespace Unity.CodeEditor.TextMate
{
internal class DocumentSnapshot
{
private LineRange[] _lineRanges;
private TextDocument _document;
private ITextSource _textSource;
private object _lock = new object();
private int _lineCount;
internal int LineCount
{
get { lock (_lock) { return _lineCount; } }
}
internal DocumentSnapshot(TextDocument document)
{
_document = document;
_lineRanges = new LineRange[document.LineCount];
Update(null);
}
internal void RemoveLines(int startLine, int endLine)
{
lock (_lock)
{
var tmpList = _lineRanges.ToList();
tmpList.RemoveRange(startLine, endLine - startLine + 1);
_lineRanges = tmpList.ToArray();
_lineCount = _lineRanges.Length;
}
}
internal string GetLineText(int lineIndex)
{
lock (_lock)
{
var lineRange = _lineRanges[lineIndex];
return _textSource.GetText(lineRange.Offset, lineRange.Length);
}
}
internal string GetLineTextIncludingTerminator(int lineIndex)
{
lock (_lock)
{
var lineRange = _lineRanges[lineIndex];
return _textSource.GetText(lineRange.Offset, lineRange.TotalLength);
}
}
internal ReadOnlyMemory<char> GetLineTextIncludingTerminatorAsMemory(int lineIndex)
{
lock (_lock)
{
var lineRange = _lineRanges[lineIndex];
return _textSource.GetTextAsMemory(lineRange.Offset, lineRange.TotalLength);
}
}
internal string GetLineTerminator(int lineIndex)
{
lock (_lock)
{
var lineRange = _lineRanges[lineIndex];
return _textSource.GetText(lineRange.Offset + lineRange.Length, lineRange.TotalLength - lineRange.Length);
}
}
internal int GetLineLength(int lineIndex)
{
lock (_lock)
{
return _lineRanges[lineIndex].Length;
}
}
internal int GetTotalLineLength(int lineIndex)
{
lock (_lock)
{
return _lineRanges[lineIndex].TotalLength;
}
}
internal string GetText()
{
lock (_lock)
{
return _textSource.Text;
}
}
internal void Update(DocumentChangeEventArgs e)
{
lock (_lock)
{
_lineCount = _document.Lines.Count;
if (e != null && e.OffsetChangeMap != null && _lineRanges != null && _lineCount == _lineRanges.Length)
{
// it's a single-line change
// update the offsets usign the OffsetChangeMap
RecalculateOffsets(e);
}
else
{
// recompute all the line ranges
// based in the document lines
RecomputeAllLineRanges(e);
}
_textSource = _document.CreateSnapshot();
}
}
private void RecalculateOffsets(DocumentChangeEventArgs e)
{
var changedLine = _document.GetLineByOffset(e.Offset);
int lineIndex = changedLine.LineNumber - 1;
_lineRanges[lineIndex].Offset = changedLine.Offset;
_lineRanges[lineIndex].Length = changedLine.Length;
_lineRanges[lineIndex].TotalLength = changedLine.TotalLength;
for (int i = lineIndex + 1; i < _lineCount; i++)
{
_lineRanges[i].Offset = e.OffsetChangeMap.GetNewOffset(_lineRanges[i].Offset);
}
}
private void RecomputeAllLineRanges(DocumentChangeEventArgs e)
{
Array.Resize(ref _lineRanges, _lineCount);
int currentLineIndex = (e != null) ?
_document.GetLineByOffset(e.Offset).LineNumber - 1 : 0;
var currentLine = _document.GetLineByNumber(currentLineIndex + 1);
while (currentLine != null)
{
_lineRanges[currentLineIndex].Offset = currentLine.Offset;
_lineRanges[currentLineIndex].Length = currentLine.Length;
_lineRanges[currentLineIndex].TotalLength = currentLine.TotalLength;
currentLine = currentLine.NextLine;
currentLineIndex++;
}
}
struct LineRange
{
internal int Offset;
internal int Length;
internal int TotalLength;
}
}
}