diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
index fcbc9bd162..f6d4b3685c 100755
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditorView.cs
@@ -17,17 +17,19 @@ using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
-
using ICSharpCode.AvalonEdit.AddIn.Options;
using ICSharpCode.AvalonEdit.AddIn.Snippets;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Rendering;
+using ICSharpCode.NRefactory;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.AvalonEdit;
using ICSharpCode.SharpDevelop.Editor.Commands;
+using ICSharpCode.SharpDevelop.Refactoring;
+using Ast = ICSharpCode.NRefactory.Ast;
namespace ICSharpCode.AvalonEdit.AddIn
{
@@ -359,9 +361,11 @@ namespace ICSharpCode.AvalonEdit.AddIn
void TextViewMouseDown(object sender, MouseButtonEventArgs e)
{
- // close existing popup immediately on text editor mouse down
+ // close existing debugger popup immediately on text editor mouse down
TryCloseExistingPopup(false);
+
if (options.CtrlClickGoToDefinition && e.ChangedButton == MouseButton.Left && Keyboard.Modifiers == ModifierKeys.Control) {
+ // Ctrl+Click Go to definition
var position = GetPositionFromPoint(e.GetPosition(this));
if (position == null)
return;
@@ -372,6 +376,45 @@ namespace ICSharpCode.AvalonEdit.AddIn
}
#endregion
+ protected override void OnKeyUp(KeyEventArgs e)
+ {
+ base.OnKeyUp(e);
+ if (e.Handled) return;
+ if (e.Key == Key.W && Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) {
+ // Select AST Node
+ var editorLang = EditorContext.GetEditorLanguage(this.Adapter); if (editorLang == null) return;
+ var parser = ParserFactory.CreateParser(editorLang.Value, new StringReader(this.Text));
+ parser.ParseMethodBodies = true;
+ parser.Parse();
+ var parsedCU = parser.CompilationUnit; if (parsedCU == null) return;
+ //var caretLocation = new Location(this.Adapter.Caret.Column, this.Adapter.Caret.Line);
+ var selectionStart = this.Adapter.Document.OffsetToPosition(this.SelectionStart);
+ var selectionEnd = this.Adapter.Document.OffsetToPosition(this.SelectionStart + this.SelectionLength);
+ foreach (var node in parsedCU.Children) {
+ // fix StartLocation / EndLocation
+ node.AcceptVisitor(new ICSharpCode.NRefactory.Visitors.SetRegionInclusionVisitor(), null);
+ }
+ Ast.INode currentNode = parsedCU.Children.Select(
+ n => EditorContext.FindInnermostNodeContainingSelection(n, selectionStart, selectionEnd)).Where(n => n != null).FirstOrDefault();
+ if (currentNode == null) return;
+
+ if (currentNode.StartLocation == selectionStart && currentNode.EndLocation == selectionEnd) {
+ // if whole node already selected, expand selection to parent
+ currentNode = currentNode.Parent;
+ if (currentNode == null)
+ return;
+ }
+ int startOffset, endOffset;
+ try {
+ startOffset = this.Adapter.Document.PositionToOffset(currentNode.StartLocation.Line, currentNode.StartLocation.Column);
+ endOffset = this.Adapter.Document.PositionToOffset(currentNode.EndLocation.Line, currentNode.EndLocation.Column);
+ } catch(ArgumentOutOfRangeException) {
+ return;
+ }
+ this.Select(startOffset, endOffset - startOffset);
+ }
+ }
+
public void JumpTo(int line, int column)
{
// closes Debugger popup on debugger step
diff --git a/src/Libraries/NRefactory/Project/NRefactory.csproj b/src/Libraries/NRefactory/Project/NRefactory.csproj
index 9e9585c33b..50ec66ad40 100644
--- a/src/Libraries/NRefactory/Project/NRefactory.csproj
+++ b/src/Libraries/NRefactory/Project/NRefactory.csproj
@@ -128,6 +128,7 @@
+
diff --git a/src/Libraries/NRefactory/Project/Src/Visitors/SetRegionInclusionVisitor.cs b/src/Libraries/NRefactory/Project/Src/Visitors/SetRegionInclusionVisitor.cs
new file mode 100644
index 0000000000..9721264467
--- /dev/null
+++ b/src/Libraries/NRefactory/Project/Src/Visitors/SetRegionInclusionVisitor.cs
@@ -0,0 +1,66 @@
+//
+//
+//
+//
+// $Revision: $
+//
+using System;
+using System.Collections.Generic;
+using ICSharpCode.NRefactory.Ast;
+
+namespace ICSharpCode.NRefactory.Visitors
+{
+ ///
+ /// Sets StartLocation and EndLocation (region) of every node to union of regions of its children.
+ /// Parsers don't do this by default,
+ /// e.g. "a.Foo()" is InvocationExpression, its region includes only the "()" and it has a child MemberReferenceExpression, with region ".Foo".
+ ///
+ public class SetRegionInclusionVisitor : NodeTrackingAstVisitor
+ {
+ Stack parentNodes = new Stack();
+
+ public SetRegionInclusionVisitor()
+ {
+ parentNodes.Push(null);
+ }
+
+ protected override void BeginVisit(INode node)
+ {
+ base.BeginVisit(node);
+
+ // Only push nodes on the stack which have valid position information.
+ if (node != null &&
+ node.StartLocation.X >= 1 && node.StartLocation.Y >= 1 &&
+ node.EndLocation.X >= 1 && node.EndLocation.Y >= 1) {
+
+ if (node is PropertyDeclaration) {
+ // PropertyDeclaration has correctly set BodyStart and BodyEnd by the parser,
+ // but it has no subnode "body", just 2 children GetRegion and SetRegion which don't cover
+ // the whole (BodyStart, BodyEnd) region => We have to handle PropertyDeclaration as a special case.
+ node.EndLocation = ((PropertyDeclaration)node).BodyEnd;
+ }
+
+ this.parentNodes.Push(node);
+ }
+ }
+
+ protected override void EndVisit(INode node)
+ {
+ base.EndVisit(node);
+
+ // Only remove those nodes which have actually been pushed before.
+ if (this.parentNodes.Count > 0 && INode.ReferenceEquals(this.parentNodes.Peek(), node)) {
+ // remove this node
+ this.parentNodes.Pop();
+ // fix region of parent
+ var parent = this.parentNodes.Peek();
+ if (parent == null)
+ return;
+ if (node.StartLocation < parent.StartLocation)
+ parent.StartLocation = node.StartLocation;
+ if (node.EndLocation > parent.EndLocation)
+ parent.EndLocation = node.EndLocation;
+ }
+ }
+ }
+}
diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/EditorContext.cs b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/EditorContext.cs
index 6583227a12..3e96406139 100644
--- a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/EditorContext.cs
+++ b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/EditorContext.cs
@@ -62,7 +62,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
if (CaretColumn > 1 && editor.Document.GetText(editor.Document.PositionToOffset(CaretLine, CaretColumn - 1), 1) == ";") {
// If caret is just after ';', pretend that caret is before ';'
// (works well e.g. for this.Foo();(*caret*) - we want to get "this.Foo()")
- // This is equivalent to pretending that ; don't exist, and actually is not such a bad idea.
+ // This is equivalent to pretending that ; don't exist, and actually it's not such a bad idea.
CaretColumn -= 1;
}
@@ -76,7 +76,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
this.CurrentElement = FindInnermostNodeAtLocation(this.CurrentMemberAST, new Location(CaretColumn, CaretLine));
- //DebugLog();
+// DebugLog();
}
void DebugLog()
@@ -131,41 +131,56 @@ namespace ICSharpCode.SharpDevelop.Refactoring
if (memberDecl == null)
return null;
if (memberDecl is MethodDeclaration) {
- return FindInnermostNodeInBlock(((MethodDeclaration)memberDecl).Body, position);
+ return FindInnermostNode(((MethodDeclaration)memberDecl).Body, position);
} else if (memberDecl is PropertyDeclaration) {
var propertyDecl = (PropertyDeclaration)memberDecl;
if (propertyDecl.HasGetRegion && position >= propertyDecl.GetRegion.StartLocation && position <= propertyDecl.GetRegion.EndLocation) {
- return FindInnermostNodeInBlock(propertyDecl.GetRegion.Block, position);
+ return FindInnermostNode(propertyDecl.GetRegion.Block, position);
}
if (propertyDecl.HasSetRegion && position >= propertyDecl.SetRegion.StartLocation && position <= propertyDecl.SetRegion.EndLocation) {
- return FindInnermostNodeInBlock(propertyDecl.SetRegion.Block, position);
+ return FindInnermostNode(propertyDecl.SetRegion.Block, position);
}
}
return null;
}
- INode FindInnermostNodeInBlock(BlockStatement node, Location position)
+ public static INode FindInnermostNode(INode node, Location position)
{
if (node == null)
return null;
- var findInnermostVisitor = new FindInnermostNodeVisitor(position);
+ var findInnermostVisitor = new FindInnermostNodeByRangeVisitor(position);
node.AcceptVisitor(findInnermostVisitor, null);
return findInnermostVisitor.InnermostNode;
}
- class FindInnermostNodeVisitor : NodeTrackingAstVisitor
+ public static INode FindInnermostNodeContainingSelection(INode node, Location start, Location end)
{
- public Location CaretLocation { get; private set; }
+ if (node == null)
+ return null;
+ var findInnermostVisitor = new FindInnermostNodeByRangeVisitor(start, end);
+ node.AcceptVisitor(findInnermostVisitor, null);
+ return findInnermostVisitor.InnermostNode;
+ }
+
+ class FindInnermostNodeByRangeVisitor : NodeTrackingAstVisitor
+ {
+ public Location RangeStart { get; private set; }
+ public Location RangeEnd { get; private set; }
public INode InnermostNode { get; private set; }
- public FindInnermostNodeVisitor(Location caretLocation)
+ public FindInnermostNodeByRangeVisitor(Location caretPosition) : this(caretPosition, caretPosition)
+ {
+ }
+
+ public FindInnermostNodeByRangeVisitor(Location selectionStart, Location selectionEnd)
{
- this.CaretLocation = caretLocation;
+ this.RangeStart = selectionStart;
+ this.RangeEnd = selectionEnd;
}
protected override void BeginVisit(INode node)
{
- if (node.StartLocation <= CaretLocation && node.EndLocation >= CaretLocation) {
+ if (node.StartLocation <= RangeStart && node.EndLocation >= RangeEnd) {
// the node visited last will be the innermost
this.InnermostNode = node;
}
@@ -218,7 +233,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
return null;
}
- SupportedLanguage? GetEditorLanguage(ITextEditor editor)
+ public static SupportedLanguage? GetEditorLanguage(ITextEditor editor)
{
if (editor == null || editor.Language == null)
return null;
@@ -233,8 +248,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
INode GetCurrentMemberAST(ITextEditor editor)
{
try {
- var resolver = GetNRefactoryResolver(editor);
- resolver.Initialize(ParserService.GetParseInformation(editor.FileName), CaretLine, CaretColumn);
+ var resolver = GetInitializedNRefactoryResolver(editor, this.CaretLine, this.CaretColumn);
return resolver.ParseCurrentMember(editor.Document.Text);
}
catch {
@@ -242,13 +256,15 @@ namespace ICSharpCode.SharpDevelop.Refactoring
}
}
- NRefactoryResolver GetNRefactoryResolver(ITextEditor editor)
+ NRefactoryResolver GetInitializedNRefactoryResolver(ITextEditor editor, int caretLine, int caretColumn)
{
if (editor == null || editor.Language == null)
return null;
try
{
- return new NRefactoryResolver(editor.Language.Properties);
+ var resolver = new NRefactoryResolver(editor.Language.Properties);
+ resolver.Initialize(ParserService.GetParseInformation(editor.FileName), caretLine, caretColumn);
+ return resolver;
}
catch(NotSupportedException)
{