Browse Source

Change RefactoringContext/Script API.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
e48d564c9b
  1. 2
      src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin
  2. 47
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDRefactoringContext.cs
  3. 115
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDScript.cs
  4. 8
      src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextDocument.cs
  5. 76
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs
  6. 7
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
  7. 52
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Action.cs
  8. 2
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/AddAnotherAccessor.cs
  9. 2
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/CreateEventInvocator.cs
  10. 4
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/CreateField.cs
  11. 4
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/InvertIf.cs
  12. 5
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/RemoveBackingStore.cs
  13. 6
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/RemoveBraces.cs
  14. 44
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CreateLinkAction.cs
  15. 39
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/FormatTextAction.cs
  16. 75
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/IActionFactory.cs
  17. 2
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/IContextAction.cs
  18. 118
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/NodeOutputAction.cs
  19. 40
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/NodeSelectionAction.cs
  20. 121
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs
  21. 277
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs
  22. 134
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/TextReplaceAction.cs
  23. 93
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs
  24. 4
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
  25. 5
      src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/IDocument.cs
  26. 6
      src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs
  27. 10
      src/Main/Base/Project/Src/Util/ExtensionMethods.cs

2
src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin

@ -146,7 +146,7 @@
displayName = "Introduce local variable" displayName = "Introduce local variable"
class = "ICSharpCode.NRefactory.CSharp.Refactoring.CreateLocalVariable" /> class = "ICSharpCode.NRefactory.CSharp.Refactoring.CreateLocalVariable" />
<CSharpContextAction id = "CreateProperty" <CSharpContextAction id = "CreateProperty"
displayName = "Create property" displayName = "Introduce property"
class = "ICSharpCode.NRefactory.CSharp.Refactoring.CreateProperty" /> class = "ICSharpCode.NRefactory.CSharp.Refactoring.CreateProperty" />
<CSharpContextAction id = "FlipOperatorArguments" <CSharpContextAction id = "FlipOperatorArguments"
displayName = "Flip operands" displayName = "Flip operands"

47
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDRefactoringContext.cs

@ -22,26 +22,24 @@ namespace CSharpBinding.Refactoring
{ {
readonly ITextEditor editor; readonly ITextEditor editor;
readonly ITextSource textSource; readonly ITextSource textSource;
readonly TextLocation location;
volatile IDocument document; volatile IDocument document;
readonly CSharpAstResolver resolver; readonly CSharpAstResolver resolver;
int selectionStart, selectionLength; int selectionStart, selectionLength;
CancellationToken cancellationToken;
public SDRefactoringContext(ITextSource textSource, CSharpAstResolver resolver, TextLocation location, int selectionStart, int selectionLength, CancellationToken cancellationToken) public SDRefactoringContext(ITextSource textSource, CSharpAstResolver resolver, TextLocation location, int selectionStart, int selectionLength, CancellationToken cancellationToken)
: base(resolver, cancellationToken)
{ {
this.textSource = textSource; this.textSource = textSource;
this.document = textSource as IDocument; this.document = textSource as IDocument;
this.resolver = resolver; this.resolver = resolver;
this.selectionStart = selectionStart; this.selectionStart = selectionStart;
this.selectionLength = selectionLength; this.selectionLength = selectionLength;
this.cancellationToken = cancellationToken; this.location = location;
this.Unit = resolver.RootNode as CompilationUnit;
this.Compilation = resolver.Compilation;
this.Location = location;
} }
public SDRefactoringContext(ITextEditor editor, CSharpAstResolver resolver, TextLocation location) public SDRefactoringContext(ITextEditor editor, CSharpAstResolver resolver, TextLocation location)
: base(resolver, CancellationToken.None)
{ {
this.editor = editor; this.editor = editor;
this.textSource = editor.Document; this.textSource = editor.Document;
@ -49,10 +47,7 @@ namespace CSharpBinding.Refactoring
this.resolver = resolver; this.resolver = resolver;
this.selectionStart = editor.SelectionStart; this.selectionStart = editor.SelectionStart;
this.selectionLength = editor.SelectionLength; this.selectionLength = editor.SelectionLength;
this.location = location;
this.Unit = resolver.RootNode as CompilationUnit;
this.Compilation = resolver.Compilation;
this.Location = location;
} }
public override bool Supports(Version version) public override bool Supports(Version version)
@ -63,16 +58,15 @@ namespace CSharpBinding.Refactoring
return project.LanguageVersion >= version; return project.LanguageVersion >= version;
} }
public override NodeOutputAction CreateNodeOutputAction(int offset, int removedChars, NodeOutput output)
{
return new SDScript.SDNodeOutputAction(document, offset, removedChars, output);
}
public override Script StartScript() public override Script StartScript()
{ {
if (editor == null) if (editor == null)
throw new InvalidOperationException("Cannot start a script in IsAvailable()."); throw new InvalidOperationException("Cannot start a script in IsAvailable().");
return new SDScript(editor, this); return new SDScript(editor, this.EolMarker);
}
public override TextLocation Location {
get { return location; }
} }
public override int SelectionStart { public override int SelectionStart {
@ -95,17 +89,6 @@ namespace CSharpBinding.Refactoring
} }
} }
public override ResolveResult Resolve(AstNode expression)
{
lock (resolver)
return resolver.Resolve(expression, cancellationToken);
}
public override void ReplaceReferences(IMember member, MemberDeclaration replaceWidth)
{
throw new NotImplementedException();
}
public override bool IsSomethingSelected { public override bool IsSomethingSelected {
get { get {
return selectionLength > 0; return selectionLength > 0;
@ -131,17 +114,11 @@ namespace CSharpBinding.Refactoring
return document.GetLocation(offset); return document.GetLocation(offset);
} }
public override CSharpFormattingOptions FormattingOptions {
get {
return new CSharpFormattingOptions();
}
}
public override string EolMarker { public override string EolMarker {
get { get {
if (document == null) if (document == null)
document = new ReadOnlyDocument(textSource); document = new ReadOnlyDocument(textSource);
return DocumentUtilitites.GetLineTerminator(document, 1); return DocumentUtilitites.GetLineTerminator(document, location.Line);
} }
} }

115
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDScript.cs

@ -3,9 +3,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.Refactoring; using ICSharpCode.NRefactory.CSharp.Refactoring;
using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.Editor;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor;
namespace CSharpBinding.Refactoring namespace CSharpBinding.Refactoring
@ -15,69 +18,95 @@ namespace CSharpBinding.Refactoring
/// </summary> /// </summary>
sealed class SDScript : Script sealed class SDScript : Script
{ {
int indentationSize = 4;
readonly ITextEditor editor; readonly ITextEditor editor;
readonly TextSegmentCollection<TextSegment> textSegmentCollection = new TextSegmentCollection<TextSegment>();
readonly OffsetChangeMap offsetChangeMap = new OffsetChangeMap();
readonly List<Action> actions = new List<Action>();
public SDScript(ITextEditor editor, SDRefactoringContext context) : base(context) public SDScript(ITextEditor editor, string eolMarker) : base(eolMarker, new CSharpFormattingOptions())
{ {
this.editor = editor; this.editor = editor;
} }
public static void RunActions (IList<ICSharpCode.NRefactory.CSharp.Refactoring.Action> actions, Script script) public override int GetCurrentOffset(TextLocation originalDocumentLocation)
{ {
for (int i = 0; i < actions.Count; i++) { int offset = editor.Document.GetOffset(originalDocumentLocation);
actions [i].Perform (script); return offsetChangeMap.GetNewOffset(offset, AnchorMovementType.Default);
var replaceChange = actions [i] as TextReplaceAction; }
if (replaceChange == null)
continue; public override int GetCurrentOffset(int originalDocumentOffset)
for (int j = 0; j < actions.Count; j++) { {
if (i == j) return offsetChangeMap.GetNewOffset(originalDocumentOffset, AnchorMovementType.Default);
continue; }
var change = actions [j] as TextReplaceAction;
if (change == null) public override void Replace(int offset, int length, string newText)
continue; {
if (replaceChange.Offset >= 0 && change.Offset >= 0) { var changeMapEntry = new OffsetChangeMapEntry(offset, length, newText.Length);
if (replaceChange.Offset < change.Offset) { textSegmentCollection.UpdateOffsets(changeMapEntry);
change.Offset -= replaceChange.RemovedChars; offsetChangeMap.Add(changeMapEntry);
if (!string.IsNullOrEmpty (replaceChange.InsertedText))
change.Offset += replaceChange.InsertedText.Length; actions.Add(delegate { editor.Document.Replace(offset, length, newText); });
} else if (replaceChange.Offset < change.Offset + change.RemovedChars) { }
change.RemovedChars = Math.Max (0, change.RemovedChars - replaceChange.RemovedChars);
change.Offset = replaceChange.Offset + (!string.IsNullOrEmpty (replaceChange.InsertedText) ? replaceChange.InsertedText.Length : 0); protected override ISegment CreateTrackedSegment(int offset, int length)
} {
var segment = new TextSegment();
segment.StartOffset = offset;
segment.Length = length;
textSegmentCollection.Add(segment);
return segment;
}
protected override int GetIndentLevelAt(int offset)
{
int oldOffset = offsetChangeMap.Invert().GetNewOffset(offset, AnchorMovementType.Default);
var line = editor.Document.GetLineByOffset(oldOffset);
int spaces = 0;
int indentationLevel = 0;
for (int i = line.Offset; i < offset; i++) {
char c = editor.Document.GetCharAt(i);
if (c == '\t') {
spaces = 0;
indentationLevel++;
} else if (c == ' ') {
spaces++;
if (spaces == indentationSize) {
spaces = 0;
indentationLevel++;
} }
} else {
break;
} }
} }
return indentationLevel;
} }
public override void Dispose () public override void Rename(IEntity entity, string name)
{ {
using (editor.Document.OpenUndoGroup ()) {
RunActions (changes, this);
}
} }
public override void InsertWithCursor(string operation, AstNode node, InsertPosition defaultPosition) public override void InsertWithCursor(string operation, AstNode node, InsertPosition defaultPosition)
{ {
throw new NotImplementedException();
} }
internal class SDNodeOutputAction : NodeOutputAction public override void FormatText(int offset, int length)
{ {
IDocument doc; }
public SDNodeOutputAction(IDocument doc, int offset, int removedChars, NodeOutput output) : base (offset, removedChars, output) public override void Select(int offset, int length)
{ {
if (doc == null) actions.Add(delegate { editor.Select(offset, length); });
throw new ArgumentNullException ("doc"); }
if (output == null)
throw new ArgumentNullException ("output"); public override void Link(params AstNode[] nodes)
this.doc = doc; {
} }
public override void Perform (Script script) public override void Dispose()
{ {
doc.Replace (Offset, RemovedChars, NodeOutput.Text); foreach (var action in actions)
} action();
} }
} }
} }

8
src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Document/TextDocument.cs

@ -345,6 +345,14 @@ namespace ICSharpCode.AvalonEdit.Document
} }
} }
/// <summary>
/// Creates an immutable snapshot of this document.
/// </summary>
public IDocument CreateDocumentSnapshot()
{
return new ReadOnlyDocument(this);
}
/// <summary> /// <summary>
/// Creates a snapshot of the current text. /// Creates a snapshot of the current text.
/// Additionally, creates a checkpoint that allows tracking document changes. /// Additionally, creates a checkpoint that allows tracking document changes.

76
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs

@ -37,8 +37,8 @@ namespace ICSharpCode.NRefactory.CSharp
{ {
CSharpFormattingOptions policy; CSharpFormattingOptions policy;
IDocument document; IDocument document;
IActionFactory factory; Script script;
List<TextReplaceAction> changes = new List<TextReplaceAction> (); //List<TextReplaceAction> changes = new List<TextReplaceAction> ();
Indent curIndent = new Indent (); Indent curIndent = new Indent ();
public int IndentLevel { public int IndentLevel {
@ -55,10 +55,6 @@ namespace ICSharpCode.NRefactory.CSharp
set; set;
} }
public List<TextReplaceAction> Changes {
get { return this.changes; }
}
public bool CorrectBlankLines { public bool CorrectBlankLines {
get; get;
set; set;
@ -71,16 +67,19 @@ namespace ICSharpCode.NRefactory.CSharp
public string EolMarker { get; set; } public string EolMarker { get; set; }
public AstFormattingVisitor (CSharpFormattingOptions policy, IDocument document, IActionFactory factory, public AstFormattingVisitor (CSharpFormattingOptions policy, IDocument document, Script script, bool tabsToSpaces = false, int indentationSize = 4)
bool tabsToSpaces = false, int indentationSize = 4)
{ {
if (factory == null) if (policy == null)
throw new ArgumentNullException ("factory"); throw new ArgumentNullException("policy");
if (document == null)
throw new ArgumentNullException("document");
if (script == null)
throw new ArgumentNullException("script");
this.policy = policy; this.policy = policy;
this.document = document; this.document = document;
this.script = script;
this.curIndent.TabsToSpaces = tabsToSpaces; this.curIndent.TabsToSpaces = tabsToSpaces;
this.curIndent.TabSize = indentationSize; this.curIndent.TabSize = indentationSize;
this.factory = factory;
this.EolMarker = Environment.NewLine; this.EolMarker = Environment.NewLine;
CorrectBlankLines = true; CorrectBlankLines = true;
} }
@ -972,34 +971,35 @@ namespace ICSharpCode.NRefactory.CSharp
void AddChange (int offset, int removedChars, string insertedText) void AddChange (int offset, int removedChars, string insertedText)
{ {
if (changes.Any (c => c.Offset == offset && c.RemovedChars == removedChars script.Replace(offset, removedChars, insertedText);
&& c.InsertedText == insertedText)) // if (changes.Any (c => c.Offset == offset && c.RemovedChars == removedChars
return; // && c.InsertedText == insertedText))
string currentText = document.GetText (offset, removedChars); // return;
if (currentText == insertedText) // string currentText = document.GetText (offset, removedChars);
return; // if (currentText == insertedText)
if (currentText.Any (c => !(char.IsWhiteSpace (c) || c == '\r' || c == '\t' || c == '{' || c == '}'))) // return;
throw new InvalidOperationException ("Tried to remove non ws chars: '" + currentText + "'"); // if (currentText.Any (c => !(char.IsWhiteSpace (c) || c == '\r' || c == '\t' || c == '{' || c == '}')))
foreach (var change in changes) { // throw new InvalidOperationException ("Tried to remove non ws chars: '" + currentText + "'");
if (change.Offset == offset) { // foreach (var change in changes) {
if (removedChars > 0 && insertedText == change.InsertedText) { // if (change.Offset == offset) {
change.RemovedChars = removedChars; // if (removedChars > 0 && insertedText == change.InsertedText) {
// change.RemovedChars = removedChars;
//// change.InsertedText = insertedText;
// return;
// }
// if (!string.IsNullOrEmpty (change.InsertedText)) {
// change.InsertedText += insertedText;
// } else {
// change.InsertedText = insertedText; // change.InsertedText = insertedText;
return; // }
} // change.RemovedChars = System.Math.Max (removedChars, change.RemovedChars);
if (!string.IsNullOrEmpty (change.InsertedText)) { // return;
change.InsertedText += insertedText; // }
} else { // }
change.InsertedText = insertedText; // //Console.WriteLine ("offset={0}, removedChars={1}, insertedText={2}", offset, removedChars, insertedText == null ? "<null>" : insertedText.Replace ("\n", "\\n").Replace ("\r", "\\r").Replace ("\t", "\\t").Replace (" ", "."));
} // //Console.WriteLine (Environment.StackTrace);
change.RemovedChars = System.Math.Max (removedChars, change.RemovedChars); //
return; // changes.Add (factory.CreateTextReplaceAction (offset, removedChars, insertedText));
}
}
//Console.WriteLine ("offset={0}, removedChars={1}, insertedText={2}", offset, removedChars, insertedText == null ? "<null>" : insertedText.Replace ("\n", "\\n").Replace ("\r", "\\r").Replace ("\t", "\\t").Replace (" ", "."));
//Console.WriteLine (Environment.StackTrace);
changes.Add (factory.CreateTextReplaceAction (offset, removedChars, insertedText));
} }
public bool IsLineIsEmptyUpToEol (TextLocation startLocation) public bool IsLineIsEmptyUpToEol (TextLocation startLocation)

7
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj

@ -243,7 +243,6 @@
<Compile Include="Parser\mcs\typespec.cs" /> <Compile Include="Parser\mcs\typespec.cs" />
<Compile Include="Parser\mcs\visit.cs" /> <Compile Include="Parser\mcs\visit.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Refactoring\Action.cs" />
<Compile Include="Refactoring\ContextAction\AddAnotherAccessor.cs" /> <Compile Include="Refactoring\ContextAction\AddAnotherAccessor.cs" />
<Compile Include="Refactoring\ContextAction\CheckIfParameterIsNull.cs" /> <Compile Include="Refactoring\ContextAction\CheckIfParameterIsNull.cs" />
<Compile Include="Refactoring\ContextAction\ConvertDecToHex.cs" /> <Compile Include="Refactoring\ContextAction\ConvertDecToHex.cs" />
@ -267,15 +266,9 @@
<Compile Include="Refactoring\ContextAction\SplitString.cs" /> <Compile Include="Refactoring\ContextAction\SplitString.cs" />
<Compile Include="Refactoring\ContextAction\UseExplicitType.cs" /> <Compile Include="Refactoring\ContextAction\UseExplicitType.cs" />
<Compile Include="Refactoring\ContextAction\UseVarKeyword.cs" /> <Compile Include="Refactoring\ContextAction\UseVarKeyword.cs" />
<Compile Include="Refactoring\CreateLinkAction.cs" />
<Compile Include="Refactoring\FormatTextAction.cs" />
<Compile Include="Refactoring\IActionFactory.cs" />
<Compile Include="Refactoring\IContextAction.cs" /> <Compile Include="Refactoring\IContextAction.cs" />
<Compile Include="Refactoring\NodeOutputAction.cs" />
<Compile Include="Refactoring\NodeSelectionAction.cs" />
<Compile Include="Refactoring\RefactoringContext.cs" /> <Compile Include="Refactoring\RefactoringContext.cs" />
<Compile Include="Refactoring\Script.cs" /> <Compile Include="Refactoring\Script.cs" />
<Compile Include="Refactoring\TextReplaceAction.cs" />
<Compile Include="Refactoring\TypeSystemAstBuilder.cs" /> <Compile Include="Refactoring\TypeSystemAstBuilder.cs" />
<Compile Include="Resolver\CompositeResolveVisitorNavigator.cs" /> <Compile Include="Resolver\CompositeResolveVisitorNavigator.cs" />
<Compile Include="Resolver\Conversions.cs" /> <Compile Include="Resolver\Conversions.cs" />

52
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Action.cs

@ -1,52 +0,0 @@
//
// Change.cs
//
// Author:
// Mike Krüger <mkrueger@novell.com>
//
// Copyright (c) 2011 Mike Krüger <mkrueger@novell.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;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// This is the base class for all refactoring operations that are performed.
/// </summary>
public abstract class Action
{
/// <summary>
/// Gets or sets the description.
/// </summary>
/// <value>
/// A brief description of the refactoring change.
/// </value>
public string Description {
get;
set;
}
/// <summary>
/// Performs the change.
/// </summary>
public abstract void Perform (Script script);
}
}

2
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/AddAnotherAccessor.cs

@ -57,7 +57,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
using (var script = context.StartScript ()) { using (var script = context.StartScript ()) {
script.InsertBefore (pdecl.RBraceToken, accessor); script.InsertBefore (pdecl.RBraceToken, accessor);
script.Select (accessorStatement); script.Select (accessorStatement);
script.FormatText (ctx => GetPropertyDeclaration (context)); script.FormatText (pdecl);
} }
} }

2
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/CreateEventInvocator.cs

@ -75,7 +75,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
ReturnType = new PrimitiveType ("void"), ReturnType = new PrimitiveType ("void"),
Modifiers = ICSharpCode.NRefactory.CSharp.Modifiers.Protected | ICSharpCode.NRefactory.CSharp.Modifiers.Virtual, Modifiers = ICSharpCode.NRefactory.CSharp.Modifiers.Protected | ICSharpCode.NRefactory.CSharp.Modifiers.Virtual,
Body = new BlockStatement () { Body = new BlockStatement () {
new VariableDeclarationStatement (context.CreateShortType (eventDeclaration.ReturnType), handlerName, new MemberReferenceExpression (new ThisReferenceExpression (), initializer.Name)), new VariableDeclarationStatement (eventDeclaration.ReturnType.Clone (), handlerName, new MemberReferenceExpression (new ThisReferenceExpression (), initializer.Name)),
new IfElseStatement () { new IfElseStatement () {
Condition = new BinaryOperatorExpression (new IdentifierExpression (handlerName), BinaryOperatorType.InEquality, new PrimitiveExpression (null)), Condition = new BinaryOperatorExpression (new IdentifierExpression (handlerName), BinaryOperatorType.InEquality, new PrimitiveExpression (null)),
TrueStatement = new ExpressionStatement (new InvocationExpression (new IdentifierExpression (handlerName), arguments)) TrueStatement = new ExpressionStatement (new InvocationExpression (new IdentifierExpression (handlerName), arguments))

4
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/CreateField.cs

@ -1,4 +1,4 @@
// //
// CreateField.cs // CreateField.cs
// //
// Author: // Author:
@ -63,7 +63,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (identifier.Parent is AssignmentExpression) { if (identifier.Parent is AssignmentExpression) {
var assign = (AssignmentExpression)identifier.Parent; var assign = (AssignmentExpression)identifier.Parent;
var other = assign.Left == identifier ? assign.Right : assign.Left; var other = assign.Left == identifier ? assign.Right : assign.Left;
return context.Resolve (other).Type.ConvertToAstType (); return context.CreateShortType (context.Resolve (other).Type);
} }
return null; return null;
} }

4
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/InvertIf.cs

@ -1,4 +1,4 @@
// //
// InvertIf.cs // InvertIf.cs
// //
// Author: // Author:
@ -46,7 +46,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
script.Replace (ifStatement.Condition, CSharpUtil.InvertCondition (ifStatement.Condition)); script.Replace (ifStatement.Condition, CSharpUtil.InvertCondition (ifStatement.Condition));
script.Replace (ifStatement.TrueStatement, ifStatement.FalseStatement); script.Replace (ifStatement.TrueStatement, ifStatement.FalseStatement);
script.Replace (ifStatement.FalseStatement, ifStatement.TrueStatement); script.Replace (ifStatement.FalseStatement, ifStatement.TrueStatement);
script.FormatText (ctx => GetIfElseStatement (ctx)); script.FormatText (ifStatement);
} }
} }

5
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/RemoveBackingStore.cs

@ -43,16 +43,15 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
var property = context.GetNode<PropertyDeclaration> (); var property = context.GetNode<PropertyDeclaration> ();
var field = GetBackingField (context); var field = GetBackingField (context);
context.ReplaceReferences (field, property);
// create new auto property // create new auto property
var newProperty = (PropertyDeclaration)property.Clone (); var newProperty = (PropertyDeclaration)property.Clone ();
newProperty.Getter.Body = BlockStatement.Null; newProperty.Getter.Body = BlockStatement.Null;
newProperty.Setter.Body = BlockStatement.Null; newProperty.Setter.Body = BlockStatement.Null;
using (var script = context.StartScript ()) { using (var script = context.StartScript ()) {
script.Remove (context.Unit.GetNodeAt<FieldDeclaration> (field.Region.BeginLine, field.Region.BeginColumn)); script.Remove (context.RootNode.GetNodeAt<FieldDeclaration> (field.Region.BeginLine, field.Region.BeginColumn));
script.Replace (property, newProperty); script.Replace (property, newProperty);
script.Rename (field, newProperty.Name);
} }
} }

6
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/RemoveBraces.cs

@ -1,4 +1,4 @@
// //
// RemoveBraces.cs // RemoveBraces.cs
// //
// Author: // Author:
@ -42,7 +42,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
using (var script = context.StartScript ()) { using (var script = context.StartScript ()) {
script.Remove (block.LBraceToken); script.Remove (block.LBraceToken);
script.Remove (block.RBraceToken); script.Remove (block.RBraceToken);
script.FormatText (ctx => ctx.Unit.GetNodeAt (block.Parent.StartLocation)); script.FormatText (block.Parent);
} }
} }
@ -53,7 +53,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return null; return null;
if (!(block.Parent is Statement)) if (!(block.Parent is Statement))
return null; return null;
if (block.Statements.Count () != 1) if (block.Statements.Count != 1)
return null; return null;
return block; return block;
} }

44
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/CreateLinkAction.cs

@ -1,44 +0,0 @@
//
// CreateLinkAction.cs
//
// Author:
// Mike Krüger <mkrueger@novell.com>
//
// Copyright (c) 2011 Mike Krüger <mkrueger@novell.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 System.Collections.Generic;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
public abstract class CreateLinkAction : Action
{
public IEnumerable<AstNode> Linked {
get;
private set;
}
public CreateLinkAction (IEnumerable<AstNode> linked)
{
this.Linked = linked;
}
}
}

39
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/FormatTextAction.cs

@ -1,39 +0,0 @@
//
// FormatTextAction.cs
//
// Author:
// Mike Krüger <mkrueger@novell.com>
//
// Copyright (c) 2011 Mike Krüger <mkrueger@novell.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;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
public abstract class FormatTextAction : Action
{
public Func<RefactoringContext, AstNode> Callback { get; private set; }
public FormatTextAction (Func<RefactoringContext, AstNode> callback)
{
this.Callback = callback;
}
}
}

75
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/IActionFactory.cs

@ -1,75 +0,0 @@
//
// IChangeFactory.cs
//
// Author:
// Mike Krüger <mkrueger@novell.com>
//
// Copyright (c) 2011 Mike Krüger <mkrueger@novell.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 System.Collections.Generic;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// A factory that creates the changes.
/// </summary>
public interface IActionFactory
{
TextReplaceAction CreateTextReplaceAction (int offset, int removedChars, string insertedText);
NodeOutputAction CreateNodeOutputAction (int offset, int removedChars, NodeOutput output);
NodeSelectionAction CreateNodeSelectionAction (AstNode node);
FormatTextAction CreateFormatTextAction (Func<RefactoringContext, AstNode> callback);
CreateLinkAction CreateLinkAction (IEnumerable<AstNode> linkedNodes);
}
public abstract class AbstractActionFactory : IActionFactory
{
#region IActionFactory implementation
public virtual TextReplaceAction CreateTextReplaceAction (int offset, int removedChars, string insertedText)
{
throw new NotImplementedException ();
}
public virtual NodeOutputAction CreateNodeOutputAction (int offset, int removedChars, NodeOutput output)
{
throw new NotImplementedException ();
}
public virtual NodeSelectionAction CreateNodeSelectionAction (AstNode node)
{
throw new NotImplementedException ();
}
public virtual FormatTextAction CreateFormatTextAction (Func<RefactoringContext, AstNode> callback)
{
throw new NotImplementedException ();
}
public virtual CreateLinkAction CreateLinkAction (IEnumerable<AstNode> linkedNodes)
{
throw new NotImplementedException ();
}
#endregion
}
}

2
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/IContextAction.cs

@ -1,4 +1,4 @@
// //
// ContextAction.cs // ContextAction.cs
// //
// Author: // Author:

118
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/NodeOutputAction.cs

@ -1,118 +0,0 @@
//
// NodeOutputChange.cs
//
// Author:
// Mike Krüger <mkrueger@novell.com>
//
// Copyright (c) 2011 Mike Krüger <mkrueger@novell.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 System.Collections.Generic;
using ICSharpCode.NRefactory.Editor;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// This is the node that should be outputted at a position.
/// </summary>
public class NodeOutput
{
public readonly Dictionary<AstNode, Segment> NodeSegments = new Dictionary<AstNode, Segment> ();
public string Text {
get;
set;
}
public void Trim ()
{
for (int i = 0; i < Text.Length; i++) {
char ch = Text [i];
if (ch != ' ' && ch != '\t') {
if (i > 0) {
Text = Text.Substring (i);
foreach (var seg in NodeSegments.Values) {
seg.Offset -= i;
}
}
break;
}
}
}
public class Segment : ISegment
{
public int Offset {
get;
internal set;
}
public int Length {
get;
internal set;
}
public int EndOffset {
get {
return Offset + Length;
}
}
public Segment (int offset)
{
this.Offset = offset;
}
public override string ToString ()
{
return string.Format ("[NodeOutput.Segment: Offset={0}, Length={1}, EndOffset={2}]", Offset, Length, EndOffset);
}
}
}
/// <summary>
/// Outputs an Ast node at a specific offset.
/// </summary>
public abstract class NodeOutputAction : TextReplaceAction
{
public NodeOutput NodeOutput {
get;
private set;
}
public override string InsertedText {
get {
return NodeOutput.Text;
}
set {
throw new NotSupportedException ("Changing text with this propery is not supported on NodeOutputChange.");
}
}
public NodeOutputAction (int offset, int removedChars, NodeOutput output) : base (offset, removedChars)
{
if (output == null)
throw new ArgumentNullException ("output");
this.NodeOutput = output;
}
}
}

40
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/NodeSelectionAction.cs

@ -1,40 +0,0 @@
//
// SelectionAction.cs
//
// Author:
// Mike Krüger <mkrueger@novell.com>
//
// Copyright (c) 2011 Mike Krüger <mkrueger@novell.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;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
public abstract class NodeSelectionAction : Action
{
public AstNode AstNode { get; private set; }
public NodeSelectionAction (AstNode astNode)
{
this.AstNode = astNode;
}
}
}

121
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs

@ -25,37 +25,51 @@
// THE SOFTWARE. // THE SOFTWARE.
using System; using System;
using System.Linq; using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.CSharp.TypeSystem;
using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.CSharp.Refactoring namespace ICSharpCode.NRefactory.CSharp.Refactoring
{ {
public abstract class RefactoringContext : AbstractActionFactory public abstract class RefactoringContext
{ {
public CompilationUnit Unit { readonly CSharpAstResolver resolver;
get; readonly CancellationToken cancellationToken;
protected set;
public RefactoringContext(CSharpAstResolver resolver, CancellationToken cancellationToken)
{
this.resolver = resolver;
this.cancellationToken = cancellationToken;
} }
public TextLocation Location { public CancellationToken CancellationToken {
get; get { return cancellationToken; }
protected set;
} }
public abstract bool Supports(Version version); public virtual AstNode RootNode {
get { return resolver.RootNode; }
}
public abstract TextLocation Location { get; }
public virtual bool Supports(Version version)
{
return true;
}
public ICompilation Compilation { public ICompilation Compilation {
get; get { return resolver.Compilation; }
protected set;
} }
public abstract CSharpFormattingOptions FormattingOptions { public virtual AstType CreateShortType (IType fullType)
get; {
var csResolver = resolver.GetResolverStateBefore(GetNode());
var builder = new TypeSystemAstBuilder(csResolver);
return builder.ConvertType(fullType);
} }
public abstract AstType CreateShortType (IType fullType);
public AstType CreateShortType (string ns, string name, int typeParameterCount = 0) public AstType CreateShortType (string ns, string name, int typeParameterCount = 0)
{ {
@ -68,34 +82,38 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return new MemberType (new SimpleType (ns), name); return new MemberType (new SimpleType (ns), name);
} }
public virtual AstType CreateShortType (AstType fullType)
{
return CreateShortType (Resolve (fullType).Type);
}
// public abstract IType GetDefinition (AstType resolvedType);
public abstract void ReplaceReferences (IMember member, MemberDeclaration replaceWidth);
public AstNode GetNode () public AstNode GetNode ()
{ {
return Unit.GetNodeAt (Location); return RootNode.GetNodeAt (Location);
} }
public T GetNode<T> () where T : AstNode public T GetNode<T> () where T : AstNode
{ {
return Unit.GetNodeAt<T> (Location); return RootNode.GetNodeAt<T> (Location);
} }
public abstract Script StartScript ();
#region Text stuff #region Text stuff
public abstract string EolMarker { get; } public virtual string EolMarker {
public abstract bool IsSomethingSelected { get; } get { return Environment.NewLine; }
public abstract string SelectedText { get; } }
public abstract int SelectionStart { get; }
public abstract int SelectionEnd { get; } public virtual bool IsSomethingSelected {
public abstract int SelectionLength { get; } get { return this.SelectionLength > 0; }
}
public virtual string SelectedText {
get { return string.Empty; }
}
public virtual int SelectionStart {
get { return 0; }
}
public virtual int SelectionEnd {
get { return 0; }
}
public virtual int SelectionLength {
get { return 0; }
}
public abstract int GetOffset (TextLocation location); public abstract int GetOffset (TextLocation location);
public int GetOffset (int line, int col) public int GetOffset (int line, int col)
{ {
@ -106,10 +124,28 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
#endregion #endregion
#region Resolving #region Resolving
public abstract ResolveResult Resolve (AstNode expression); public ResolveResult Resolve (AstNode node)
{
return resolver.Resolve (node, cancellationToken);
}
public IType ResolveType (AstType type)
{
return resolver.Resolve (type, cancellationToken).Type;
}
public IType GetExpectedType (Expression expression)
{
return resolver.GetExpectedType(expression, cancellationToken);
}
public Conversion GetConversion (Expression expression)
{
return resolver.GetConversion(expression, cancellationToken);
}
#endregion #endregion
public string GetNameProposal (string name, bool camelCase = true) public virtual string GetNameProposal (string name, bool camelCase = true)
{ {
string baseName = (camelCase ? char.ToLower (name [0]) : char.ToUpper (name [0])) + name.Substring (1); string baseName = (camelCase ? char.ToLower (name [0]) : char.ToUpper (name [0])) + name.Substring (1);
@ -129,17 +165,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
{ {
return baseName + (number > 0 ? (number + 1).ToString () : ""); return baseName + (number > 0 ? (number + 1).ToString () : "");
} }
}
public abstract Script StartScript();
public static class RefactoringExtensions
{
#region ConvertTypes
public static ICSharpCode.NRefactory.CSharp.AstType ConvertToAstType (this IType type)
{
var builder = new TypeSystemAstBuilder ();
return builder.ConvertType (type);
}
#endregion
} }
} }

277
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs

@ -1,6 +1,6 @@
// //
// Script.cs // Script.cs
// //
// Author: // Author:
// Mike Krüger <mkrueger@novell.com> // Mike Krüger <mkrueger@novell.com>
// //
@ -27,106 +27,175 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using ICSharpCode.NRefactory.Editor;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Refactoring namespace ICSharpCode.NRefactory.CSharp.Refactoring
{ {
/// <summary>
/// Class for creating change scripts.
/// 'Original document' = document without the change script applied.
/// 'Current document' = document with the change script (as far as it is already created) applies.
/// </summary>
public abstract class Script : IDisposable public abstract class Script : IDisposable
{ {
public RefactoringContext Context { internal struct Segment : ISegment
get; {
private set; readonly int offset;
} readonly int length;
protected readonly List<Action> changes = new List<Action> (); public int Offset {
get { return offset; }
public IEnumerable<Action> Actions { }
get {
return changes; public int Length {
get { return length; }
}
public int EndOffset {
get { return Offset + Length; }
}
public Segment (int offset, int length)
{
this.offset = offset;
this.length = length;
}
public override string ToString ()
{
return string.Format ("[Script.Segment: Offset={0}, Length={1}, EndOffset={2}]", Offset, Length, EndOffset);
} }
} }
public Script (RefactoringContext context) readonly string eolMarker;
{ readonly CSharpFormattingOptions formattingOptions;
if (context == null) Dictionary<AstNode, ISegment> segmentsForInsertedNodes = new Dictionary<AstNode, ISegment>();
throw new ArgumentNullException ("context");
this.Context = context;
}
public void Queue (Action change) protected Script(string eolMarker, CSharpFormattingOptions formattingOptions)
{ {
changes.Add (change); if (eolMarker == null)
throw new ArgumentNullException("eolMarker");
if (formattingOptions == null)
throw new ArgumentNullException("formattingOptions");
this.eolMarker = eolMarker;
this.formattingOptions = formattingOptions;
} }
public void InsertText (int offset, string text) /// <summary>
{ /// Given an offset in the original document (at the start of script execution),
Queue (Context.CreateTextReplaceAction (offset, 0, text)); /// returns the offset in the current document.
} /// </summary>
public abstract int GetCurrentOffset(int originalDocumentOffset);
public void InsertBefore (AstNode node, AstNode insertNode) /// <summary>
{ /// Given an offset in the original document (at the start of script execution),
var startOffset = Context.GetOffset (node.StartLocation.Line, 1); /// returns the offset in the current document.
var output = OutputNode (GetIndentLevelAt (startOffset), insertNode); /// </summary>
public abstract int GetCurrentOffset(TextLocation originalDocumentLocation);
if (!(insertNode is Expression || insertNode is AstType))
output.Text += Context.EolMarker; /// <summary>
/// Creates a tracked segment for the specified (offset,length)-segment.
Queue (Context.CreateNodeOutputAction (startOffset, 0, output)); /// Offset is interpreted to be an offset in the current document.
} /// </summary>
/// <returns>
public void AddTo (BlockStatement bodyStatement, AstNode insertNode) /// A segment that initially has the specified values, and updates
/// on every <see cref="Replace(int,int,string)"/> call.
/// </returns>
protected abstract ISegment CreateTrackedSegment(int offset, int length);
protected ISegment GetSegment(AstNode node)
{ {
var startOffset = Context.GetOffset (bodyStatement.LBraceToken.StartLocation) + 1; ISegment segment;
var output = OutputNode (GetIndentLevelAt (startOffset), insertNode, true); if (segmentsForInsertedNodes.TryGetValue(node, out segment))
Queue (Context.CreateNodeOutputAction (startOffset, 0, output)); return segment;
if (node.StartLocation.IsEmpty || node.EndLocation.IsEmpty) {
throw new InvalidOperationException("Trying to get the position of a node that is not part of the original document and was not inserted");
}
int startOffset = GetCurrentOffset(node.StartLocation);
int endOffset = GetCurrentOffset(node.EndLocation);
return new Segment(startOffset, endOffset - startOffset);
} }
public void Link (params AstNode[] nodes) /// <summary>
/// Replaces text.
/// </summary>
/// <param name="offset">The starting offset of the text to be replaced.</param>
/// <param name="length">The length of the text to be replaced.</param>
/// <param name="newText">The new text.</param>
public abstract void Replace (int offset, int length, string newText);
public void InsertText(int offset, string newText)
{ {
Queue (Context.CreateLinkAction (nodes)); Replace(offset, 0, newText);
} }
public void Link (IEnumerable<AstNode> nodes) public CSharpFormattingOptions FormattingOptions {
{ get { return formattingOptions; }
Queue (Context.CreateLinkAction (nodes));
} }
public void Remove (AstNode node) public void Remove (AstNode node)
{ {
var startOffset = Context.GetOffset (node.StartLocation); var segment = GetSegment(node);
var endOffset = Context.GetOffset (node.EndLocation); Replace(segment.Offset, segment.Length, string.Empty);
Remove (startOffset, endOffset - startOffset);
} }
void Remove (int offset, int length) public void InsertBefore (AstNode node, AstNode insertNode)
{ {
Queue (Context.CreateTextReplaceAction (offset, length, null)); var startOffset = GetCurrentOffset (new TextLocation(node.StartLocation.Line, 1));
var output = OutputNode (GetIndentLevelAt (startOffset), insertNode);
string text = output.Text;
if (!(insertNode is Expression || insertNode is AstType))
text += eolMarker;
InsertText(startOffset, text);
output.RegisterTrackedSegments(this, startOffset);
}
public void AddTo (BlockStatement bodyStatement, AstNode insertNode)
{
var startOffset = GetCurrentOffset (bodyStatement.LBraceToken.EndLocation);
var output = OutputNode (1 + GetIndentLevelAt (startOffset), insertNode, true);
InsertText (startOffset, output.Text);
output.RegisterTrackedSegments (this, startOffset);
} }
void Replace (int offset, int length, string text) public virtual void Link (params AstNode[] nodes)
{ {
Queue (Context.CreateTextReplaceAction (offset, length, text)); // Default implementation: do nothing
// Derived classes are supposed to enter the text editor's linked state.
} }
public void Replace (AstNode node, AstNode replaceWith) public void Replace (AstNode node, AstNode replaceWith)
{ {
var startOffset = Context.GetOffset (node.StartLocation); var segment = GetSegment (node);
var endOffset = Context.GetOffset (node.EndLocation); int startOffset = segment.Offset;
int level = 0; int level = 0;
if (!(replaceWith is Expression) && !(replaceWith is AstType)) if (!(replaceWith is Expression) && !(replaceWith is AstType))
level = GetIndentLevelAt (startOffset); level = GetIndentLevelAt (startOffset);
NodeOutput output = OutputNode (level, replaceWith); NodeOutput output = OutputNode (level, replaceWith);
output.Trim (); output.TrimStart ();
Queue (Context.CreateNodeOutputAction (startOffset, endOffset - startOffset, output)); Replace (startOffset, segment.Length, output.Text);
output.RegisterTrackedSegments(this, startOffset);
} }
public void FormatText (Func<RefactoringContext, AstNode> callback) public void FormatText (AstNode node)
{ {
Queue (Context.CreateFormatTextAction (callback)); var segment = GetSegment(node);
FormatText(segment.Offset, segment.Length);
} }
public abstract void FormatText (int offset, int length);
public void Select (AstNode node) public void Select (AstNode node)
{ {
Queue (Context.CreateNodeSelectionAction (node)); var segment = GetSegment(node);
Select(segment.Offset, segment.Length);
}
public virtual void Select (int offset, int length)
{
// default implementation: do nothing
// Derived classes are supposed to set the text editor's selection
} }
public enum InsertPosition { public enum InsertPosition {
@ -138,65 +207,101 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
public abstract void InsertWithCursor (string operation, AstNode node, InsertPosition defaultPosition); public abstract void InsertWithCursor (string operation, AstNode node, InsertPosition defaultPosition);
protected int GetIndentLevelAt (int offset) protected virtual int GetIndentLevelAt (int offset)
{ {
var node = Context.Unit.GetNodeAt (Context.GetLocation (offset)); return 0;
int level = 0;
while (node != null) {
if (node is BlockStatement || node is TypeDeclaration || node is NamespaceDeclaration)
level++;
node = node.Parent;
}
return level;
} }
sealed class SegmentTrackingOutputFormatter : TextWriterOutputFormatter sealed class SegmentTrackingOutputFormatter : TextWriterOutputFormatter
{ {
readonly NodeOutput result; internal List<KeyValuePair<AstNode, Segment>> NewSegments = new List<KeyValuePair<AstNode, Segment>>();
Stack<int> startOffsets = new Stack<int>();
readonly StringWriter stringWriter; readonly StringWriter stringWriter;
public SegmentTrackingOutputFormatter(NodeOutput result, StringWriter stringWriter) public SegmentTrackingOutputFormatter(StringWriter stringWriter)
: base(stringWriter) : base(stringWriter)
{ {
this.result = result;
this.stringWriter = stringWriter; this.stringWriter = stringWriter;
} }
public override void StartNode(AstNode node) public override void StartNode(AstNode node)
{ {
base.StartNode(node); base.StartNode(node);
result.NodeSegments [node] = new NodeOutput.Segment (stringWriter.GetStringBuilder ().Length); startOffsets.Push(stringWriter.GetStringBuilder ().Length);
} }
public override void EndNode(AstNode node) public override void EndNode(AstNode node)
{ {
var nodeSegment = result.NodeSegments [node]; int startOffset = startOffsets.Pop();
nodeSegment.Length = stringWriter.GetStringBuilder ().Length - nodeSegment.Offset; int endOffset = stringWriter.GetStringBuilder ().Length;
NewSegments.Add(new KeyValuePair<AstNode, Segment>(node, new Segment(startOffset, endOffset - startOffset)));
base.EndNode(node); base.EndNode(node);
} }
} }
protected NodeOutput OutputNode (int indentLevel, AstNode node, bool startWithNewLine = false) protected NodeOutput OutputNode(int indentLevel, AstNode node, bool startWithNewLine = false)
{ {
var result = new NodeOutput ();
var stringWriter = new StringWriter (); var stringWriter = new StringWriter ();
var formatter = new SegmentTrackingOutputFormatter (result, stringWriter); var formatter = new SegmentTrackingOutputFormatter (stringWriter);
formatter.Indentation = indentLevel; formatter.Indentation = indentLevel;
stringWriter.NewLine = Context.EolMarker; stringWriter.NewLine = eolMarker;
if (startWithNewLine) if (startWithNewLine)
formatter.NewLine (); formatter.NewLine ();
var visitor = new CSharpOutputVisitor (formatter, Context.FormattingOptions); var visitor = new CSharpOutputVisitor (formatter, formattingOptions);
node.AcceptVisitor (visitor, null); node.AcceptVisitor (visitor, null);
result.Text = stringWriter.ToString ().TrimEnd (); string text = stringWriter.ToString().TrimEnd();
if (node is FieldDeclaration) if (node is FieldDeclaration)
result.Text += Context.EolMarker; text += eolMarker;
return new NodeOutput(text, formatter.NewSegments);
return result;
} }
#region IDisposable implementation protected class NodeOutput
public abstract void Dispose (); {
#endregion string text;
List<KeyValuePair<AstNode, Segment>> newSegments;
int trimmedLength;
internal NodeOutput(string text, List<KeyValuePair<AstNode, Segment>> newSegments)
{
this.text = text;
this.newSegments = newSegments;
}
public string Text {
get { return text; }
}
public void TrimStart()
{
for (int i = 0; i < text.Length; i++) {
char ch = text [i];
if (ch != ' ' && ch != '\t') {
if (i > 0) {
text = text.Substring (i);
trimmedLength = i;
}
break;
}
}
}
public void RegisterTrackedSegments(Script script, int insertionOffset)
{
foreach (var pair in newSegments) {
int offset = insertionOffset + pair.Value.Offset - trimmedLength;
ISegment trackedSegment = script.CreateTrackedSegment(offset, pair.Value.Length);
script.segmentsForInsertedNodes.Add(pair.Key, trackedSegment);
}
}
}
/// <summary>
/// Performs a rename refactoring.
/// </summary>
public abstract void Rename(IEntity entity, string name);
public abstract void Dispose();
} }
} }

134
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/TextReplaceAction.cs

@ -1,134 +0,0 @@
//
// TextReplaceChange.cs
//
// Author:
// Mike Krüger <mkrueger@novell.com>
//
// Copyright (c) 2011 Mike Krüger <mkrueger@novell.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;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
/// <summary>
/// This is the base action for changes in a text document.
/// </summary>
public abstract class TextReplaceAction : Action
{
/// <summary>
/// Gets or sets the offset.
/// </summary>
/// <value>
/// The offset of the replace.
/// </value>
public int Offset {
get;
set;
}
int removedChars;
/// <summary>
/// Gets or sets the numer of chars to removed.
/// </summary>
/// <value>
/// The numer of chars to remove.
/// </value>
/// <exception cref='ArgumentOutOfRangeException'>
/// Is thrown when an argument passed to a method is invalid because it is outside the allowable range of values as
/// specified by the method.
/// </exception>
public int RemovedChars {
get {
return removedChars;
}
set {
if (value < 0)
throw new ArgumentOutOfRangeException ("RemovedChars", "needs to be >= 0");
removedChars = value;
}
}
/// <summary>
/// Gets or sets the inserted text.
/// </summary>
/// <value>
/// The text to insert.
/// </value>
public virtual string InsertedText {
get;
set;
}
/// <summary>
/// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.CSharp.Refactoring.TextReplaceAction"/> class.
/// </summary>
/// <param name='offset'>
/// The offset of the replace.
/// </param>
/// <param name='removedChars'>
/// The numer of chars to remove.
/// </param>
/// <exception cref='ArgumentOutOfRangeException'>
/// Is thrown when an argument passed to a method is invalid because it is outside the allowable range of values as
/// specified by the method.
/// </exception>
protected TextReplaceAction (int offset, int removedChars)
{
if (removedChars < 0)
throw new ArgumentOutOfRangeException ("removedChars", "removedChars needs to be >= 0");
if (offset < 0)
throw new ArgumentOutOfRangeException ("offset", "offset needs to be >= 0");
this.removedChars = removedChars;
this.Offset = offset;
}
/// <summary>
/// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.CSharp.Refactoring.TextReplaceAction"/> class.
/// </summary>
/// <param name='offset'>
/// The offset of the replace.
/// </param>
/// <param name='removedChars'>
/// The numer of chars to remove.
/// </param>
/// <param name='insertedText'>
/// The text to insert.
/// </param>
/// <exception cref='ArgumentOutOfRangeException'>
/// Is thrown when an argument passed to a method is invalid because it is outside the allowable range of values as
/// specified by the method.
public TextReplaceAction (int offset, int removedChars, string insertedText) : this (offset, removedChars)
{
this.InsertedText = insertedText;
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="ICSharpCode.NRefactory.CSharp.Refactoring.TextReplaceAction"/>.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents the current <see cref="ICSharpCode.NRefactory.CSharp.Refactoring.TextReplaceAction"/>.
/// </returns>
public override string ToString ()
{
return string.Format ("[TextReplaceAction: Offset={0}, RemovedChars={1}, InsertedText={2}]", Offset, RemovedChars, InsertedText == null ? "<null>" : InsertedText.Replace ("\t", "\\t").Replace ("\n", "\\n").Replace ("\r", "\\r"));
}
}
}

93
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs

@ -28,6 +28,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary> /// <summary>
/// Resolves C# AST nodes. /// Resolves C# AST nodes.
/// </summary> /// </summary>
/// <remarks>This class is thread-safe.</remarks>
public class CSharpAstResolver public class CSharpAstResolver
{ {
readonly CSharpResolver initialResolverState; readonly CSharpResolver initialResolverState;
@ -120,17 +121,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (navigator == null) if (navigator == null)
throw new ArgumentNullException("navigator"); throw new ArgumentNullException("navigator");
if (resolverInitialized) lock (resolveVisitor) {
throw new InvalidOperationException("Applying a navigator is only valid as the first operation on the CSharpAstResolver."); if (resolverInitialized)
throw new InvalidOperationException("Applying a navigator is only valid as the first operation on the CSharpAstResolver.");
resolverInitialized = true;
resolveVisitor.cancellationToken = cancellationToken; resolverInitialized = true;
resolveVisitor.SetNavigator(navigator); resolveVisitor.cancellationToken = cancellationToken;
try { resolveVisitor.SetNavigator(navigator);
resolveVisitor.Scan(rootNode); try {
} finally { resolveVisitor.Scan(rootNode);
resolveVisitor.SetNavigator(null); } finally {
resolveVisitor.cancellationToken = CancellationToken.None; resolveVisitor.SetNavigator(null);
resolveVisitor.cancellationToken = CancellationToken.None;
}
} }
} }
@ -141,14 +144,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
if (node == null || node.IsNull || IsUnresolvableNode(node)) if (node == null || node.IsNull || IsUnresolvableNode(node))
return ErrorResolveResult.UnknownError; return ErrorResolveResult.UnknownError;
InitResolver(); lock (resolveVisitor) {
resolveVisitor.cancellationToken = cancellationToken; InitResolver();
try { resolveVisitor.cancellationToken = cancellationToken;
ResolveResult rr = resolveVisitor.GetResolveResult(node); try {
Debug.Assert(rr != null); ResolveResult rr = resolveVisitor.GetResolveResult(node);
return rr; Debug.Assert(rr != null);
} finally { return rr;
resolveVisitor.cancellationToken = CancellationToken.None; } finally {
resolveVisitor.cancellationToken = CancellationToken.None;
}
} }
} }
@ -168,14 +173,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
if (node == null || node.IsNull) if (node == null || node.IsNull)
throw new ArgumentNullException("node"); throw new ArgumentNullException("node");
InitResolver(); lock (resolveVisitor) {
resolveVisitor.cancellationToken = cancellationToken; InitResolver();
try { resolveVisitor.cancellationToken = cancellationToken;
CSharpResolver resolver = resolveVisitor.GetResolverStateBefore(node); try {
Debug.Assert(resolver != null); CSharpResolver resolver = resolveVisitor.GetResolverStateBefore(node);
return resolver; Debug.Assert(resolver != null);
} finally { return resolver;
resolveVisitor.cancellationToken = CancellationToken.None; } finally {
resolveVisitor.cancellationToken = CancellationToken.None;
}
} }
} }
@ -191,14 +198,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
node = node.Parent; node = node.Parent;
if (node == null) if (node == null)
return initialResolverState; return initialResolverState;
InitResolver(); lock (resolveVisitor) {
resolveVisitor.cancellationToken = cancellationToken; InitResolver();
try { resolveVisitor.cancellationToken = cancellationToken;
CSharpResolver resolver = resolveVisitor.GetResolverStateAfter(node); try {
Debug.Assert(resolver != null); CSharpResolver resolver = resolveVisitor.GetResolverStateAfter(node);
return resolver; Debug.Assert(resolver != null);
} finally { return resolver;
resolveVisitor.cancellationToken = CancellationToken.None; } finally {
resolveVisitor.cancellationToken = CancellationToken.None;
}
} }
} }
@ -206,12 +215,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{ {
if (expr == null || expr.IsNull) if (expr == null || expr.IsNull)
throw new ArgumentNullException("expr"); throw new ArgumentNullException("expr");
InitResolver(); lock (resolveVisitor) {
resolveVisitor.cancellationToken = cancellationToken; InitResolver();
try { resolveVisitor.cancellationToken = cancellationToken;
return resolveVisitor.GetConversionWithTargetType(expr); try {
} finally { return resolveVisitor.GetConversionWithTargetType(expr);
resolveVisitor.cancellationToken = CancellationToken.None; } finally {
resolveVisitor.cancellationToken = CancellationToken.None;
}
} }
} }

4
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs

@ -35,6 +35,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary> /// <summary>
/// Contains the main resolver logic. /// Contains the main resolver logic.
/// </summary> /// </summary>
/// <remarks>
/// Despite being immutable, this class is not thread-safe. (due to caches)
/// TODO: fix this, it's very hard for NR users to tell whether two CSharpResolvers share the same caches
/// </remarks>
public class CSharpResolver public class CSharpResolver
{ {
static readonly ResolveResult ErrorResult = ErrorResolveResult.UnknownError; static readonly ResolveResult ErrorResult = ErrorResolveResult.UnknownError;

5
src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/IDocument.cs

@ -27,6 +27,11 @@ namespace ICSharpCode.NRefactory.Editor
/// </summary> /// </summary>
public interface IDocument : ITextSource, IServiceProvider public interface IDocument : ITextSource, IServiceProvider
{ {
/// <summary>
/// Creates an immutable snapshot of this document.
/// </summary>
IDocument CreateDocumentSnapshot();
/// <summary> /// <summary>
/// Gets/Sets the text of the whole document.. /// Gets/Sets the text of the whole document..
/// </summary> /// </summary>

6
src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs

@ -324,6 +324,12 @@ namespace ICSharpCode.NRefactory.Editor
return textSource.CreateSnapshot(offset, length); return textSource.CreateSnapshot(offset, length);
} }
/// <inheritdoc/>
public IDocument CreateDocumentSnapshot()
{
return this; // ReadOnlyDocument is immutable
}
/// <inheritdoc/> /// <inheritdoc/>
public System.IO.TextReader CreateReader() public System.IO.TextReader CreateReader()
{ {

10
src/Main/Base/Project/Src/Util/ExtensionMethods.cs

@ -77,7 +77,15 @@ namespace ICSharpCode.SharpDevelop
/// </summary> /// </summary>
public static void FireAndForget(this Task task) public static void FireAndForget(this Task task)
{ {
task.ContinueWith(t => { if (t.Exception != null) Core.MessageService.ShowException(t.Exception); }); task.ContinueWith(
t => {
if (t.Exception != null) {
if (t.Exception.InnerExceptions.Count == 1)
Core.MessageService.ShowException(t.Exception.InnerExceptions[0]);
else
Core.MessageService.ShowException(t.Exception);
}
});
} }
/// <summary> /// <summary>

Loading…
Cancel
Save