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