458 lines
11 KiB
C#
458 lines
11 KiB
C#
// <file>
|
|
// <copyright see="prj:///doc/copyright.txt"/>
|
|
// <license see="prj:///doc/license.txt"/>
|
|
// <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
|
|
// <version>$Revision$</version>
|
|
// </file>
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
|
|
using ICSharpCode.TextEditor.Undo;
|
|
|
|
namespace ICSharpCode.TextEditor.Document
|
|
{
|
|
/// <summary>
|
|
/// Describes the caret marker
|
|
/// </summary>
|
|
public enum LineViewerStyle {
|
|
/// <summary>
|
|
/// No line viewer will be displayed
|
|
/// </summary>
|
|
None,
|
|
|
|
/// <summary>
|
|
/// The row in which the caret is will be marked
|
|
/// </summary>
|
|
FullRow
|
|
}
|
|
|
|
/// <summary>
|
|
/// Describes the indent style
|
|
/// </summary>
|
|
public enum IndentStyle {
|
|
/// <summary>
|
|
/// No indentation occurs
|
|
/// </summary>
|
|
None,
|
|
|
|
/// <summary>
|
|
/// The indentation from the line above will be
|
|
/// taken to indent the curent line
|
|
/// </summary>
|
|
Auto,
|
|
|
|
/// <summary>
|
|
/// Inteligent, context sensitive indentation will occur
|
|
/// </summary>
|
|
Smart
|
|
}
|
|
|
|
/// <summary>
|
|
/// Describes the bracket highlighting style
|
|
/// </summary>
|
|
public enum BracketHighlightingStyle {
|
|
|
|
/// <summary>
|
|
/// Brackets won't be highlighted
|
|
/// </summary>
|
|
None,
|
|
|
|
/// <summary>
|
|
/// Brackets will be highlighted if the caret is on the bracket
|
|
/// </summary>
|
|
OnBracket,
|
|
|
|
/// <summary>
|
|
/// Brackets will be highlighted if the caret is after the bracket
|
|
/// </summary>
|
|
AfterBracket
|
|
}
|
|
|
|
/// <summary>
|
|
/// Describes the selection mode of the text area
|
|
/// </summary>
|
|
public enum DocumentSelectionMode {
|
|
/// <summary>
|
|
/// The 'normal' selection mode.
|
|
/// </summary>
|
|
Normal,
|
|
|
|
/// <summary>
|
|
/// Selections will be added to the current selection or new
|
|
/// ones will be created (multi-select mode)
|
|
/// </summary>
|
|
Additive
|
|
}
|
|
|
|
/// <summary>
|
|
/// The default <see cref="IDocument"/> implementation.
|
|
/// </summary>
|
|
internal sealed class DefaultDocument : IDocument
|
|
{
|
|
bool readOnly = false;
|
|
|
|
LineManager lineTrackingStrategy;
|
|
BookmarkManager bookmarkManager;
|
|
ITextBufferStrategy textBufferStrategy;
|
|
IFormattingStrategy formattingStrategy;
|
|
FoldingManager foldingManager;
|
|
UndoStack undoStack = new UndoStack();
|
|
ITextEditorProperties textEditorProperties = new DefaultTextEditorProperties();
|
|
MarkerStrategy markerStrategy;
|
|
|
|
public LineManager LineManager {
|
|
get { return lineTrackingStrategy; }
|
|
set { lineTrackingStrategy = value; }
|
|
}
|
|
|
|
public event EventHandler<LineLengthChangeEventArgs> LineLengthChanged {
|
|
add { lineTrackingStrategy.LineLengthChanged += value; }
|
|
remove { lineTrackingStrategy.LineLengthChanged -= value; }
|
|
}
|
|
public event EventHandler<LineCountChangeEventArgs> LineCountChanged {
|
|
add { lineTrackingStrategy.LineCountChanged += value; }
|
|
remove { lineTrackingStrategy.LineCountChanged -= value; }
|
|
}
|
|
public event EventHandler<LineEventArgs> LineDeleted {
|
|
add { lineTrackingStrategy.LineDeleted += value; }
|
|
remove { lineTrackingStrategy.LineDeleted -= value; }
|
|
}
|
|
|
|
public MarkerStrategy MarkerStrategy {
|
|
get { return markerStrategy; }
|
|
set { markerStrategy = value; }
|
|
}
|
|
|
|
public ITextEditorProperties TextEditorProperties {
|
|
get {
|
|
return textEditorProperties;
|
|
}
|
|
set {
|
|
textEditorProperties = value;
|
|
}
|
|
}
|
|
|
|
public UndoStack UndoStack {
|
|
get {
|
|
return undoStack;
|
|
}
|
|
}
|
|
|
|
public IList<LineSegment> LineSegmentCollection {
|
|
get {
|
|
return lineTrackingStrategy.LineSegmentCollection;
|
|
}
|
|
}
|
|
|
|
public bool ReadOnly {
|
|
get {
|
|
return readOnly;
|
|
}
|
|
set {
|
|
readOnly = value;
|
|
}
|
|
}
|
|
|
|
public ITextBufferStrategy TextBufferStrategy {
|
|
get {
|
|
return textBufferStrategy;
|
|
}
|
|
set {
|
|
textBufferStrategy = value;
|
|
}
|
|
}
|
|
|
|
public IFormattingStrategy FormattingStrategy {
|
|
get {
|
|
return formattingStrategy;
|
|
}
|
|
set {
|
|
formattingStrategy = value;
|
|
}
|
|
}
|
|
|
|
public FoldingManager FoldingManager {
|
|
get {
|
|
return foldingManager;
|
|
}
|
|
set {
|
|
foldingManager = value;
|
|
}
|
|
}
|
|
|
|
public IHighlightingStrategy HighlightingStrategy {
|
|
get {
|
|
return lineTrackingStrategy.HighlightingStrategy;
|
|
}
|
|
set {
|
|
lineTrackingStrategy.HighlightingStrategy = value;
|
|
}
|
|
}
|
|
|
|
public int TextLength {
|
|
get {
|
|
return textBufferStrategy.Length;
|
|
}
|
|
}
|
|
|
|
public BookmarkManager BookmarkManager {
|
|
get {
|
|
return bookmarkManager;
|
|
}
|
|
set {
|
|
bookmarkManager = value;
|
|
}
|
|
}
|
|
|
|
|
|
public string TextContent {
|
|
get {
|
|
return GetText(0, textBufferStrategy.Length);
|
|
}
|
|
set {
|
|
Debug.Assert(textBufferStrategy != null);
|
|
Debug.Assert(lineTrackingStrategy != null);
|
|
OnDocumentAboutToBeChanged(new DocumentEventArgs(this, 0, 0, value));
|
|
textBufferStrategy.SetContent(value);
|
|
lineTrackingStrategy.SetContent(value);
|
|
undoStack.ClearAll();
|
|
|
|
OnDocumentChanged(new DocumentEventArgs(this, 0, 0, value));
|
|
OnTextContentChanged(EventArgs.Empty);
|
|
}
|
|
}
|
|
|
|
public void Insert(int offset, string text)
|
|
{
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
OnDocumentAboutToBeChanged(new DocumentEventArgs(this, offset, -1, text));
|
|
|
|
textBufferStrategy.Insert(offset, text);
|
|
lineTrackingStrategy.Insert(offset, text);
|
|
|
|
undoStack.Push(new UndoableInsert(this, offset, text));
|
|
|
|
OnDocumentChanged(new DocumentEventArgs(this, offset, -1, text));
|
|
}
|
|
|
|
public void Remove(int offset, int length)
|
|
{
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
OnDocumentAboutToBeChanged(new DocumentEventArgs(this, offset, length));
|
|
undoStack.Push(new UndoableDelete(this, offset, GetText(offset, length)));
|
|
|
|
textBufferStrategy.Remove(offset, length);
|
|
lineTrackingStrategy.Remove(offset, length);
|
|
|
|
OnDocumentChanged(new DocumentEventArgs(this, offset, length));
|
|
}
|
|
|
|
public void Replace(int offset, int length, string text)
|
|
{
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
OnDocumentAboutToBeChanged(new DocumentEventArgs(this, offset, length, text));
|
|
undoStack.Push(new UndoableReplace(this, offset, GetText(offset, length), text));
|
|
|
|
textBufferStrategy.Replace(offset, length, text);
|
|
lineTrackingStrategy.Replace(offset, length, text);
|
|
|
|
OnDocumentChanged(new DocumentEventArgs(this, offset, length, text));
|
|
}
|
|
|
|
public char GetCharAt(int offset)
|
|
{
|
|
return textBufferStrategy.GetCharAt(offset);
|
|
}
|
|
|
|
public string GetText(int offset, int length)
|
|
{
|
|
#if DEBUG
|
|
if (length < 0) throw new ArgumentOutOfRangeException("length", length, "length < 0");
|
|
#endif
|
|
return textBufferStrategy.GetText(offset, length);
|
|
}
|
|
public string GetText(ISegment segment)
|
|
{
|
|
return GetText(segment.Offset, segment.Length);
|
|
}
|
|
|
|
public int TotalNumberOfLines {
|
|
get {
|
|
return lineTrackingStrategy.TotalNumberOfLines;
|
|
}
|
|
}
|
|
|
|
public int GetLineNumberForOffset(int offset)
|
|
{
|
|
return lineTrackingStrategy.GetLineNumberForOffset(offset);
|
|
}
|
|
|
|
public LineSegment GetLineSegmentForOffset(int offset)
|
|
{
|
|
return lineTrackingStrategy.GetLineSegmentForOffset(offset);
|
|
}
|
|
|
|
public LineSegment GetLineSegment(int line)
|
|
{
|
|
return lineTrackingStrategy.GetLineSegment(line);
|
|
}
|
|
|
|
public int GetFirstLogicalLine(int lineNumber)
|
|
{
|
|
return lineTrackingStrategy.GetFirstLogicalLine(lineNumber);
|
|
}
|
|
|
|
public int GetLastLogicalLine(int lineNumber)
|
|
{
|
|
return lineTrackingStrategy.GetLastLogicalLine(lineNumber);
|
|
}
|
|
|
|
public int GetVisibleLine(int lineNumber)
|
|
{
|
|
return lineTrackingStrategy.GetVisibleLine(lineNumber);
|
|
}
|
|
|
|
// public int GetVisibleColumn(int logicalLine, int logicalColumn)
|
|
// {
|
|
// return lineTrackingStrategy.GetVisibleColumn(logicalLine, logicalColumn);
|
|
// }
|
|
//
|
|
public int GetNextVisibleLineAbove(int lineNumber, int lineCount)
|
|
{
|
|
return lineTrackingStrategy.GetNextVisibleLineAbove(lineNumber, lineCount);
|
|
}
|
|
|
|
public int GetNextVisibleLineBelow(int lineNumber, int lineCount)
|
|
{
|
|
return lineTrackingStrategy.GetNextVisibleLineBelow(lineNumber, lineCount);
|
|
}
|
|
|
|
public TextLocation OffsetToPosition(int offset)
|
|
{
|
|
int lineNr = GetLineNumberForOffset(offset);
|
|
LineSegment line = GetLineSegment(lineNr);
|
|
return new TextLocation(offset - line.Offset, lineNr);
|
|
}
|
|
|
|
public int PositionToOffset(TextLocation p)
|
|
{
|
|
if (p.Y >= this.TotalNumberOfLines) {
|
|
return 0;
|
|
}
|
|
LineSegment line = GetLineSegment(p.Y);
|
|
return Math.Min(this.TextLength, line.Offset + Math.Min(line.Length, p.X));
|
|
}
|
|
|
|
public void UpdateSegmentListOnDocumentChange<T>(List<T> list, DocumentEventArgs e) where T : ISegment
|
|
{
|
|
int removedCharacters = e.Length > 0 ? e.Length : 0;
|
|
int insertedCharacters = e.Text != null ? e.Text.Length : 0;
|
|
for (int i = 0; i < list.Count; ++i) {
|
|
ISegment s = list[i];
|
|
int segmentStart = s.Offset;
|
|
int segmentEnd = s.Offset + s.Length;
|
|
|
|
if (e.Offset <= segmentStart) {
|
|
segmentStart -= removedCharacters;
|
|
if (segmentStart < e.Offset)
|
|
segmentStart = e.Offset;
|
|
}
|
|
if (e.Offset < segmentEnd) {
|
|
segmentEnd -= removedCharacters;
|
|
if (segmentEnd < e.Offset)
|
|
segmentEnd = e.Offset;
|
|
}
|
|
|
|
Debug.Assert(segmentStart <= segmentEnd);
|
|
|
|
if (segmentStart == segmentEnd) {
|
|
list.RemoveAt(i);
|
|
--i;
|
|
continue;
|
|
}
|
|
|
|
if (e.Offset <= segmentStart)
|
|
segmentStart += insertedCharacters;
|
|
if (e.Offset < segmentEnd)
|
|
segmentEnd += insertedCharacters;
|
|
|
|
Debug.Assert(segmentStart < segmentEnd);
|
|
|
|
s.Offset = segmentStart;
|
|
s.Length = segmentEnd - segmentStart;
|
|
}
|
|
}
|
|
|
|
void OnDocumentAboutToBeChanged(DocumentEventArgs e)
|
|
{
|
|
if (DocumentAboutToBeChanged != null) {
|
|
DocumentAboutToBeChanged(this, e);
|
|
}
|
|
}
|
|
|
|
void OnDocumentChanged(DocumentEventArgs e)
|
|
{
|
|
if (DocumentChanged != null) {
|
|
DocumentChanged(this, e);
|
|
}
|
|
}
|
|
|
|
public event DocumentEventHandler DocumentAboutToBeChanged;
|
|
public event DocumentEventHandler DocumentChanged;
|
|
|
|
// UPDATE STUFF
|
|
List<TextAreaUpdate> updateQueue = new List<TextAreaUpdate>();
|
|
|
|
public List<TextAreaUpdate> UpdateQueue {
|
|
get {
|
|
return updateQueue;
|
|
}
|
|
}
|
|
|
|
public void RequestUpdate(TextAreaUpdate update)
|
|
{
|
|
if (updateQueue.Count == 1 && updateQueue[0].TextAreaUpdateType == TextAreaUpdateType.WholeTextArea) {
|
|
// if we're going to update the whole text area, we don't need to store detail updates
|
|
return;
|
|
}
|
|
if (update.TextAreaUpdateType == TextAreaUpdateType.WholeTextArea) {
|
|
// if we're going to update the whole text area, we don't need to store detail updates
|
|
updateQueue.Clear();
|
|
}
|
|
updateQueue.Add(update);
|
|
}
|
|
|
|
public void CommitUpdate()
|
|
{
|
|
if (UpdateCommited != null) {
|
|
UpdateCommited(this, EventArgs.Empty);
|
|
}
|
|
}
|
|
|
|
void OnTextContentChanged(EventArgs e)
|
|
{
|
|
if (TextContentChanged != null) {
|
|
TextContentChanged(this, e);
|
|
}
|
|
}
|
|
|
|
public event EventHandler UpdateCommited;
|
|
public event EventHandler TextContentChanged;
|
|
|
|
[Conditional("DEBUG")]
|
|
internal static void ValidatePosition(IDocument document, TextLocation position)
|
|
{
|
|
document.GetLineSegment(position.Line);
|
|
}
|
|
}
|
|
}
|