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

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

@ -22,26 +22,24 @@ namespace CSharpBinding.Refactoring @@ -22,26 +22,24 @@ namespace CSharpBinding.Refactoring
{
readonly ITextEditor editor;
readonly ITextSource textSource;
readonly TextLocation location;
volatile IDocument document;
readonly CSharpAstResolver resolver;
int selectionStart, selectionLength;
CancellationToken cancellationToken;
public SDRefactoringContext(ITextSource textSource, CSharpAstResolver resolver, TextLocation location, int selectionStart, int selectionLength, CancellationToken cancellationToken)
: base(resolver, cancellationToken)
{
this.textSource = textSource;
this.document = textSource as IDocument;
this.resolver = resolver;
this.selectionStart = selectionStart;
this.selectionLength = selectionLength;
this.cancellationToken = cancellationToken;
this.Unit = resolver.RootNode as CompilationUnit;
this.Compilation = resolver.Compilation;
this.Location = location;
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.textSource = editor.Document;
@ -49,10 +47,7 @@ namespace CSharpBinding.Refactoring @@ -49,10 +47,7 @@ namespace CSharpBinding.Refactoring
this.resolver = resolver;
this.selectionStart = editor.SelectionStart;
this.selectionLength = editor.SelectionLength;
this.Unit = resolver.RootNode as CompilationUnit;
this.Compilation = resolver.Compilation;
this.Location = location;
this.location = location;
}
public override bool Supports(Version version)
@ -63,16 +58,15 @@ namespace CSharpBinding.Refactoring @@ -63,16 +58,15 @@ namespace CSharpBinding.Refactoring
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()
{
if (editor == null)
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 {
@ -95,17 +89,6 @@ namespace CSharpBinding.Refactoring @@ -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 {
get {
return selectionLength > 0;
@ -131,17 +114,11 @@ namespace CSharpBinding.Refactoring @@ -131,17 +114,11 @@ namespace CSharpBinding.Refactoring
return document.GetLocation(offset);
}
public override CSharpFormattingOptions FormattingOptions {
get {
return new CSharpFormattingOptions();
}
}
public override string EolMarker {
get {
if (document == null)
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 @@ @@ -3,9 +3,12 @@
using System;
using System.Collections.Generic;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.Refactoring;
using ICSharpCode.NRefactory.Editor;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Editor;
namespace CSharpBinding.Refactoring
@ -15,69 +18,95 @@ namespace CSharpBinding.Refactoring @@ -15,69 +18,95 @@ namespace CSharpBinding.Refactoring
/// </summary>
sealed class SDScript : Script
{
int indentationSize = 4;
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;
}
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++) {
actions [i].Perform (script);
var replaceChange = actions [i] as TextReplaceAction;
if (replaceChange == null)
continue;
for (int j = 0; j < actions.Count; j++) {
if (i == j)
continue;
var change = actions [j] as TextReplaceAction;
if (change == null)
continue;
if (replaceChange.Offset >= 0 && change.Offset >= 0) {
if (replaceChange.Offset < change.Offset) {
change.Offset -= replaceChange.RemovedChars;
if (!string.IsNullOrEmpty (replaceChange.InsertedText))
change.Offset += replaceChange.InsertedText.Length;
} 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);
}
int offset = editor.Document.GetOffset(originalDocumentLocation);
return offsetChangeMap.GetNewOffset(offset, AnchorMovementType.Default);
}
public override int GetCurrentOffset(int originalDocumentOffset)
{
return offsetChangeMap.GetNewOffset(originalDocumentOffset, AnchorMovementType.Default);
}
public override void Replace(int offset, int length, string newText)
{
var changeMapEntry = new OffsetChangeMapEntry(offset, length, newText.Length);
textSegmentCollection.UpdateOffsets(changeMapEntry);
offsetChangeMap.Add(changeMapEntry);
actions.Add(delegate { editor.Document.Replace(offset, length, newText); });
}
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)
{
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)
{
if (doc == null)
throw new ArgumentNullException ("doc");
if (output == null)
throw new ArgumentNullException ("output");
this.doc = doc;
}
public override void Perform (Script script)
{
doc.Replace (Offset, RemovedChars, NodeOutput.Text);
}
}
public override void Select(int offset, int length)
{
actions.Add(delegate { editor.Select(offset, length); });
}
public override void Link(params AstNode[] nodes)
{
}
public override void Dispose()
{
foreach (var action in actions)
action();
}
}
}

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

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

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

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

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

@ -1,52 +0,0 @@ @@ -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 @@ -57,7 +57,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
using (var script = context.StartScript ()) {
script.InsertBefore (pdecl.RBraceToken, accessor);
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 @@ -75,7 +75,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
ReturnType = new PrimitiveType ("void"),
Modifiers = ICSharpCode.NRefactory.CSharp.Modifiers.Protected | ICSharpCode.NRefactory.CSharp.Modifiers.Virtual,
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 () {
Condition = new BinaryOperatorExpression (new IdentifierExpression (handlerName), BinaryOperatorType.InEquality, new PrimitiveExpression (null)),
TrueStatement = new ExpressionStatement (new InvocationExpression (new IdentifierExpression (handlerName), arguments))

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

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

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

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
//
//
// InvertIf.cs
//
// Author:
@ -46,7 +46,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -46,7 +46,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
script.Replace (ifStatement.Condition, CSharpUtil.InvertCondition (ifStatement.Condition));
script.Replace (ifStatement.TrueStatement, ifStatement.FalseStatement);
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 @@ -43,16 +43,15 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
var property = context.GetNode<PropertyDeclaration> ();
var field = GetBackingField (context);
context.ReplaceReferences (field, property);
// create new auto property
var newProperty = (PropertyDeclaration)property.Clone ();
newProperty.Getter.Body = BlockStatement.Null;
newProperty.Setter.Body = BlockStatement.Null;
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.Rename (field, newProperty.Name);
}
}

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

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

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

@ -1,44 +0,0 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -1,4 +1,4 @@
//
//
// ContextAction.cs
//
// Author:

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

@ -1,118 +0,0 @@ @@ -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 @@ @@ -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 @@ @@ -25,37 +25,51 @@
// THE SOFTWARE.
using System;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.CSharp.TypeSystem;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
public abstract class RefactoringContext : AbstractActionFactory
public abstract class RefactoringContext
{
public CompilationUnit Unit {
get;
protected set;
readonly CSharpAstResolver resolver;
readonly CancellationToken cancellationToken;
public RefactoringContext(CSharpAstResolver resolver, CancellationToken cancellationToken)
{
this.resolver = resolver;
this.cancellationToken = cancellationToken;
}
public TextLocation Location {
get;
protected set;
public CancellationToken CancellationToken {
get { return cancellationToken; }
}
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 {
get;
protected set;
get { return resolver.Compilation; }
}
public abstract CSharpFormattingOptions FormattingOptions {
get;
public virtual AstType CreateShortType (IType fullType)
{
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)
{
@ -68,34 +82,38 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -68,34 +82,38 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
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 ()
{
return Unit.GetNodeAt (Location);
return RootNode.GetNodeAt (Location);
}
public T GetNode<T> () where T : AstNode
{
return Unit.GetNodeAt<T> (Location);
return RootNode.GetNodeAt<T> (Location);
}
public abstract Script StartScript ();
#region Text stuff
public abstract string EolMarker { get; }
public abstract bool IsSomethingSelected { get; }
public abstract string SelectedText { get; }
public abstract int SelectionStart { get; }
public abstract int SelectionEnd { get; }
public abstract int SelectionLength { get; }
public virtual string EolMarker {
get { return Environment.NewLine; }
}
public virtual bool IsSomethingSelected {
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 int GetOffset (int line, int col)
{
@ -106,10 +124,28 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -106,10 +124,28 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
#endregion
#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
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);
@ -129,17 +165,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -129,17 +165,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
return baseName + (number > 0 ? (number + 1).ToString () : "");
}
}
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
public abstract Script StartScript();
}
}

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

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
//
// Script.cs
//
//
// Author:
// Mike Krüger <mkrueger@novell.com>
//
@ -27,106 +27,175 @@ using System; @@ -27,106 +27,175 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ICSharpCode.NRefactory.Editor;
using ICSharpCode.NRefactory.TypeSystem;
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 RefactoringContext Context {
get;
private set;
}
protected readonly List<Action> changes = new List<Action> ();
public IEnumerable<Action> Actions {
get {
return changes;
internal struct Segment : ISegment
{
readonly int offset;
readonly int length;
public int Offset {
get { return offset; }
}
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)
{
if (context == null)
throw new ArgumentNullException ("context");
this.Context = context;
}
readonly string eolMarker;
readonly CSharpFormattingOptions formattingOptions;
Dictionary<AstNode, ISegment> segmentsForInsertedNodes = new Dictionary<AstNode, ISegment>();
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)
{
Queue (Context.CreateTextReplaceAction (offset, 0, text));
}
/// <summary>
/// Given an offset in the original document (at the start of script execution),
/// returns the offset in the current document.
/// </summary>
public abstract int GetCurrentOffset(int originalDocumentOffset);
public void InsertBefore (AstNode node, AstNode insertNode)
{
var startOffset = Context.GetOffset (node.StartLocation.Line, 1);
var output = OutputNode (GetIndentLevelAt (startOffset), insertNode);
if (!(insertNode is Expression || insertNode is AstType))
output.Text += Context.EolMarker;
Queue (Context.CreateNodeOutputAction (startOffset, 0, output));
}
public void AddTo (BlockStatement bodyStatement, AstNode insertNode)
/// <summary>
/// Given an offset in the original document (at the start of script execution),
/// returns the offset in the current document.
/// </summary>
public abstract int GetCurrentOffset(TextLocation originalDocumentLocation);
/// <summary>
/// Creates a tracked segment for the specified (offset,length)-segment.
/// Offset is interpreted to be an offset in the current document.
/// </summary>
/// <returns>
/// 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;
var output = OutputNode (GetIndentLevelAt (startOffset), insertNode, true);
Queue (Context.CreateNodeOutputAction (startOffset, 0, output));
ISegment segment;
if (segmentsForInsertedNodes.TryGetValue(node, out segment))
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)
{
Queue (Context.CreateLinkAction (nodes));
public CSharpFormattingOptions FormattingOptions {
get { return formattingOptions; }
}
public void Remove (AstNode node)
{
var startOffset = Context.GetOffset (node.StartLocation);
var endOffset = Context.GetOffset (node.EndLocation);
Remove (startOffset, endOffset - startOffset);
var segment = GetSegment(node);
Replace(segment.Offset, segment.Length, string.Empty);
}
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)
{
var startOffset = Context.GetOffset (node.StartLocation);
var endOffset = Context.GetOffset (node.EndLocation);
var segment = GetSegment (node);
int startOffset = segment.Offset;
int level = 0;
if (!(replaceWith is Expression) && !(replaceWith is AstType))
level = GetIndentLevelAt (startOffset);
NodeOutput output = OutputNode (level, replaceWith);
output.Trim ();
Queue (Context.CreateNodeOutputAction (startOffset, endOffset - startOffset, output));
output.TrimStart ();
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)
{
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 {
@ -138,65 +207,101 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -138,65 +207,101 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
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));
int level = 0;
while (node != null) {
if (node is BlockStatement || node is TypeDeclaration || node is NamespaceDeclaration)
level++;
node = node.Parent;
}
return level;
return 0;
}
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;
public SegmentTrackingOutputFormatter(NodeOutput result, StringWriter stringWriter)
public SegmentTrackingOutputFormatter(StringWriter stringWriter)
: base(stringWriter)
{
this.result = result;
this.stringWriter = stringWriter;
}
public override void StartNode(AstNode node)
{
base.StartNode(node);
result.NodeSegments [node] = new NodeOutput.Segment (stringWriter.GetStringBuilder ().Length);
startOffsets.Push(stringWriter.GetStringBuilder ().Length);
}
public override void EndNode(AstNode node)
{
var nodeSegment = result.NodeSegments [node];
nodeSegment.Length = stringWriter.GetStringBuilder ().Length - nodeSegment.Offset;
int startOffset = startOffsets.Pop();
int endOffset = stringWriter.GetStringBuilder ().Length;
NewSegments.Add(new KeyValuePair<AstNode, Segment>(node, new Segment(startOffset, endOffset - startOffset)));
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 formatter = new SegmentTrackingOutputFormatter (result, stringWriter);
var formatter = new SegmentTrackingOutputFormatter (stringWriter);
formatter.Indentation = indentLevel;
stringWriter.NewLine = Context.EolMarker;
stringWriter.NewLine = eolMarker;
if (startWithNewLine)
formatter.NewLine ();
var visitor = new CSharpOutputVisitor (formatter, Context.FormattingOptions);
var visitor = new CSharpOutputVisitor (formatter, formattingOptions);
node.AcceptVisitor (visitor, null);
result.Text = stringWriter.ToString ().TrimEnd ();
string text = stringWriter.ToString().TrimEnd();
if (node is FieldDeclaration)
result.Text += Context.EolMarker;
return result;
text += eolMarker;
return new NodeOutput(text, formatter.NewSegments);
}
#region IDisposable implementation
public abstract void Dispose ();
#endregion
protected class NodeOutput
{
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 @@ @@ -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 @@ -28,6 +28,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary>
/// Resolves C# AST nodes.
/// </summary>
/// <remarks>This class is thread-safe.</remarks>
public class CSharpAstResolver
{
readonly CSharpResolver initialResolverState;
@ -120,17 +121,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -120,17 +121,19 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (navigator == null)
throw new ArgumentNullException("navigator");
if (resolverInitialized)
throw new InvalidOperationException("Applying a navigator is only valid as the first operation on the CSharpAstResolver.");
resolverInitialized = true;
resolveVisitor.cancellationToken = cancellationToken;
resolveVisitor.SetNavigator(navigator);
try {
resolveVisitor.Scan(rootNode);
} finally {
resolveVisitor.SetNavigator(null);
resolveVisitor.cancellationToken = CancellationToken.None;
lock (resolveVisitor) {
if (resolverInitialized)
throw new InvalidOperationException("Applying a navigator is only valid as the first operation on the CSharpAstResolver.");
resolverInitialized = true;
resolveVisitor.cancellationToken = cancellationToken;
resolveVisitor.SetNavigator(navigator);
try {
resolveVisitor.Scan(rootNode);
} finally {
resolveVisitor.SetNavigator(null);
resolveVisitor.cancellationToken = CancellationToken.None;
}
}
}
@ -141,14 +144,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -141,14 +144,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
if (node == null || node.IsNull || IsUnresolvableNode(node))
return ErrorResolveResult.UnknownError;
InitResolver();
resolveVisitor.cancellationToken = cancellationToken;
try {
ResolveResult rr = resolveVisitor.GetResolveResult(node);
Debug.Assert(rr != null);
return rr;
} finally {
resolveVisitor.cancellationToken = CancellationToken.None;
lock (resolveVisitor) {
InitResolver();
resolveVisitor.cancellationToken = cancellationToken;
try {
ResolveResult rr = resolveVisitor.GetResolveResult(node);
Debug.Assert(rr != null);
return rr;
} finally {
resolveVisitor.cancellationToken = CancellationToken.None;
}
}
}
@ -168,14 +173,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -168,14 +173,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
if (node == null || node.IsNull)
throw new ArgumentNullException("node");
InitResolver();
resolveVisitor.cancellationToken = cancellationToken;
try {
CSharpResolver resolver = resolveVisitor.GetResolverStateBefore(node);
Debug.Assert(resolver != null);
return resolver;
} finally {
resolveVisitor.cancellationToken = CancellationToken.None;
lock (resolveVisitor) {
InitResolver();
resolveVisitor.cancellationToken = cancellationToken;
try {
CSharpResolver resolver = resolveVisitor.GetResolverStateBefore(node);
Debug.Assert(resolver != null);
return resolver;
} finally {
resolveVisitor.cancellationToken = CancellationToken.None;
}
}
}
@ -191,14 +198,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -191,14 +198,16 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
node = node.Parent;
if (node == null)
return initialResolverState;
InitResolver();
resolveVisitor.cancellationToken = cancellationToken;
try {
CSharpResolver resolver = resolveVisitor.GetResolverStateAfter(node);
Debug.Assert(resolver != null);
return resolver;
} finally {
resolveVisitor.cancellationToken = CancellationToken.None;
lock (resolveVisitor) {
InitResolver();
resolveVisitor.cancellationToken = cancellationToken;
try {
CSharpResolver resolver = resolveVisitor.GetResolverStateAfter(node);
Debug.Assert(resolver != null);
return resolver;
} finally {
resolveVisitor.cancellationToken = CancellationToken.None;
}
}
}
@ -206,12 +215,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -206,12 +215,14 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
if (expr == null || expr.IsNull)
throw new ArgumentNullException("expr");
InitResolver();
resolveVisitor.cancellationToken = cancellationToken;
try {
return resolveVisitor.GetConversionWithTargetType(expr);
} finally {
resolveVisitor.cancellationToken = CancellationToken.None;
lock (resolveVisitor) {
InitResolver();
resolveVisitor.cancellationToken = cancellationToken;
try {
return resolveVisitor.GetConversionWithTargetType(expr);
} finally {
resolveVisitor.cancellationToken = CancellationToken.None;
}
}
}

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

@ -35,6 +35,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -35,6 +35,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary>
/// Contains the main resolver logic.
/// </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
{
static readonly ResolveResult ErrorResult = ErrorResolveResult.UnknownError;

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

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

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

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

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

@ -77,7 +77,15 @@ namespace ICSharpCode.SharpDevelop @@ -77,7 +77,15 @@ namespace ICSharpCode.SharpDevelop
/// </summary>
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>

Loading…
Cancel
Save