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

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

@ -18,13 +18,13 @@ namespace SharpRefactoring.ContextActions @@ -18,13 +18,13 @@ namespace SharpRefactoring.ContextActions
/// </summary>
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)
yield break;
var symbol = editorAST.SymbolUnderCaret;
foreach (var contextAction in GetAddUsingContextActions(symbol, editorAST.Editor)) {
var symbol = context.CurrentSymbol;
foreach (var contextAction in GetAddUsingContextActions(symbol, context.Editor)) {
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 @@ @@ -6,8 +6,9 @@
// </file>
using System;
using System.Collections.Generic;
using System.Windows;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.AvalonEdit;
using ICSharpCode.SharpDevelop.Refactoring;
namespace SharpRefactoring.ContextActions
@ -19,7 +20,20 @@ namespace SharpRefactoring.ContextActions @@ -19,7 +20,20 @@ namespace SharpRefactoring.ContextActions
{
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)
yield return generateCodeAction;
}

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

@ -47,5 +47,17 @@ namespace SharpRefactoring @@ -47,5 +47,17 @@ namespace SharpRefactoring
resolver.Initialize(ParserService.GetParseInformation(context.FileName), context.Caret.Line, context.Caret.Column);
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 @@ -30,12 +30,11 @@ namespace SharpRefactoring
/// <summary>
/// If given symbol is Unknown ResolveResult, returns action that can generate code for this missing symbol.
/// </summary>
public static GenerateCodeContextAction GetContextAction(ResolveResult symbol, ITextEditor editor)
public static GenerateCodeContextAction GetContextAction(ResolveResult symbol, EditorContext context)
{
if (symbol is UnknownMethodResolveResult) {
UnknownMethodResolveResult unknownMethodCall = symbol as UnknownMethodResolveResult;
Ast.Expression expression = GetExpressionInContext(unknownMethodCall, editor);
UnknownMethodResolveResult unknownMethodCall = (UnknownMethodResolveResult)symbol;
Ast.Expression expression = context.GetContainingElement<Ast.InvocationExpression>();
if (expression == null)
return null;
@ -45,58 +44,15 @@ namespace SharpRefactoring @@ -45,58 +44,15 @@ namespace SharpRefactoring
} catch (FormatException) {
}
return new IntroduceMethodContextAction(unknownMethodCall, expression, editor) {
Title = title
};
if (unknownMethodCall.Target.IsUserCode()) {
// Don't introduce method on non-modyfiable types
return new IntroduceMethodContextAction(unknownMethodCall, expression, context.Editor) {
Title = title
};
}
}
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
@ -110,25 +66,27 @@ namespace SharpRefactoring @@ -110,25 +66,27 @@ namespace SharpRefactoring
public class IntroduceMethodContextAction : GenerateCodeContextAction
{
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 IntroduceMethodContextAction(UnknownMethodResolveResult symbol, Ast.Expression expression, ITextEditor editor)
public IntroduceMethodContextAction(UnknownMethodResolveResult symbol, Ast.Expression invocationExpr, ITextEditor editor)
{
if (symbol == null)
throw new ArgumentNullException("rr");
if (expression == null)
if (invocationExpr == null)
throw new ArgumentNullException("ex");
if (editor == null)
throw new ArgumentNullException("editor");
this.UnknownMethodCall = symbol;
this.Expression = expression;
this.InvocationExpr = invocationExpr;
this.Editor = editor;
}
public override void Execute()
{
IClass targetClass = UnknownMethodCall.Target.GetUnderlyingClass();
if (targetClass == null)
return;
bool isNew = false;
object result = null;
@ -141,7 +99,7 @@ namespace SharpRefactoring @@ -141,7 +99,7 @@ namespace SharpRefactoring
?? targetClass;
}
if (targetClass.BodyRegion.IsEmpty) {
if (!targetClass.IsUserCode()) {
IntroduceMethodDialog dialog = new IntroduceMethodDialog(UnknownMethodCall.CallingClass);
dialog.Owner = WorkbenchSingleton.MainWindow;
@ -152,10 +110,10 @@ namespace SharpRefactoring @@ -152,10 +110,10 @@ namespace SharpRefactoring
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
: rr.Target.GetUnderlyingClass();
@ -167,7 +125,7 @@ namespace SharpRefactoring @@ -167,7 +125,7 @@ namespace SharpRefactoring
ModifierEnum modifiers = ModifierEnum.None;
bool isExtension = targetClass.BodyRegion.IsEmpty;
bool isExtension = !targetClass.IsUserCode();
if (IsEqualClass(rr.CallingClass, targetClass)) {
if (rr.CallingMember != null)
@ -190,11 +148,11 @@ namespace SharpRefactoring @@ -190,11 +148,11 @@ namespace SharpRefactoring
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);
if (typeRef.IsNull) {
if (expression.Parent is Ast.ExpressionStatement)
if (invocationExpr.Parent is Ast.ExpressionStatement)
typeRef = new Ast.TypeReference("void", true);
else
typeRef = new Ast.TypeReference("object", true);
@ -204,7 +162,7 @@ namespace SharpRefactoring @@ -204,7 +162,7 @@ namespace SharpRefactoring
Name = rr.CallName,
Modifier = CodeGenerator.ConvertModifier(modifiers, finder),
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)

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

@ -1,49 +0,0 @@ @@ -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 @@ -282,6 +282,8 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion
public void HighlightParameter(IInsightWindow window, int index)
{
if (window == null)
return;
var item = window.SelectedItem as MethodInsightItem;
if (item != null)

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

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

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

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

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

@ -5,8 +5,11 @@ @@ -5,8 +5,11 @@
// <version>$Revision: $</version>
// </file>
using System;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver;
using ICSharpCode.SharpDevelop.Editor;
@ -21,7 +24,32 @@ namespace ICSharpCode.SharpDevelop.Refactoring @@ -21,7 +24,32 @@ namespace ICSharpCode.SharpDevelop.Refactoring
public class EditorContext
{
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; }
public EditorContext(ITextEditor editor)
@ -29,81 +57,155 @@ namespace ICSharpCode.SharpDevelop.Refactoring @@ -29,81 +57,155 @@ namespace ICSharpCode.SharpDevelop.Refactoring
if (editor == null)
throw new ArgumentNullException("editor");
this.Editor = editor;
this.SnippetParser = GetSnippetParser(editor);
this.Resolver = GetResolver(editor);
this.CaretLine = editor.Caret.Line;
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>
ResolveResult symbolUnderCaret;
public ResolveResult SymbolUnderCaret
public TNode GetCurrentElement<TNode>() where TNode : class, INode
{
get
{
if (symbolUnderCaret != 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;
}
if (this.CurrentElement is TNode)
return (TNode)this.CurrentElement;
return null;
}
IDocumentLine currentLine;
public IDocumentLine CurrentLine
public TNode GetContainingElement<TNode>() where TNode : class, INode
{
get
var node = this.CurrentElement;
while(node != null)
{
if (currentLine != null)
return currentLine;
try
{
return (currentLine = Editor.Document.GetLine(Editor.Caret.Line));
}
catch
{
return null;
}
if (node is TNode)
return (TNode)node;
node = node.Parent;
}
return null;
}
INode currentLineAST;
public INode CurrentLineAST
// public TNode GetInnerElement<TNode>() where TNode : class, INode
// {
// var findChildVisitor = new FindOutermostNodeVisitor<TNode>();
// this.CurrentElement.AcceptVisitor(findChildVisitor, null);
// return findChildVisitor.FoundNode;
// }
INode FindInnermostNodeAtLocation(INode memberDecl, Location position)
{
get
{
if (currentLineAST != null)
return currentLineAST;
if (this.SnippetParser == null || this.CurrentLine == null)
return null;
try {
return (currentLineAST = SnippetParser.Parse(this.CurrentLine.Text));
if (memberDecl == null)
return null;
if (memberDecl is MethodDeclaration) {
return FindInnermostNodeInBlock(((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);
}
catch {
return null;
if (propertyDecl.HasSetRegion && position >= propertyDecl.SetRegion.StartLocation && position <= propertyDecl.SetRegion.EndLocation) {
return FindInnermostNodeInBlock(propertyDecl.SetRegion.Block, position);
}
}
return null;
}
INode currentMemberAST;
public INode CurrentMemberAST
INode FindInnermostNodeInBlock(BlockStatement node, Location position)
{
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)
return null;
if (currentMemberAST != null)
return currentMemberAST;
try {
Resolver.Initialize(ParserService.GetParseInformation(Editor.FileName), Editor.Caret.Line, Editor.Caret.Column);
return (currentMemberAST = Resolver.ParseCurrentMember(Editor.Document.Text));
}
catch {
return null;
this.CaretLocation = caretLocation;
}
protected override void BeginVisit(INode node)
{
if (node.StartLocation <= CaretLocation && node.EndLocation >= CaretLocation) {
// the node visited last will be the innermost
this.InnermostNode = node;
}
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)
{
var lang = GetEditorLanguage(editor);
@ -113,29 +215,42 @@ namespace ICSharpCode.SharpDevelop.Refactoring @@ -113,29 +215,42 @@ namespace ICSharpCode.SharpDevelop.Refactoring
return null;
}
NRefactoryResolver GetResolver(ITextEditor editor)
SupportedLanguage? GetEditorLanguage(ITextEditor editor)
{
if (editor == null || editor.Language == null)
return null;
try
{
return new NRefactoryResolver(editor.Language.Properties);
if (editor.Language.Properties == LanguageProperties.CSharp)
return SupportedLanguage.CSharp;
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;
}
}
SupportedLanguage? GetEditorLanguage(ITextEditor editor)
NRefactoryResolver GetNRefactoryResolver(ITextEditor editor)
{
if (editor == null || editor.Language == null)
return null;
if (editor.Language.Properties == LanguageProperties.CSharp)
return SupportedLanguage.CSharp;
if (editor.Language.Properties == LanguageProperties.VBNet)
return SupportedLanguage.VBNet;
return null;
try
{
return new NRefactoryResolver(editor.Language.Properties);
}
catch(NotSupportedException)
{
return null;
}
}
}
}

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

@ -18,6 +18,8 @@ using System.Windows.Media; @@ -18,6 +18,8 @@ using System.Windows.Media;
using System.Windows.Documents;
using System.Windows.Forms;
using ICSharpCode.Core.Presentation;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Gui;
using WinForms = System.Windows.Forms;
@ -348,6 +350,29 @@ namespace ICSharpCode.SharpDevelop @@ -348,6 +350,29 @@ namespace ICSharpCode.SharpDevelop
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)
{
if (original == null)
@ -458,5 +483,24 @@ namespace ICSharpCode.SharpDevelop @@ -458,5 +483,24 @@ namespace ICSharpCode.SharpDevelop
if (itemToAdd != null)
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 @@ -70,7 +70,7 @@ namespace ICSharpCode.SharpDevelop.Dom
}
/// <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.
/// </summary>
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 @@ -114,7 +114,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
language = NR.SupportedLanguage.VBNet;
inferAllowed = false;
} 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