5
// Mike Krüger <mkrueger@xamarin.com>
7
// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com)
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
10
// of this software and associated documentation files (the "Software"), to deal
11
// in the Software without restriction, including without limitation the rights
12
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
// copies of the Software, and to permit persons to whom the Software is
14
// furnished to do so, subject to the following conditions:
16
// The above copyright notice and this permission notice shall be included in
17
// all copies or substantial portions of the Software.
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
using ICSharpCode.NRefactory.Editor;
28
using System.Threading;
30
using ICSharpCode.NRefactory.CSharp.Refactoring;
31
using ICSharpCode.NRefactory.TypeSystem;
32
using System.Collections.Generic;
34
namespace ICSharpCode.NRefactory.CSharp
37
/// The formatting changes are used to format a specific region inside a document and apply a minimal formatting
38
/// changeset to a given document. This is useful for a text editor environment.
40
public class FormattingChanges
42
readonly IDocument document;
43
readonly internal List<TextReplaceAction> changes = new List<TextReplaceAction> ();
45
internal FormattingChanges (IDocument document)
48
throw new ArgumentNullException("document");
49
this.document = document;
53
/// Applies the changes to the input document.
55
public void ApplyChanges()
57
ApplyChanges(0, document.TextLength, document.Replace, (o, l, v) => document.GetText(o, l) == v);
60
public void ApplyChanges(int startOffset, int length)
62
ApplyChanges(startOffset, length, document.Replace, (o, l, v) => document.GetText(o, l) == v);
66
/// Applies the changes to the given Script instance.
68
public void ApplyChanges(Script script)
70
ApplyChanges(0, document.TextLength, script.Replace);
73
public void ApplyChanges(int startOffset, int length, Script script)
75
ApplyChanges(startOffset, length, script.Replace);
78
public void ApplyChanges(int startOffset, int length, Action<int, int, string> documentReplace, Func<int, int, string, bool> filter = null)
80
int endOffset = startOffset + length;
81
// Console.WriteLine ("apply:"+ startOffset + "->" + endOffset);
82
// Console.WriteLine (document.Text.Substring (0, startOffset) + new string ('x',length) + document.Text.Substring (startOffset+ length));
84
TextReplaceAction previousChange = null;
86
var depChanges = new List<TextReplaceAction> ();
87
foreach (var change in changes.OrderBy(c => c.Offset)) {
88
if (previousChange != null) {
89
if (change.Equals(previousChange)) {
90
// ignore duplicate changes
93
if (change.Offset < previousChange.Offset + previousChange.RemovalLength) {
94
throw new InvalidOperationException ("Detected overlapping changes " + change + "/" + previousChange);
97
previousChange = change;
98
bool skipChange = change.Offset + change.RemovalLength < startOffset || change.Offset > endOffset;
99
skipChange |= filter != null && filter(change.Offset + delta, change.RemovalLength, change.NewText);
100
skipChange &= !depChanges.Contains(change);
102
documentReplace(change.Offset + delta, change.RemovalLength, change.NewText);
103
delta += change.NewText.Length - change.RemovalLength;
104
if (change.DependsOn != null) {
105
depChanges.Add(change.DependsOn);
112
internal TextReplaceAction AddChange(int offset, int removedChars, string insertedText)
114
if (removedChars == 0 && string.IsNullOrEmpty (insertedText))
116
var action = new TextReplaceAction (offset, removedChars, insertedText);
121
internal sealed class TextReplaceAction
123
internal readonly int Offset;
124
internal readonly int RemovalLength;
125
internal readonly string NewText;
126
internal TextReplaceAction DependsOn;
128
public TextReplaceAction (int offset, int removalLength, string newText)
130
this.Offset = offset;
131
this.RemovalLength = removalLength;
132
this.NewText = newText ?? string.Empty;
135
public override bool Equals(object obj)
137
TextReplaceAction other = obj as TextReplaceAction;
141
return this.Offset == other.Offset && this.RemovalLength == other.RemovalLength && this.NewText == other.NewText;
144
public override int GetHashCode()
149
public override string ToString()
151
return string.Format("[TextReplaceAction: Offset={0}, RemovalLength={1}, NewText={2}]", Offset, RemovalLength, NewText);