1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
5
using System.Collections;
6
using System.Collections.Generic;
7
using System.Collections.ObjectModel;
8
using System.Diagnostics;
11
using System.Threading;
13
using System.Windows.Controls;
14
using System.Windows.Controls.Primitives;
15
using System.Windows.Input;
16
using ICSharpCode.Core;
17
using ICSharpCode.Core.Presentation;
18
using ICSharpCode.NRefactory.Visitors;
19
using ICSharpCode.SharpDevelop.Bookmarks;
20
using ICSharpCode.SharpDevelop.Dom;
21
using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver;
22
using ICSharpCode.SharpDevelop.Dom.Refactoring;
23
using ICSharpCode.SharpDevelop.Editor;
24
using ICSharpCode.SharpDevelop.Gui;
25
using ICSharpCode.SharpDevelop.Gui.ClassBrowser;
26
using ICSharpCode.SharpDevelop.Project;
27
using Ast = ICSharpCode.NRefactory.Ast;
29
namespace ICSharpCode.SharpDevelop.Refactoring
31
public class RefactoringMenuContext
33
public readonly ITextEditor Editor;
34
public readonly ExpressionResult ExpressionResult;
35
public readonly ResolveResult ResolveResult;
36
public readonly bool IsDefinition;
37
/// <remarks>Can be null.</remarks>
38
public readonly IProjectContent ProjectContent;
39
public readonly ICompilationUnit CompilationUnit;
41
public RefactoringMenuContext(ITextEditor editor, ExpressionResult expressionResult,
42
ResolveResult resolveResult, bool isDefinition,
43
IProjectContent projectContent, ICompilationUnit compilationUnit)
46
this.ExpressionResult = expressionResult;
47
this.ResolveResult = resolveResult;
48
this.IsDefinition = isDefinition;
49
this.ProjectContent = projectContent;
50
this.CompilationUnit = compilationUnit;
54
public interface IRefactoringMenuItemFactory
56
MenuItem Create(RefactoringMenuContext context);
60
/// Build refactoring commands for the item that has been clicked on in the text editor.
61
/// The commands are inserted to the top level of the context menu.
63
/// /SharpDevelop/ViewContent/TextEditor/ContextMenu, id=Refactoring
65
public class RefactoringMenuBuilder : IMenuItemBuilder
67
public ICollection BuildItems(Codon codon, object owner)
69
ITextEditor textEditor = (ITextEditor)owner;
70
if (string.IsNullOrEmpty(textEditor.FileName))
72
List<object> resultItems = new List<object>();
73
// list of dotnet names that have definitions in this line
74
List<string> definitions = new List<string>();
76
// Include menu for member that has been clicked on
77
IExpressionFinder expressionFinder = ParserService.GetExpressionFinder(textEditor.FileName);
78
ExpressionResult expressionResult = FindFullExpressionAtCaret(textEditor, expressionFinder);
80
AddTopLevelItems(resultItems, textEditor, expressionResult, definitions, false);
82
AddItemForCurrentClassAndMethod(resultItems, textEditor, expressionResult, definitions);
84
if (resultItems.Count > 0) {
85
resultItems.Add(new Separator());
90
void AddTopLevelItems(List<object> resultItems, ITextEditor textEditor, ExpressionResult expressionResult, List<string> definitions, bool addAsSubmenu)
92
// Insert items at this position to get the outermost expression first, followed by the inner expressions (if any).
93
int insertIndex = resultItems.Count;
94
ResolveResult rr = ResolveExpressionAtCaret(textEditor, expressionResult);
97
if (rr is MethodGroupResolveResult) {
98
item = MakeItem(definitions, ((MethodGroupResolveResult)rr).GetMethodIfSingleOverload());
99
} else if (rr is MemberResolveResult) {
100
MemberResolveResult mrr = (MemberResolveResult)rr;
101
item = MakeItem(definitions, mrr.ResolvedMember);
102
// Seems not to be needed, as AddItemForCurrentClassAndMethod works for indexer as well (martinkonicek)
103
/*if (RefactoringService.FixIndexerExpression(expressionFinder, ref expressionResult, mrr)) {
105
// Insert this member
106
resultItems.Insert(insertIndex, item);
108
// Include menu for the underlying expression of the
109
// indexer expression as well.
110
AddTopLevelItems(textEditor, expressionResult, true);
112
} else if (rr is TypeResolveResult) {
113
item = MakeItem(definitions, ((TypeResolveResult)rr).ResolvedClass);
114
} else if (rr is LocalResolveResult) {
115
bool isDefinition = textEditor.Caret.Line == ((LocalResolveResult)rr).VariableDefinitionRegion.BeginLine;
116
ParseInformation pi = ParserService.GetParseInformation(textEditor.FileName);
117
IProjectContent pc = null;
119
pc = pi.CompilationUnit.ProjectContent;
120
RefactoringMenuContext context = new RefactoringMenuContext(textEditor, expressionResult, rr, isDefinition, pc, pi.CompilationUnit);
121
item = MakeItem((LocalResolveResult)rr, context);
122
insertIndex = 0; // Insert local variable menu item at the topmost position.
126
resultItems.Insert(insertIndex, item);
128
foreach (object subItem in item.Items) {
129
resultItems.Add(subItem);
131
item.Items.Clear(); // detach resultItems from old parent
136
void AddItemForCurrentClassAndMethod(List<object> resultItems, ITextEditor textEditor, ExpressionResult expressionResult, List<string> definitions)
138
ResolveResult rr = ResolveExpressionAtCaret(textEditor, expressionResult);
139
MenuItem item = null;
140
int caretLine = textEditor.Caret.Line;
141
// Include menu for current class and method
142
ICompilationUnit cu = null;
143
IMember callingMember = null;
144
if (rr != null && rr.CallingMember != null) {
145
callingMember = rr.CallingMember;
147
ParseInformation parseInfo = ParserService.GetParseInformation(textEditor.FileName);
148
if (parseInfo != null) {
149
cu = parseInfo.CompilationUnit;
151
IClass callingClass = cu.GetInnermostClass(caretLine, textEditor.Caret.Column);
152
callingMember = GetCallingMember(callingClass, caretLine, textEditor.Caret.Column);
156
if (callingMember != null) {
157
item = MakeItem(definitions, callingMember);
159
item.Header = StringParser.Parse("${res:SharpDevelop.Refactoring.CurrentMethod}: ") + callingMember.Name;
160
resultItems.Add(item);
165
IMember GetCallingMember(IClass callingClass, int caretLine, int caretColumn)
167
if (callingClass == null) {
170
foreach (IMethod method in callingClass.Methods) {
171
if (method.BodyRegion.IsInside(caretLine, caretColumn)) {
175
foreach (IProperty property in callingClass.Properties) {
176
if (property.BodyRegion.IsInside(caretLine, caretColumn)) {
183
MenuItem MakeItem(LocalResolveResult local, RefactoringMenuContext context)
185
Debug.Assert(local == context.ResolveResult);
186
MenuItem item = MakeItemWithGoToDefinition(local.VariableName,
187
local.IsParameter ? ClassBrowserIconService.Parameter : ClassBrowserIconService.LocalVariable,
188
local.CallingClass.CompilationUnit,
189
context.IsDefinition ? DomRegion.Empty : local.VariableDefinitionRegion);
190
string treePath = "/SharpDevelop/ViewContent/DefaultTextEditor/Refactoring/";
191
treePath += local.IsParameter ? "Parameter" : "LocalVariable";
192
if (context.IsDefinition) treePath += "Definition";
193
foreach (object obj in MenuService.CreateMenuItems(null, context, treePath))
198
MenuItem MakeItem(List<string> definitions, IMember member)
200
if (member == null) return null;
201
if (definitions.Contains(member.DotNetName)) return null;
202
definitions.Add(member.DotNetName);
203
MenuItem item = MakeItem(MemberNode.Create(member), member.DeclaringType.CompilationUnit, member.Region);
204
MenuItem declaringType = MakeItem(null, member.DeclaringType);
205
if (declaringType != null) {
206
declaringType.Header = StringParser.Parse("${res:SharpDevelop.Refactoring.DeclaringType}: ") + declaringType.Header;
207
item.Items.Add(declaringType);
212
MenuItem MakeItem(List<string> definitions, IClass c)
214
if (c == null) return null;
215
if (definitions != null) {
216
if (definitions.Contains(c.DotNetName)) return null;
217
definitions.Add(c.DotNetName);
219
return MakeItem(new ClassNode((IProject)c.ProjectContent.Project, c), c.CompilationUnit, c.Region);
222
MenuItem MakeItemWithGoToDefinition(string title, IImage image, ICompilationUnit cu, DomRegion region)
224
MenuItem item = new MenuItem();
226
item.Icon = image.CreateImage();
227
if (cu != null && cu.FileName != null && !region.IsEmpty) {
228
MenuItem gotoDefinitionItem = new MenuItem();
229
gotoDefinitionItem.Header = MenuService.ConvertLabel(StringParser.Parse("${res:ICSharpCode.NAntAddIn.GotoDefinitionMenuLabel}"));
230
gotoDefinitionItem.Icon = ClassBrowserIconService.GotoArrow.CreateImage();
231
gotoDefinitionItem.InputGestureText = new KeyGesture(Key.Enter, ModifierKeys.Control).GetDisplayStringForCulture(Thread.CurrentThread.CurrentUICulture);
232
gotoDefinitionItem.Click += delegate {
233
FileService.JumpToFilePosition(cu.FileName, region.BeginLine, region.BeginColumn);
235
item.Items.Add(gotoDefinitionItem);
240
MenuItem MakeItem(ExtTreeNode classBrowserTreeNode, ICompilationUnit cu, DomRegion region)
242
MenuItem item = MakeItemWithGoToDefinition(classBrowserTreeNode.Text, ClassBrowserIconService.GetImageByIndex(classBrowserTreeNode.ImageIndex), cu, region);
243
foreach (object obj in MenuService.CreateMenuItems(null, classBrowserTreeNode, classBrowserTreeNode.ContextmenuAddinTreePath))
248
static ExpressionResult FindFullExpressionAtCaret(ITextEditor textArea, IExpressionFinder expressionFinder)
250
if (expressionFinder != null) {
251
return expressionFinder.FindFullExpression(textArea.Document.Text, textArea.Caret.Offset);
253
return new ExpressionResult(null);
257
static ResolveResult ResolveExpressionAtCaret(ITextEditor textArea, ExpressionResult expressionResult)
259
if (expressionResult.Expression != null) {
260
return ParserService.Resolve(expressionResult, textArea.Caret.Line, textArea.Caret.Column, textArea.FileName, textArea.Document.Text);