~halega/+junk/sharpdevelop

« back to all changes in this revision

Viewing changes to src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs

  • Committer: sk
  • Date: 2011-09-10 05:17:57 UTC
  • Revision ID: halega@halega.com-20110910051757-qfouz1llya9m6boy
4.1.0.7915 Release Candidate 1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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)
 
3
 
 
4
using System;
 
5
using System.Collections;
 
6
using System.Collections.Generic;
 
7
using System.Collections.ObjectModel;
 
8
using System.Diagnostics;
 
9
using System.IO;
 
10
using System.Linq;
 
11
using System.Threading;
 
12
using System.Windows;
 
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;
 
28
 
 
29
namespace ICSharpCode.SharpDevelop.Refactoring
 
30
{
 
31
        public class RefactoringMenuContext
 
32
        {
 
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;
 
40
                
 
41
                public RefactoringMenuContext(ITextEditor editor, ExpressionResult expressionResult,
 
42
                                              ResolveResult resolveResult, bool isDefinition,
 
43
                                              IProjectContent projectContent, ICompilationUnit compilationUnit)
 
44
                {
 
45
                        this.Editor = editor;
 
46
                        this.ExpressionResult = expressionResult;
 
47
                        this.ResolveResult = resolveResult;
 
48
                        this.IsDefinition = isDefinition;
 
49
                        this.ProjectContent = projectContent;
 
50
                        this.CompilationUnit = compilationUnit;
 
51
                }
 
52
        }
 
53
        
 
54
        public interface IRefactoringMenuItemFactory
 
55
        {
 
56
                MenuItem Create(RefactoringMenuContext context);
 
57
        }
 
58
        
 
59
        /// <summary>
 
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.
 
62
        /// Path:
 
63
        /// /SharpDevelop/ViewContent/TextEditor/ContextMenu, id=Refactoring
 
64
        /// </summary>
 
65
        public class RefactoringMenuBuilder : IMenuItemBuilder
 
66
        {
 
67
                public ICollection BuildItems(Codon codon, object owner)
 
68
                {
 
69
                        ITextEditor textEditor = (ITextEditor)owner;
 
70
                        if (string.IsNullOrEmpty(textEditor.FileName))
 
71
                                return new object[0];
 
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>();
 
75
                        
 
76
                        // Include menu for member that has been clicked on
 
77
                        IExpressionFinder expressionFinder = ParserService.GetExpressionFinder(textEditor.FileName);
 
78
                        ExpressionResult expressionResult = FindFullExpressionAtCaret(textEditor, expressionFinder);
 
79
                        
 
80
                        AddTopLevelItems(resultItems, textEditor, expressionResult, definitions, false);
 
81
                        
 
82
                        AddItemForCurrentClassAndMethod(resultItems, textEditor, expressionResult, definitions);
 
83
                        
 
84
                        if (resultItems.Count > 0) {
 
85
                                resultItems.Add(new Separator());
 
86
                        }
 
87
                        return resultItems;
 
88
                }
 
89
                
 
90
                void AddTopLevelItems(List<object> resultItems, ITextEditor textEditor, ExpressionResult expressionResult, List<string> definitions, bool addAsSubmenu)
 
91
                {
 
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);
 
95
                        MenuItem item = null;
 
96
                        
 
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)) {
 
104
                                        if (item != null) {
 
105
                                                // Insert this member
 
106
                                                resultItems.Insert(insertIndex, item);
 
107
                                        }
 
108
                                        // Include menu for the underlying expression of the
 
109
                                        // indexer expression as well.
 
110
                                        AddTopLevelItems(textEditor, expressionResult, true);
 
111
                                }*/
 
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; 
 
118
                                if (pi != 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.
 
123
                        }
 
124
                        if (item != null) {
 
125
                                if (addAsSubmenu) {
 
126
                                        resultItems.Insert(insertIndex, item);
 
127
                                } else {
 
128
                                        foreach (object subItem in item.Items) {
 
129
                                                resultItems.Add(subItem);
 
130
                                        }
 
131
                                        item.Items.Clear(); // detach resultItems from old parent
 
132
                                }
 
133
                        }
 
134
                }
 
135
                
 
136
                void AddItemForCurrentClassAndMethod(List<object> resultItems, ITextEditor textEditor, ExpressionResult expressionResult, List<string> definitions)
 
137
                {
 
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;
 
146
                        } else {
 
147
                                ParseInformation parseInfo = ParserService.GetParseInformation(textEditor.FileName);
 
148
                                if (parseInfo != null) {
 
149
                                        cu = parseInfo.CompilationUnit;
 
150
                                        if (cu != null) {
 
151
                                                IClass callingClass = cu.GetInnermostClass(caretLine, textEditor.Caret.Column);
 
152
                                                callingMember = GetCallingMember(callingClass, caretLine, textEditor.Caret.Column);
 
153
                                        }
 
154
                                }
 
155
                        }
 
156
                        if (callingMember != null) {
 
157
                                item = MakeItem(definitions, callingMember);
 
158
                                if (item != null) {
 
159
                                        item.Header = StringParser.Parse("${res:SharpDevelop.Refactoring.CurrentMethod}: ") + callingMember.Name;
 
160
                                        resultItems.Add(item);
 
161
                                }
 
162
                        }
 
163
                }
 
164
                
 
165
                IMember GetCallingMember(IClass callingClass, int caretLine, int caretColumn)
 
166
                {
 
167
                        if (callingClass == null) {
 
168
                                return null;
 
169
                        }
 
170
                        foreach (IMethod method in callingClass.Methods) {
 
171
                                if (method.BodyRegion.IsInside(caretLine, caretColumn)) {
 
172
                                        return method;
 
173
                                }
 
174
                        }
 
175
                        foreach (IProperty property in callingClass.Properties) {
 
176
                                if (property.BodyRegion.IsInside(caretLine, caretColumn)) {
 
177
                                        return property;
 
178
                                }
 
179
                        }
 
180
                        return null;
 
181
                }
 
182
                
 
183
                MenuItem MakeItem(LocalResolveResult local, RefactoringMenuContext context)
 
184
                {
 
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))
 
194
                                item.Items.Add(obj);
 
195
                        return item;
 
196
                }
 
197
                
 
198
                MenuItem MakeItem(List<string> definitions, IMember member)
 
199
                {
 
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);
 
208
                        }
 
209
                        return item;
 
210
                }
 
211
                
 
212
                MenuItem MakeItem(List<string> definitions, IClass c)
 
213
                {
 
214
                        if (c == null) return null;
 
215
                        if (definitions != null) {
 
216
                                if (definitions.Contains(c.DotNetName)) return null;
 
217
                                definitions.Add(c.DotNetName);
 
218
                        }
 
219
                        return MakeItem(new ClassNode((IProject)c.ProjectContent.Project, c), c.CompilationUnit, c.Region);
 
220
                }
 
221
                
 
222
                MenuItem MakeItemWithGoToDefinition(string title, IImage image, ICompilationUnit cu, DomRegion region)
 
223
                {
 
224
                        MenuItem item = new MenuItem();
 
225
                        item.Header = title;
 
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);
 
234
                                };
 
235
                                item.Items.Add(gotoDefinitionItem);
 
236
                        }
 
237
                        return item;
 
238
                }
 
239
                
 
240
                MenuItem MakeItem(ExtTreeNode classBrowserTreeNode, ICompilationUnit cu, DomRegion region)
 
241
                {
 
242
                        MenuItem item = MakeItemWithGoToDefinition(classBrowserTreeNode.Text, ClassBrowserIconService.GetImageByIndex(classBrowserTreeNode.ImageIndex), cu, region);
 
243
                        foreach (object obj in MenuService.CreateMenuItems(null, classBrowserTreeNode, classBrowserTreeNode.ContextmenuAddinTreePath))
 
244
                                item.Items.Add(obj);
 
245
                        return item;
 
246
                }
 
247
                
 
248
                static ExpressionResult FindFullExpressionAtCaret(ITextEditor textArea, IExpressionFinder expressionFinder)
 
249
                {
 
250
                        if (expressionFinder != null) {
 
251
                                return expressionFinder.FindFullExpression(textArea.Document.Text, textArea.Caret.Offset);
 
252
                        } else {
 
253
                                return new ExpressionResult(null);
 
254
                        }
 
255
                }
 
256
                
 
257
                static ResolveResult ResolveExpressionAtCaret(ITextEditor textArea, ExpressionResult expressionResult)
 
258
                {
 
259
                        if (expressionResult.Expression != null) {
 
260
                                return ParserService.Resolve(expressionResult, textArea.Caret.Line, textArea.Caret.Column, textArea.FileName, textArea.Document.Text);
 
261
                        }
 
262
                        return null;
 
263
                }
 
264
        }
 
265
}