Browse Source

Rewrote EditorContext so that it will be more usable for implementing Context actions. It now provides AST Node at caret and its parents.

Next will be to move Context actions to background thread.

Found 2 bugs in IntroduceMethod (overloads and type arguments are ignored), will fix them.

Fixed NullReferenceException in NRefactoryInsightWindowHandler.HighlightParameter (when typing "," after last method parameter).

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@6368 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
pull/1/head
Martin Koníček 15 years ago
parent
commit
d724328441
  1. 3
      src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj
  2. 8
      src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/AddUsing.cs
  3. 0
      src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/FindTypeDeclarationsVisitor.cs
  4. 18
      src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/GenerateMember.cs
  5. 12
      src/AddIns/Misc/SharpRefactoring/Project/Src/Extensions.cs
  6. 86
      src/AddIns/Misc/SharpRefactoring/Project/Src/GenerateCode.cs
  7. 49
      src/AddIns/Misc/SharpRefactoring/Project/Src/MenuItemFactories/IntroduceMethod.cs
  8. 2
      src/Main/Base/Project/Src/Editor/CodeCompletion/NRefactoryInsightWindowHandler.cs
  9. 20
      src/Main/Base/Project/Src/Services/ParserService/ParserService.cs
  10. 2
      src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsService.cs
  11. 247
      src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/EditorContext.cs
  12. 44
      src/Main/Base/Project/Src/Util/ExtensionMethods.cs
  13. 2
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ICompilationUnit.cs
  14. 2
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs

3
src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj

@ -73,7 +73,7 @@
<Compile Include="Src\ContextActions\AddUsing.cs" /> <Compile Include="Src\ContextActions\AddUsing.cs" />
<Compile Include="Src\ContextActions\DelegateAction.cs" /> <Compile Include="Src\ContextActions\DelegateAction.cs" />
<Compile Include="Src\ContextActions\Extensions.cs" /> <Compile Include="Src\ContextActions\Extensions.cs" />
<Compile Include="Src\ContextActions\FindNodesVisitor.cs" /> <Compile Include="Src\ContextActions\FindTypeDeclarationsVisitor.cs" />
<Compile Include="Src\ContextActions\GenerateMember.cs" /> <Compile Include="Src\ContextActions\GenerateMember.cs" />
<Compile Include="Src\ContextActions\ImplementAbstractClass.cs" /> <Compile Include="Src\ContextActions\ImplementAbstractClass.cs" />
<Compile Include="Src\ContextActions\ImplementInterface.cs" /> <Compile Include="Src\ContextActions\ImplementInterface.cs" />
@ -108,7 +108,6 @@
</Compile> </Compile>
<Compile Include="Src\Gui\Wrapper.cs" /> <Compile Include="Src\Gui\Wrapper.cs" />
<Compile Include="Src\InsertCtorSnippetRefactoring.cs" /> <Compile Include="Src\InsertCtorSnippetRefactoring.cs" />
<Compile Include="Src\MenuItemFactories\IntroduceMethod.cs" />
<Compile Include="Src\MenuItemFactories\ParameterCheckRefactoringMenuBuilder.cs" /> <Compile Include="Src\MenuItemFactories\ParameterCheckRefactoringMenuBuilder.cs" />
<Compile Include="Src\MenuItemFactories\ResolveAttribute.cs" /> <Compile Include="Src\MenuItemFactories\ResolveAttribute.cs" />
<Compile Include="Src\MenuItemFactories\ResolveExtensionMethod.cs" /> <Compile Include="Src\MenuItemFactories\ResolveExtensionMethod.cs" />

8
src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/AddUsing.cs

@ -18,13 +18,13 @@ namespace SharpRefactoring.ContextActions
/// </summary> /// </summary>
public class AddUsingProvider : IContextActionsProvider public class AddUsingProvider : IContextActionsProvider
{ {
public IEnumerable<IContextAction> GetAvailableActions(EditorContext editorAST) public IEnumerable<IContextAction> GetAvailableActions(EditorContext context)
{ {
var currentLineAST = editorAST.CurrentLineAST; var currentLineAST = context.CurrentLineAST;
if (currentLineAST == null) if (currentLineAST == null)
yield break; yield break;
var symbol = editorAST.SymbolUnderCaret; var symbol = context.CurrentSymbol;
foreach (var contextAction in GetAddUsingContextActions(symbol, editorAST.Editor)) { foreach (var contextAction in GetAddUsingContextActions(symbol, context.Editor)) {
yield return contextAction; yield return contextAction;
} }
} }

0
src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/FindNodesVisitor.cs → src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/FindTypeDeclarationsVisitor.cs

18
src/AddIns/Misc/SharpRefactoring/Project/Src/ContextActions/GenerateMember.cs

@ -6,8 +6,9 @@
// </file> // </file>
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Windows;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.AvalonEdit;
using ICSharpCode.SharpDevelop.Refactoring; using ICSharpCode.SharpDevelop.Refactoring;
namespace SharpRefactoring.ContextActions namespace SharpRefactoring.ContextActions
@ -19,7 +20,20 @@ namespace SharpRefactoring.ContextActions
{ {
public IEnumerable<IContextAction> GetAvailableActions(EditorContext editorContext) public IEnumerable<IContextAction> GetAvailableActions(EditorContext editorContext)
{ {
var generateCodeAction = GenerateCode.GetContextAction(editorContext.SymbolUnderCaret, editorContext.Editor); if (string.IsNullOrEmpty(editorContext.CurrentExpression.Expression)) {
yield break;
}
if (editorContext.CurrentExpression.Region != null &&
editorContext.CurrentExpression.Region.EndLine > editorContext.CurrentExpression.Region.BeginLine) {
// do not yield the action for 2-line expressions like this, which are actually 2 different expressions
// variable.(*caret*)
// CallFooMethod();
// this check is not correct for this case because it does not yield the action when it should:
// variable.Foo((*caret*)
// 123);
yield break;
}
var generateCodeAction = GenerateCode.GetContextAction(editorContext.CurrentSymbol, editorContext);
if (generateCodeAction != null) if (generateCodeAction != null)
yield return generateCodeAction; yield return generateCodeAction;
} }

12
src/AddIns/Misc/SharpRefactoring/Project/Src/Extensions.cs

@ -47,5 +47,17 @@ namespace SharpRefactoring
resolver.Initialize(ParserService.GetParseInformation(context.FileName), context.Caret.Line, context.Caret.Column); resolver.Initialize(ParserService.GetParseInformation(context.FileName), context.Caret.Line, context.Caret.Column);
return resolver; return resolver;
} }
public static bool IsUserCode(this IReturnType rt)
{
if (rt == null)
return false;
return IsUserCode(rt.GetUnderlyingClass());
}
public static bool IsUserCode(this IClass c)
{
return (c != null && !c.BodyRegion.IsEmpty);
}
} }
} }

86
src/AddIns/Misc/SharpRefactoring/Project/Src/GenerateCode.cs

@ -30,12 +30,11 @@ namespace SharpRefactoring
/// <summary> /// <summary>
/// If given symbol is Unknown ResolveResult, returns action that can generate code for this missing symbol. /// If given symbol is Unknown ResolveResult, returns action that can generate code for this missing symbol.
/// </summary> /// </summary>
public static GenerateCodeContextAction GetContextAction(ResolveResult symbol, ITextEditor editor) public static GenerateCodeContextAction GetContextAction(ResolveResult symbol, EditorContext context)
{ {
if (symbol is UnknownMethodResolveResult) { if (symbol is UnknownMethodResolveResult) {
UnknownMethodResolveResult unknownMethodCall = (UnknownMethodResolveResult)symbol;
UnknownMethodResolveResult unknownMethodCall = symbol as UnknownMethodResolveResult; Ast.Expression expression = context.GetContainingElement<Ast.InvocationExpression>();
Ast.Expression expression = GetExpressionInContext(unknownMethodCall, editor);
if (expression == null) if (expression == null)
return null; return null;
@ -45,58 +44,15 @@ namespace SharpRefactoring
} catch (FormatException) { } catch (FormatException) {
} }
return new IntroduceMethodContextAction(unknownMethodCall, expression, editor) { if (unknownMethodCall.Target.IsUserCode()) {
Title = title // Don't introduce method on non-modyfiable types
}; return new IntroduceMethodContextAction(unknownMethodCall, expression, context.Editor) {
Title = title
};
}
} }
return null; return null;
} }
internal static Ast.Expression GetExpressionInContext(UnknownMethodResolveResult rr, ITextEditor editor)
{
if (rr.Target == null || rr.Target.GetUnderlyingClass() == null)
return null;
NRefactoryResolver resolver = Extensions.CreateResolverForContext(rr.CallingClass.ProjectContent.Language, editor);
Ast.INode node = resolver.ParseCurrentMember(editor.Document.Text);
if (node == null)
return null;
resolver.RunLookupTableVisitor(node);
InvocationExpressionLookupVisitor visitor = new InvocationExpressionLookupVisitor(editor);
node.AcceptVisitor(visitor, null);
return visitor.Expression;
}
internal class InvocationExpressionLookupVisitor : AbstractAstVisitor
{
ITextEditor editor;
Ast.InvocationExpression expression;
public Ast.InvocationExpression Expression {
get { return expression; }
}
public InvocationExpressionLookupVisitor(ITextEditor editor)
{
this.editor = editor;
this.expression = null;
}
public override object VisitInvocationExpression(Ast.InvocationExpression invocationExpression, object data)
{
int startOffset = editor.Document.PositionToOffset(invocationExpression.TargetObject.StartLocation.Line, invocationExpression.TargetObject.StartLocation.Column);
int endOffset = editor.Document.PositionToOffset(invocationExpression.EndLocation.Line, invocationExpression.EndLocation.Column);
int offset = editor.Caret.Offset;
if (offset >= startOffset && offset <= endOffset)
expression = invocationExpression;
return base.VisitInvocationExpression(invocationExpression, data);
}
}
} }
public abstract class GenerateCodeContextAction : IContextAction public abstract class GenerateCodeContextAction : IContextAction
@ -110,25 +66,27 @@ namespace SharpRefactoring
public class IntroduceMethodContextAction : GenerateCodeContextAction public class IntroduceMethodContextAction : GenerateCodeContextAction
{ {
public UnknownMethodResolveResult UnknownMethodCall { get; private set; } public UnknownMethodResolveResult UnknownMethodCall { get; private set; }
public Ast.Expression Expression { get; private set; } public Ast.Expression InvocationExpr { get; private set; }
public ITextEditor Editor { get; private set; } public ITextEditor Editor { get; private set; }
public IntroduceMethodContextAction(UnknownMethodResolveResult symbol, Ast.Expression expression, ITextEditor editor) public IntroduceMethodContextAction(UnknownMethodResolveResult symbol, Ast.Expression invocationExpr, ITextEditor editor)
{ {
if (symbol == null) if (symbol == null)
throw new ArgumentNullException("rr"); throw new ArgumentNullException("rr");
if (expression == null) if (invocationExpr == null)
throw new ArgumentNullException("ex"); throw new ArgumentNullException("ex");
if (editor == null) if (editor == null)
throw new ArgumentNullException("editor"); throw new ArgumentNullException("editor");
this.UnknownMethodCall = symbol; this.UnknownMethodCall = symbol;
this.Expression = expression; this.InvocationExpr = invocationExpr;
this.Editor = editor; this.Editor = editor;
} }
public override void Execute() public override void Execute()
{ {
IClass targetClass = UnknownMethodCall.Target.GetUnderlyingClass(); IClass targetClass = UnknownMethodCall.Target.GetUnderlyingClass();
if (targetClass == null)
return;
bool isNew = false; bool isNew = false;
object result = null; object result = null;
@ -141,7 +99,7 @@ namespace SharpRefactoring
?? targetClass; ?? targetClass;
} }
if (targetClass.BodyRegion.IsEmpty) { if (!targetClass.IsUserCode()) {
IntroduceMethodDialog dialog = new IntroduceMethodDialog(UnknownMethodCall.CallingClass); IntroduceMethodDialog dialog = new IntroduceMethodDialog(UnknownMethodCall.CallingClass);
dialog.Owner = WorkbenchSingleton.MainWindow; dialog.Owner = WorkbenchSingleton.MainWindow;
@ -152,10 +110,10 @@ namespace SharpRefactoring
result = dialog.Result; result = dialog.Result;
} }
ExecuteIntroduceMethod(UnknownMethodCall, Expression, Editor, isNew, result); ExecuteIntroduceMethod(UnknownMethodCall, InvocationExpr, Editor, isNew, result);
} }
internal void ExecuteIntroduceMethod(UnknownMethodResolveResult rr, Ast.Expression expression, ITextEditor editor, bool isNew, object result) internal void ExecuteIntroduceMethod(UnknownMethodResolveResult rr, Ast.Expression invocationExpr, ITextEditor editor, bool isNew, object result)
{ {
IClass targetClass = IsEqualClass(rr.CallingClass, rr.Target.GetUnderlyingClass()) ? rr.CallingClass IClass targetClass = IsEqualClass(rr.CallingClass, rr.Target.GetUnderlyingClass()) ? rr.CallingClass
: rr.Target.GetUnderlyingClass(); : rr.Target.GetUnderlyingClass();
@ -167,7 +125,7 @@ namespace SharpRefactoring
ModifierEnum modifiers = ModifierEnum.None; ModifierEnum modifiers = ModifierEnum.None;
bool isExtension = targetClass.BodyRegion.IsEmpty; bool isExtension = !targetClass.IsUserCode();
if (IsEqualClass(rr.CallingClass, targetClass)) { if (IsEqualClass(rr.CallingClass, targetClass)) {
if (rr.CallingMember != null) if (rr.CallingMember != null)
@ -190,11 +148,11 @@ namespace SharpRefactoring
NRefactoryResolver resolver = Extensions.CreateResolverForContext(targetClass.ProjectContent.Language, editor); NRefactoryResolver resolver = Extensions.CreateResolverForContext(targetClass.ProjectContent.Language, editor);
IReturnType type = resolver.GetExpectedTypeFromContext(expression); IReturnType type = resolver.GetExpectedTypeFromContext(invocationExpr);
Ast.TypeReference typeRef = CodeGenerator.ConvertType(type, finder); Ast.TypeReference typeRef = CodeGenerator.ConvertType(type, finder);
if (typeRef.IsNull) { if (typeRef.IsNull) {
if (expression.Parent is Ast.ExpressionStatement) if (invocationExpr.Parent is Ast.ExpressionStatement)
typeRef = new Ast.TypeReference("void", true); typeRef = new Ast.TypeReference("void", true);
else else
typeRef = new Ast.TypeReference("object", true); typeRef = new Ast.TypeReference("object", true);
@ -204,7 +162,7 @@ namespace SharpRefactoring
Name = rr.CallName, Name = rr.CallName,
Modifier = CodeGenerator.ConvertModifier(modifiers, finder), Modifier = CodeGenerator.ConvertModifier(modifiers, finder),
TypeReference = typeRef, TypeReference = typeRef,
Parameters = CreateParameters(rr, finder, expression as Ast.InvocationExpression).ToList(), Parameters = CreateParameters(rr, finder, invocationExpr as Ast.InvocationExpression).ToList(),
}; };
if (targetClass.ClassType != ClassType.Interface) if (targetClass.ClassType != ClassType.Interface)

49
src/AddIns/Misc/SharpRefactoring/Project/Src/MenuItemFactories/IntroduceMethod.cs

@ -1,49 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Siegfried Pammer" email="siegfriedpammer@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using ICSharpCode.Core;
using ICSharpCode.NRefactory.Visitors;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver;
using ICSharpCode.SharpDevelop.Dom.Refactoring;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Refactoring;
using SharpRefactoring.Gui;
using Ast = ICSharpCode.NRefactory.Ast;
namespace SharpRefactoring
{
/// <summary>
/// Provides <see cref="GenerateCode.GetContextAction"></see> as editor context menu entry.
/// </summary>
public class IntroduceMethod : IRefactoringMenuItemFactory
{
public MenuItem Create(RefactoringMenuContext context)
{
if (context.ExpressionResult.Context == ExpressionContext.Attribute)
return null;
var introduceCodeAction = GenerateCode.GetContextAction(context.ResolveResult, context.Editor);
if (introduceCodeAction == null)
return null;
var item = new MenuItem() {
Header = introduceCodeAction.Title,
Icon = ClassBrowserIconService.GotoArrow.CreateImage()
};
item.Click += delegate { introduceCodeAction.Execute(); };
return item;
}
}
}

2
src/Main/Base/Project/Src/Editor/CodeCompletion/NRefactoryInsightWindowHandler.cs

@ -282,6 +282,8 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion
public void HighlightParameter(IInsightWindow window, int index) public void HighlightParameter(IInsightWindow window, int index)
{ {
if (window == null)
return;
var item = window.SelectedItem as MethodInsightItem; var item = window.SelectedItem as MethodInsightItem;
if (item != null) if (item != null)

20
src/Main/Base/Project/Src/Services/ParserService/ParserService.cs

@ -263,19 +263,23 @@ namespace ICSharpCode.SharpDevelop
/// </summary> /// </summary>
public static ResolveResult Resolve(int caretLine, int caretColumn, IDocument document, string fileName) public static ResolveResult Resolve(int caretLine, int caretColumn, IDocument document, string fileName)
{ {
IExpressionFinder expressionFinder = GetExpressionFinder(fileName); var expressionResult = FindFullExpression(caretLine, caretColumn, document, fileName);
if (expressionFinder == null)
return null;
if (caretColumn > document.GetLine(caretLine).Length)
return null;
string documentText = document.Text;
var expressionResult = expressionFinder.FindFullExpression(documentText, document.PositionToOffset(caretLine, caretColumn));
string expression = (expressionResult.Expression ?? "").Trim(); string expression = (expressionResult.Expression ?? "").Trim();
if (expression.Length > 0) { if (expression.Length > 0) {
return Resolve(expressionResult, caretLine, caretColumn, fileName, documentText); return Resolve(expressionResult, caretLine, caretColumn, fileName, document.Text);
} else } else
return null; return null;
} }
public static ExpressionResult FindFullExpression(int caretLine, int caretColumn, IDocument document, string fileName)
{
IExpressionFinder expressionFinder = GetExpressionFinder(fileName);
if (expressionFinder == null)
return ExpressionResult.Empty;
if (caretColumn > document.GetLine(caretLine).Length)
return ExpressionResult.Empty;
return expressionFinder.FindFullExpression(document.Text, document.PositionToOffset(caretLine, caretColumn));
}
public static ResolveResult Resolve(int offset, IDocument document, string fileName) public static ResolveResult Resolve(int offset, IDocument document, string fileName)
{ {

2
src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsService.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
} }
/// <summary> /// <summary>
/// Gets actions available for current line of the editor. /// Gets actions available for current caret position in the editor.
/// </summary> /// </summary>
public IEnumerable<IContextAction> GetAvailableActions(ITextEditor editor) public IEnumerable<IContextAction> GetAvailableActions(ITextEditor editor)
{ {

247
src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/EditorContext.cs

@ -5,8 +5,11 @@
// <version>$Revision: $</version> // <version>$Revision: $</version>
// </file> // </file>
using System; using System;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory; using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast; using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors;
using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver; using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver;
using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor;
@ -21,7 +24,32 @@ namespace ICSharpCode.SharpDevelop.Refactoring
public class EditorContext public class EditorContext
{ {
public ITextEditor Editor { get; private set; } public ITextEditor Editor { get; private set; }
SnippetParser SnippetParser { get; set; } int CaretLine { get; set; }
int CaretColumn { get; set; }
/// <summary>
/// Language independent.
/// </summary>
public ExpressionResult CurrentExpression { get; private set; }
/// <summary>
/// Language independent.
/// </summary>
public ResolveResult CurrentSymbol { get; private set; }
public IDocumentLine CurrentLine { get; private set; }
/// <summary>
/// Only available for C# and VB.
/// </summary>
public INode CurrentLineAST { get; private set; }
/// <summary>
/// Only available for C# and VB.
/// </summary>
public INode CurrentMemberAST { get; private set; }
/// <summary>
/// Only available for C# and VB.
/// </summary>
public INode CurrentElement { get; private set; }
NRefactoryResolver Resolver { get; set; } NRefactoryResolver Resolver { get; set; }
public EditorContext(ITextEditor editor) public EditorContext(ITextEditor editor)
@ -29,81 +57,155 @@ namespace ICSharpCode.SharpDevelop.Refactoring
if (editor == null) if (editor == null)
throw new ArgumentNullException("editor"); throw new ArgumentNullException("editor");
this.Editor = editor; this.Editor = editor;
this.SnippetParser = GetSnippetParser(editor); this.CaretLine = editor.Caret.Line;
this.Resolver = GetResolver(editor); this.CaretColumn = editor.Caret.Column;
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.
CaretColumn -= 1;
}
Stopwatch sw = new Stopwatch();
sw.Start();
this.CurrentExpression = GetExpressionAtCaret(editor);
this.CurrentSymbol = ResolveExpression(editor);
long elapsedResolveMs = sw.ElapsedMilliseconds;
this.CurrentLine = editor.Document.GetLine(CaretLine);
this.CurrentLineAST = GetCurrentLineAst(this.CurrentLine, editor);
this.CurrentMemberAST = GetCurrentMemberAST(editor);
this.CurrentElement = FindInnermostNodeAtLocation(this.CurrentMemberAST, new Location(CaretColumn, CaretLine));
// ICSharpCode.Core.LoggingService.Debug(string.Format(
// @"
// Context actions (elapsed {5} ms ({6} ms in Resolver)):
// ExprAtCaret: {0}
// ----------------------
// SymbolAtCaret: {1}
// ----------------------
// CurrentLineAST: {2}
// ----------------------
// AstNodeAtCaret: {3}
// ----------------------
// CurrentMemberAST: {4}
// ----------------------",
// CurrentExpression, CurrentSymbol, CurrentLineAST, CurrentElement, CurrentMemberAST == null ? "" : CurrentMemberAST.ToString().TakeStartEllipsis(100),
// sw.ElapsedMilliseconds, elapsedResolveMs));
} }
// TODO make all reference types cached ResolveResult? - implement own Nullable<T> public TNode GetCurrentElement<TNode>() where TNode : class, INode
ResolveResult symbolUnderCaret;
public ResolveResult SymbolUnderCaret
{ {
get if (this.CurrentElement is TNode)
{ return (TNode)this.CurrentElement;
if (symbolUnderCaret != null) return null;
return symbolUnderCaret;
// workaround so that Resolve works when the caret is placed also at the end of the word
symbolUnderCaret = ParserService.Resolve(Editor.Caret.Line, Editor.Caret.Column > 1 ? Editor.Caret.Column - 1 : 1, Editor.Document, Editor.FileName);
if (symbolUnderCaret == null)
symbolUnderCaret = ParserService.Resolve(Editor.Caret.Line, Editor.Caret.Column, Editor.Document, Editor.FileName);
return symbolUnderCaret;
}
} }
IDocumentLine currentLine; public TNode GetContainingElement<TNode>() where TNode : class, INode
public IDocumentLine CurrentLine
{ {
get var node = this.CurrentElement;
while(node != null)
{ {
if (currentLine != null) if (node is TNode)
return currentLine; return (TNode)node;
try node = node.Parent;
{
return (currentLine = Editor.Document.GetLine(Editor.Caret.Line));
}
catch
{
return null;
}
} }
return null;
} }
INode currentLineAST; // public TNode GetInnerElement<TNode>() where TNode : class, INode
public INode CurrentLineAST // {
// var findChildVisitor = new FindOutermostNodeVisitor<TNode>();
// this.CurrentElement.AcceptVisitor(findChildVisitor, null);
// return findChildVisitor.FoundNode;
// }
INode FindInnermostNodeAtLocation(INode memberDecl, Location position)
{ {
get if (memberDecl == null)
{ return null;
if (currentLineAST != null) if (memberDecl is MethodDeclaration) {
return currentLineAST; return FindInnermostNodeInBlock(((MethodDeclaration)memberDecl).Body, position);
if (this.SnippetParser == null || this.CurrentLine == null) } else if (memberDecl is PropertyDeclaration) {
return null; var propertyDecl = (PropertyDeclaration)memberDecl;
try { if (propertyDecl.HasGetRegion && position >= propertyDecl.GetRegion.StartLocation && position <= propertyDecl.GetRegion.EndLocation) {
return (currentLineAST = SnippetParser.Parse(this.CurrentLine.Text)); return FindInnermostNodeInBlock(propertyDecl.GetRegion.Block, position);
} }
catch { if (propertyDecl.HasSetRegion && position >= propertyDecl.SetRegion.StartLocation && position <= propertyDecl.SetRegion.EndLocation) {
return null; return FindInnermostNodeInBlock(propertyDecl.SetRegion.Block, position);
} }
} }
return null;
} }
INode currentMemberAST; INode FindInnermostNodeInBlock(BlockStatement node, Location position)
public INode CurrentMemberAST
{ {
get if (node == null)
return null;
var findInnermostVisitor = new FindInnermostNodeVisitor(position);
node.AcceptVisitor(findInnermostVisitor, null);
return findInnermostVisitor.InnermostNode;
}
class FindInnermostNodeVisitor : NodeTrackingAstVisitor
{
public Location CaretLocation { get; private set; }
public INode InnermostNode { get; private set; }
public FindInnermostNodeVisitor(Location caretLocation)
{ {
if (Resolver == null) this.CaretLocation = caretLocation;
return null; }
if (currentMemberAST != null)
return currentMemberAST; protected override void BeginVisit(INode node)
try { {
Resolver.Initialize(ParserService.GetParseInformation(Editor.FileName), Editor.Caret.Line, Editor.Caret.Column); if (node.StartLocation <= CaretLocation && node.EndLocation >= CaretLocation) {
return (currentMemberAST = Resolver.ParseCurrentMember(Editor.Document.Text)); // the node visited last will be the innermost
} this.InnermostNode = node;
catch {
return null;
} }
base.BeginVisit(node);
} }
} }
// class FindOutermostNodeVisitor<TNode> : NodeTrackingAstVisitor where TNode : class, INode
// {
// public TNode FoundNode { get; private set; }
//
// protected override void BeginVisit(INode node)
// {
// if (node is TNode && FoundNode == null) {
// FoundNode = (TNode)node;
// }
// base.BeginVisit(node);
// }
// }
ResolveResult ResolveExpression(ITextEditor editor)
{
return ParserService.Resolve(this.CurrentExpression, CaretLine, CaretColumn, editor.FileName, editor.Document.Text);
}
ExpressionResult GetExpressionAtCaret(ITextEditor editor)
{
ExpressionResult expr = ParserService.FindFullExpression(CaretLine, CaretColumn, editor.Document, editor.FileName);
// if no expression, look one character back (works better with method calls - Foo()(*caret*))
if (string.IsNullOrWhiteSpace(expr.Expression) && CaretColumn > 1)
expr = ParserService.FindFullExpression(CaretLine, CaretColumn - 1, editor.Document, editor.FileName);
return expr;
}
INode GetCurrentLineAst(IDocumentLine currentLine, ITextEditor editor)
{
if (currentLine == null)
return null;
var snippetParser = GetSnippetParser(editor);
return snippetParser.Parse(currentLine.Text);
}
SnippetParser GetSnippetParser(ITextEditor editor) SnippetParser GetSnippetParser(ITextEditor editor)
{ {
var lang = GetEditorLanguage(editor); var lang = GetEditorLanguage(editor);
@ -113,29 +215,42 @@ namespace ICSharpCode.SharpDevelop.Refactoring
return null; return null;
} }
NRefactoryResolver GetResolver(ITextEditor editor) SupportedLanguage? GetEditorLanguage(ITextEditor editor)
{ {
if (editor == null || editor.Language == null) if (editor == null || editor.Language == null)
return null; return null;
try if (editor.Language.Properties == LanguageProperties.CSharp)
{ return SupportedLanguage.CSharp;
return new NRefactoryResolver(editor.Language.Properties); if (editor.Language.Properties == LanguageProperties.VBNet)
return SupportedLanguage.VBNet;
return null;
}
INode GetCurrentMemberAST(ITextEditor editor)
{
try {
var resolver = GetNRefactoryResolver(editor);
resolver.Initialize(ParserService.GetParseInformation(editor.FileName), CaretLine, CaretColumn);
return resolver.ParseCurrentMember(editor.Document.Text);
} }
catch(NotSupportedException) catch {
{
return null; return null;
} }
} }
SupportedLanguage? GetEditorLanguage(ITextEditor editor) NRefactoryResolver GetNRefactoryResolver(ITextEditor editor)
{ {
if (editor == null || editor.Language == null) if (editor == null || editor.Language == null)
return null; return null;
if (editor.Language.Properties == LanguageProperties.CSharp) try
return SupportedLanguage.CSharp; {
if (editor.Language.Properties == LanguageProperties.VBNet) return new NRefactoryResolver(editor.Language.Properties);
return SupportedLanguage.VBNet; }
return null; catch(NotSupportedException)
{
return null;
}
} }
} }
} }

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

@ -18,6 +18,8 @@ using System.Windows.Media;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Forms; using System.Windows.Forms;
using ICSharpCode.Core.Presentation; using ICSharpCode.Core.Presentation;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
using WinForms = System.Windows.Forms; using WinForms = System.Windows.Forms;
@ -348,6 +350,29 @@ namespace ICSharpCode.SharpDevelop
return s.Substring(0, s.Length - stringToRemove.Length); return s.Substring(0, s.Length - stringToRemove.Length);
} }
/// <summary>
/// Takes at most <param name="length" /> first characters from string.
/// String can be null.
/// </summary>
public static string TakeStart(this string s, int length)
{
if (string.IsNullOrEmpty(s) || length >= s.Length)
return s;
return s.Substring(0, length);
}
/// <summary>
/// Takes at most <param name="length" /> first characters from string, and appends '...' if string is longer.
/// String can be null.
/// </summary>
public static string TakeStartEllipsis(this string s, int length)
{
if (string.IsNullOrEmpty(s) || length >= s.Length)
return s;
return s.Substring(0, length) + "...";
}
public static string Replace(this string original, string pattern, string replacement, StringComparison comparisonType) public static string Replace(this string original, string pattern, string replacement, StringComparison comparisonType)
{ {
if (original == null) if (original == null)
@ -458,5 +483,24 @@ namespace ICSharpCode.SharpDevelop
if (itemToAdd != null) if (itemToAdd != null)
list.Add(itemToAdd); list.Add(itemToAdd);
} }
public static ExpressionResult FindFullExpressionAtCaret(this ITextEditor editor)
{
if (editor == null)
throw new ArgumentNullException("editor");
return ParserService.FindFullExpression(editor.Caret.Line, editor.Caret.Column, editor.Document, editor.FileName);
}
public static ResolveResult ResolveSymbolAtCaret(this ITextEditor editor)
{
if (editor == null)
throw new ArgumentNullException("editor");
return ParserService.Resolve(editor.Caret.Line, editor.Caret.Column, editor.Document, editor.FileName);
}
public static bool IsEmpty(ExpressionResult expr)
{
return expr.Region.IsEmpty;
}
} }
} }

2
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Interfaces/ICompilationUnit.cs

@ -70,7 +70,7 @@ namespace ICSharpCode.SharpDevelop.Dom
} }
/// <summary> /// <summary>
/// Returns the innerst class in which the carret currently is, returns null /// Returns the innermost class in which the carret currently is, returns null
/// if the carret is outside any class boundaries. /// if the carret is outside any class boundaries.
/// </summary> /// </summary>
IClass GetInnermostClass(int caretLine, int caretColumn); IClass GetInnermostClass(int caretLine, int caretColumn);

2
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs

@ -114,7 +114,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
language = NR.SupportedLanguage.VBNet; language = NR.SupportedLanguage.VBNet;
inferAllowed = false; inferAllowed = false;
} else { } else {
throw new NotSupportedException("The language " + languageProperties.ToString() + " is not supported in the resolver"); throw new NotSupportedException("The language " + languageProperties.ToString() + " is not supported in the NRefactoryResolver");
} }
} }

Loading…
Cancel
Save