From 5091b49f8e26900ba0012baba267d1172945747f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 2 Sep 2010 19:22:52 +0200 Subject: [PATCH] DocumentUtilitites: add LoadDocumentFromBuffer and LoadReadOnlyDocumentFromBuffer --- .../Project/ICSharpCode.SharpDevelop.csproj | 1 + .../Project/Src/Editor/DocumentUtilitites.cs | 35 ++- .../Project/Src/Editor/ReadOnlyDocument.cs | 281 ++++++++++++++++++ 3 files changed, 306 insertions(+), 11 deletions(-) create mode 100644 src/Main/Base/Project/Src/Editor/ReadOnlyDocument.cs diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 5e5dbf5aff..ba7a73832b 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -155,6 +155,7 @@ ToolTipService.cs + diff --git a/src/Main/Base/Project/Src/Editor/DocumentUtilitites.cs b/src/Main/Base/Project/Src/Editor/DocumentUtilitites.cs index a7c2399c04..bb827bed69 100644 --- a/src/Main/Base/Project/Src/Editor/DocumentUtilitites.cs +++ b/src/Main/Base/Project/Src/Editor/DocumentUtilitites.cs @@ -20,6 +20,30 @@ namespace ICSharpCode.SharpDevelop.Editor /// public static class DocumentUtilitites { + /// + /// Creates a new mutable document from the specified text buffer. + /// + /// + /// Use the more efficient if you only need a read-only document. + /// + public static IDocument LoadDocumentFromBuffer(ITextBuffer buffer) + { + if (buffer == null) + throw new ArgumentNullException("buffer"); + var doc = new TextDocument(GetTextSource(buffer)); + return new AvalonEditDocumentAdapter(doc, null); + } + + /// + /// Creates a new read-only document from the specified text buffer. + /// + public static IDocument LoadReadOnlyDocumentFromBuffer(ITextBuffer buffer) + { + if (buffer == null) + throw new ArgumentNullException("buffer"); + return new ReadOnlyDocument(buffer); + } + /// /// Gets the word in front of the caret. /// @@ -173,17 +197,6 @@ namespace ICSharpCode.SharpDevelop.Editor return NormalizeNewLines(input, GetLineTerminator(document, lineNumber)); } - /// - /// Creates an IDocument from an ITextBuffer. - /// - public static IDocument LoadDocumentFromBuffer(ITextBuffer buffer) - { - if (buffer == null) - throw new ArgumentNullException("buffer"); - TextDocument document = new TextDocument(buffer.Text); - return new AvalonEdit.AvalonEditDocumentAdapter(document, null); - } - #region ITextSource implementation public static ICSharpCode.AvalonEdit.Document.ITextSource GetTextSource(ITextBuffer textBuffer) { diff --git a/src/Main/Base/Project/Src/Editor/ReadOnlyDocument.cs b/src/Main/Base/Project/Src/Editor/ReadOnlyDocument.cs new file mode 100644 index 0000000000..46613adad1 --- /dev/null +++ b/src/Main/Base/Project/Src/Editor/ReadOnlyDocument.cs @@ -0,0 +1,281 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using ICSharpCode.Core; +using ICSharpCode.NRefactory; + +namespace ICSharpCode.SharpDevelop.Editor +{ + /// + /// Read-only implementation of IDocument. + /// + sealed class ReadOnlyDocument : IDocument + { + ITextBuffer textBuffer; + int[] lines; + + public ReadOnlyDocument(ITextBuffer textBuffer) + { + // ensure that underlying buffer is immutable + this.textBuffer = textBuffer.CreateSnapshot(); + List lines = new List(); + lines.Add(0); + int offset = 0; + string newlineType; + var textSource = DocumentUtilitites.GetTextSource(this.textBuffer); + while ((offset = ICSharpCode.AvalonEdit.Document.TextUtilities.FindNextNewLine(textSource, offset, out newlineType)) >= 0) { + offset += newlineType.Length; + lines.Add(offset); + } + this.lines = lines.ToArray(); + } + + public IDocumentLine GetLine(int lineNumber) + { + if (lineNumber < 1 || lineNumber > lines.Length) + throw new ArgumentOutOfRangeException("lineNumber", lineNumber, "Value must be between 1 and " + lines.Length); + return new ReadOnlyDocumentLine(this, lineNumber); + } + + sealed class ReadOnlyDocumentLine : IDocumentLine + { + readonly ReadOnlyDocument doc; + readonly int lineNumber; + + public ReadOnlyDocumentLine(ReadOnlyDocument doc, int lineNumber) + { + this.doc = doc; + this.lineNumber = lineNumber; + } + + public int Offset { + get { + return doc.GetStartOffset(lineNumber); + } + } + + public int Length { + get { + return doc.GetEndOffset(lineNumber) - doc.GetStartOffset(lineNumber); + } + } + + public int EndOffset { + get { + return doc.GetEndOffset(lineNumber); + } + } + + public int TotalLength { + get { + return doc.GetTotalEndOffset(lineNumber) - doc.GetStartOffset(lineNumber); + } + } + + public int DelimiterLength { + get { + return doc.GetTotalEndOffset(lineNumber) - doc.GetEndOffset(lineNumber); + } + } + + public int LineNumber { + get { return lineNumber; } + } + + public string Text { + get { + return doc.GetText(this.Offset, this.Length); + } + } + } + + int GetStartOffset(int lineNumber) + { + return lines[lineNumber-1]; + } + + int GetTotalEndOffset(int lineNumber) + { + return lineNumber < lines.Length ? lines[lineNumber] : textBuffer.TextLength; + } + + int GetEndOffset(int lineNumber) + { + if (lineNumber == lines.Length) + return textBuffer.TextLength; + int off = lines[lineNumber] - 1; + if (off > 0 && textBuffer.GetCharAt(off - 1) == '\r' && textBuffer.GetCharAt(off) == '\n') + off--; + return off; + } + + public IDocumentLine GetLineForOffset(int offset) + { + return GetLine(GetLineNumberForOffset(offset)); + } + + int GetLineNumberForOffset(int offset) + { + int r = Array.BinarySearch(lines, offset); + return r < 0 ? ~r : r + 1; + } + + public int PositionToOffset(int line, int column) + { + if (line < 1 || line > lines.Length) + throw new ArgumentOutOfRangeException("line", line, "Value must be between 1 and " + lines.Length); + int lineStart = GetStartOffset(line); + if (column <= 0) + return lineStart; + int lineEnd = GetEndOffset(line); + if (column >= lineEnd - lineStart) + return lineEnd; + return lineStart + column - 1; + } + + public Location OffsetToPosition(int offset) + { + if (offset < 0 || offset > textBuffer.TextLength) + throw new ArgumentOutOfRangeException("offset", offset, "Value must be between 0 and " + textBuffer.TextLength); + int line = GetLineNumberForOffset(offset); + return new Location(offset-GetStartOffset(line)+1, line); + } + + public event EventHandler Changing { add {} remove {} } + + public event EventHandler Changed { add {} remove {} } + + public event EventHandler TextChanged { add {} remove {} } + + public string Text { + get { return textBuffer.Text; } + set { + throw new NotSupportedException(); + } + } + + public int TotalNumberOfLines { + get { return lines.Length; } + } + + public ITextBufferVersion Version { + get { return null; } + } + + public int TextLength { + get { return textBuffer.TextLength; } + } + + public void Insert(int offset, string text) + { + throw new NotSupportedException(); + } + + public void Remove(int offset, int length) + { + throw new NotSupportedException(); + } + + public void Replace(int offset, int length, string newText) + { + throw new NotSupportedException(); + } + + public void StartUndoableAction() + { + } + + public void EndUndoableAction() + { + } + + public IDisposable OpenUndoGroup() + { + return new CallbackOnDispose(EndUndoableAction); + } + + public ITextAnchor CreateAnchor(int offset) + { + return new ReadOnlyDocumentTextAnchor(OffsetToPosition(offset), offset); + } + + sealed class ReadOnlyDocumentTextAnchor : ITextAnchor + { + readonly Location location; + readonly int offset; + + public ReadOnlyDocumentTextAnchor(Location location, int offset) + { + this.location = location; + this.offset = offset; + } + + public event EventHandler Deleted { add {} remove {} } + + public Location Location { + get { return location; } + } + + public int Offset { + get { return offset; } + } + + public AnchorMovementType MovementType { get; set; } + + public bool SurviveDeletion { get; set; } + + public bool IsDeleted { + get { return false; } + } + + public int Line { + get { return location.Line; } + } + + public int Column { + get { return location.Column; } + } + } + + public ITextBuffer CreateSnapshot() + { + return this; // ReadOnlyDocument is immutable + } + + public ITextBuffer CreateSnapshot(int offset, int length) + { + return textBuffer.CreateSnapshot(offset, length); + } + + public System.IO.TextReader CreateReader() + { + return textBuffer.CreateReader(); + } + + public System.IO.TextReader CreateReader(int offset, int length) + { + return textBuffer.CreateReader(offset, length); + } + + public char GetCharAt(int offset) + { + return textBuffer.GetCharAt(offset); + } + + public string GetText(int offset, int length) + { + return textBuffer.GetText(offset, length); + } + + public object GetService(Type serviceType) + { + return null; + } + } +}