From 8d12fe8a20a0d647b0ad11419fa1326a146f1755 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 21 Sep 2011 18:09:10 +0200 Subject: [PATCH] Reimplemented F6 for members. --- .../Project/Src/CSharpCompletionBinding.cs | 304 +----------------- .../Base/Project/Src/Commands/FileCommands.cs | 6 +- .../CodeCompletion/CodeCompletionBinding.cs | 155 +++------ .../ContextActions/ContextActionsHelper.cs | 23 +- .../FindReferenceService.cs | 125 ++++++- .../RefactoringService/GoToMemberAction.cs | 6 +- .../Base/Project/Src/Util/ExtensionMethods.cs | 1 + 7 files changed, 192 insertions(+), 428 deletions(-) diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs index 758da4f2f0..256e779b70 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpCompletionBinding.cs @@ -1,313 +1,33 @@ // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) -/* using System; -using ICSharpCode.Core; -using ICSharpCode.NRefactory; -using ICSharpCode.NRefactory.Visitors; -using ICSharpCode.SharpDevelop; -using ICSharpCode.SharpDevelop.Dom; -using ICSharpCode.SharpDevelop.Dom.CSharp; -using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver; -using ICSharpCode.SharpDevelop.Dom.Refactoring; +using ICSharpCode.NRefactory.Completion; +using ICSharpCode.NRefactory.CSharp.Completion; using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor.CodeCompletion; -using AST = ICSharpCode.NRefactory.Ast; namespace CSharpBinding { - public class CSharpCompletionBinding : NRefactoryCodeCompletionBinding + public class CSharpCompletionBinding : ICodeCompletionBinding { - public CSharpCompletionBinding() : base(SupportedLanguage.CSharp) + public CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) { + return CodeCompletionKeyPressResult.None; } - static CSharpExpressionFinder CreateExpressionFinder(string fileName) + public bool HandleKeyPressed(ITextEditor editor, char ch) { - return new CSharpExpressionFinder(ParserService.GetParseInformation(fileName)); + if (ch == '.') + return CtrlSpace(editor); + else + return false; } - public override CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) + public bool CtrlSpace(ITextEditor editor) { - CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName); - int cursor = editor.Caret.Offset; - if (ch == '[') { - var line = editor.Document.GetLineForOffset(cursor); - - } else if (ch == ',' && CodeCompletionOptions.InsightRefreshOnComma && CodeCompletionOptions.InsightEnabled) { - IInsightWindow insightWindow; - if (insightHandler.InsightRefreshOnComma(editor, ch, out insightWindow)) { - return CodeCompletionKeyPressResult.Completed; - } - } else if(ch == '=') { - var curLine = editor.Document.GetLineForOffset(cursor); - string documentText = editor.Document.Text; - int position = editor.Caret.Offset - 2; - - if (position > 0 && (documentText[position + 1] == '+')) { - ExpressionResult result = ef.FindFullExpression(documentText, position); - - if(result.Expression != null) { - ResolveResult resolveResult = ParserService.Resolve(result, editor.Caret.Line, editor.Caret.Column, editor.FileName, documentText); - if (resolveResult != null && resolveResult.ResolvedType != null) { - IClass underlyingClass = resolveResult.ResolvedType.GetUnderlyingClass(); - if (underlyingClass != null && underlyingClass.IsTypeInInheritanceTree(ParserService.CurrentProjectContent.GetClass("System.MulticastDelegate", 0))) { - EventHandlerCompletionItemProvider eventHandlerProvider = new EventHandlerCompletionItemProvider(result.Expression, resolveResult); - eventHandlerProvider.ShowCompletion(editor); - return CodeCompletionKeyPressResult.Completed; - } - } - } - } else if (position > 0) { - ExpressionResult result = ef.FindFullExpression(documentText, position); - - if(result.Expression != null) { - ResolveResult resolveResult = ParserService.Resolve(result, editor.Caret.Line, editor.Caret.Column, editor.FileName, documentText); - if (resolveResult != null && resolveResult.ResolvedType != null) { - if (ProvideContextCompletion(editor, resolveResult.ResolvedType, ch)) { - return CodeCompletionKeyPressResult.Completed; - } - } - } - } - } else if (ch == '.') { - new CSharpCodeCompletionDataProvider().ShowCompletion(editor); - return CodeCompletionKeyPressResult.Completed; - } else if (ch == '>') { - if (IsInComment(editor)) return CodeCompletionKeyPressResult.None; - char prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' '; - if (prevChar == '-') { - new PointerArrowCompletionDataProvider().ShowCompletion(editor); - - return CodeCompletionKeyPressResult.Completed; - } - } - - if (char.IsLetter(ch) && CodeCompletionOptions.CompleteWhenTyping) { - if (editor.SelectionLength > 0) { - // allow code completion when overwriting an identifier - int endOffset = editor.SelectionStart + editor.SelectionLength; - // but block code completion when overwriting only part of an identifier - if (endOffset < editor.Document.TextLength && char.IsLetterOrDigit(editor.Document.GetCharAt(endOffset))) - return CodeCompletionKeyPressResult.None; - - editor.Document.Remove(editor.SelectionStart, editor.SelectionLength); - // Read caret position again after removal - this is required because the document might change in other - // locations, too (e.g. bound elements in snippets). - cursor = editor.Caret.Offset; - } - char prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' '; - bool afterUnderscore = prevChar == '_'; - if (afterUnderscore) { - cursor--; - prevChar = cursor > 1 ? editor.Document.GetCharAt(cursor - 1) : ' '; - } - if (!char.IsLetterOrDigit(prevChar) && prevChar != '.' && !IsInComment(editor)) { - ExpressionResult result = ef.FindExpression(editor.Document.Text, cursor); - LoggingService.Debug("CC: Beginning to type a word, result=" + result); - if (result.Context != ExpressionContext.IdentifierExpected) { - var ctrlSpaceProvider = new NRefactoryCtrlSpaceCompletionItemProvider(LanguageProperties.CSharp, result.Context); - ctrlSpaceProvider.ShowTemplates = true; - ctrlSpaceProvider.AllowCompleteExistingExpression = afterUnderscore; - ctrlSpaceProvider.ShowCompletion(editor); - return CodeCompletionKeyPressResult.CompletedIncludeKeyInCompletion; - } - } - } - - return base.HandleKeyPress(editor, ch); - } - - class CSharpCodeCompletionDataProvider : DotCodeCompletionItemProvider - { - public override ResolveResult Resolve(ITextEditor editor, ExpressionResult expressionResult) - { - // bypass ParserService.Resolve and set resolver.LimitMethodExtractionUntilCaretLine - ParseInformation parseInfo = ParserService.GetParseInformation(editor.FileName); - NRefactoryResolver resolver = new NRefactoryResolver(LanguageProperties.CSharp); - resolver.LimitMethodExtractionUntilLine = editor.Caret.Line; - return resolver.Resolve(expressionResult, parseInfo, editor.Document.Text); - } - } - - class PointerArrowCompletionDataProvider : DotCodeCompletionItemProvider - { - public override ResolveResult Resolve(ITextEditor editor, ExpressionResult expressionResult) - { - ResolveResult rr = base.Resolve(editor, expressionResult); - if (rr != null && rr.ResolvedType != null) { - PointerReturnType prt = rr.ResolvedType.CastToDecoratingReturnType(); - if (prt != null) - return new ResolveResult(rr.CallingClass, rr.CallingMember, prt.BaseType); - } - return null; - } - - public override ExpressionResult GetExpression(ITextEditor editor) - { - // - 1 because the "-" is already inserted (the ">" is about to be inserted) - return GetExpressionFromOffset(editor, editor.Caret.Offset - 1); - } - } - - bool IsInComment(ITextEditor editor) - { - CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName); - int cursor = editor.Caret.Offset - 1; - return ef.FilterComments(editor.Document.GetText(0, cursor + 1), ref cursor) == null; - } - - public override bool HandleKeyword(ITextEditor editor, string word) - { - switch (word) { - case "using": - if (IsInComment(editor)) return false; - - ParseInformation parseInfo = ParserService.GetParseInformation(editor.FileName); - if (parseInfo != null) { - IClass innerMostClass = parseInfo.CompilationUnit.GetInnermostClass(editor.Caret.Line, editor.Caret.Column); - if (innerMostClass == null) { - new NRefactoryCtrlSpaceCompletionItemProvider(LanguageProperties.CSharp, ExpressionContext.Namespace).ShowCompletion(editor); - return true; - } - } - break; - case "as": - case "is": - if (IsInComment(editor)) return false; - new NRefactoryCtrlSpaceCompletionItemProvider(LanguageProperties.CSharp, ExpressionContext.Type).ShowCompletion(editor); - return true; - case "override": - if (IsInComment(editor)) return false; - new OverrideCompletionItemProvider().ShowCompletion(editor); - return true; - case "new": - return ShowNewCompletion(editor); - case "case": - if (IsInComment(editor)) return false; - return DoCaseCompletion(editor); - case "return": - if (IsInComment(editor)) return false; - IMember m = GetCurrentMember(editor); - if (m != null) { - return ProvideContextCompletion(editor, m.ReturnType, ' '); - } - break; - } - return base.HandleKeyword(editor, word); - } - - bool ShowNewCompletion(ITextEditor editor) - { - CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName); - int cursor = editor.Caret.Offset; - string documentToCursor = editor.Document.GetText(0, cursor); - ExpressionResult expressionResult = ef.FindExpression(documentToCursor, cursor); - - LoggingService.Debug("ShowNewCompletion: expression is " + expressionResult); - if (expressionResult.Context.IsObjectCreation) { - var currentLine = editor.Document.GetLineForOffset(cursor); - string lineText = editor.Document.GetText(currentLine.Offset, cursor - currentLine.Offset); - // when the new follows an assignment, improve code-completion by detecting the - // type of the variable that is assigned to - if (lineText.Replace(" ", "").EndsWith("=new")) { - int pos = lineText.LastIndexOf('='); - ExpressionContext context = FindExactContextForNewCompletion(editor, documentToCursor, - currentLine, pos); - if (context != null) - expressionResult.Context = context; - } - new NRefactoryCtrlSpaceCompletionItemProvider(LanguageProperties.CSharp, expressionResult.Context).ShowCompletion(editor); - return true; - } return false; } - - ExpressionContext FindExactContextForNewCompletion(ITextEditor editor, string documentToCursor, - IDocumentLine currentLine, int pos) - { - CSharpExpressionFinder ef = CreateExpressionFinder(editor.FileName); - // find expression on left hand side of the assignment - ExpressionResult lhsExpr = ef.FindExpression(documentToCursor, currentLine.Offset + pos); - if (lhsExpr.Expression != null) { - ResolveResult rr = ParserService.Resolve(lhsExpr, currentLine.LineNumber, pos, editor.FileName, editor.Document.Text); - if (rr != null && rr.ResolvedType != null) { - ExpressionContext context; - IClass c; - if (rr.ResolvedType.IsArrayReturnType) { - // when creating an array, all classes deriving from the array's element type are allowed - IReturnType elementType = rr.ResolvedType.CastToArrayReturnType().ArrayElementType; - c = elementType != null ? elementType.GetUnderlyingClass() : null; - context = ExpressionContext.TypeDerivingFrom(elementType, false); - } else { - // when creating a normal instance, all non-abstract classes deriving from the type - // are allowed - c = rr.ResolvedType.GetUnderlyingClass(); - context = ExpressionContext.TypeDerivingFrom(rr.ResolvedType, true); - } - if (c != null && context.ShowEntry(c)) { - // Try to suggest an entry (List a = new => suggest List). - - string suggestedClassName = LanguageProperties.CSharp.CodeGenerator.GenerateCode( - CodeGenerator.ConvertType( - rr.ResolvedType, - new ClassFinder(ParserService.GetParseInformation(editor.FileName), editor.Caret.Line, editor.Caret.Column) - ), ""); - if (suggestedClassName != c.Name) { - // create a special code completion item that completes also the type arguments - context.SuggestedItem = new SuggestedCodeCompletionItem(c, suggestedClassName); - } else { - context.SuggestedItem = new CodeCompletionItem(c); - } - } - return context; - } - } - return null; - } - - #region "case"-keyword completion - bool DoCaseCompletion(ITextEditor editor) - { - ITextEditorCaret caret = editor.Caret; - NRefactoryResolver r = new NRefactoryResolver(LanguageProperties.CSharp); - if (r.Initialize(ParserService.GetParseInformation(editor.FileName), caret.Line, caret.Column)) { - AST.INode currentMember = r.ParseCurrentMember(editor.Document.Text); - if (currentMember != null) { - CaseCompletionSwitchFinder ccsf = new CaseCompletionSwitchFinder(caret.Line, caret.Column); - currentMember.AcceptVisitor(ccsf, null); - if (ccsf.bestStatement != null) { - r.RunLookupTableVisitor(currentMember); - ResolveResult rr = r.ResolveInternal(ccsf.bestStatement.SwitchExpression, ExpressionContext.Default); - if (rr != null && rr.ResolvedType != null) { - return ProvideContextCompletion(editor, rr.ResolvedType, ' '); - } - } - } - } - return false; - } - - private class CaseCompletionSwitchFinder : AbstractAstVisitor - { - Location caretLocation; - internal AST.SwitchStatement bestStatement; - - public CaseCompletionSwitchFinder(int caretLine, int caretColumn) - { - caretLocation = new Location(caretColumn, caretLine); - } - - public override object VisitSwitchStatement(AST.SwitchStatement switchStatement, object data) - { - if (switchStatement.StartLocation < caretLocation && caretLocation < switchStatement.EndLocation) { - bestStatement = switchStatement; - } - return base.VisitSwitchStatement(switchStatement, data); - } - } - #endregion } } -*/ + diff --git a/src/Main/Base/Project/Src/Commands/FileCommands.cs b/src/Main/Base/Project/Src/Commands/FileCommands.cs index 09faad359f..bb26141ea6 100644 --- a/src/Main/Base/Project/Src/Commands/FileCommands.cs +++ b/src/Main/Base/Project/Src/Commands/FileCommands.cs @@ -65,7 +65,8 @@ namespace ICSharpCode.SharpDevelop.Commands internal static void Save(IWorkbenchWindow window) { - window.ViewContents.ForEach(Save); + foreach (var vc in window.ViewContents) + Save(vc); } internal static void Save(IViewContent content) @@ -153,7 +154,8 @@ namespace ICSharpCode.SharpDevelop.Commands // save remaining files once (display Save As dialog) var files = remainingViewContents.SelectMany(content => content.Files).Distinct(); - files.ForEach(Save); + foreach (var file in files) + Save(file); } internal static void Save(OpenedFile file) diff --git a/src/Main/Base/Project/Src/Editor/CodeCompletion/CodeCompletionBinding.cs b/src/Main/Base/Project/Src/Editor/CodeCompletion/CodeCompletionBinding.cs index 3e38f663ca..d63164b2e9 100644 --- a/src/Main/Base/Project/Src/Editor/CodeCompletion/CodeCompletionBinding.cs +++ b/src/Main/Base/Project/Src/Editor/CodeCompletion/CodeCompletionBinding.cs @@ -13,7 +13,28 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion /// public interface ICodeCompletionBinding { + /// + /// This method is called when typing a character in the editor, immediately before + /// the character is inserted into the document. + /// + /// The editor + /// The character being inserted. + /// Returns whether the completion binding has shown code completion. CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch); + + /// + /// This method is called after typing a character in the editor, + /// after character is inserted into the document. + /// + /// The editor + /// The character that was inserted. + /// Returns whether the completion binding has shown code completion. + bool HandleKeyPressed(ITextEditor editor, char ch); + + /// + /// Invokes ctrl-space completion. + /// + /// Returns whether the completion binding has shown code completion. bool CtrlSpace(ITextEditor editor); } @@ -90,7 +111,7 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion this.extensions = extensions; } - public CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) + bool MatchesExtension(ITextEditor editor) { string ext = Path.GetExtension(editor.FileName); foreach (string extension in extensions) { @@ -98,132 +119,36 @@ namespace ICSharpCode.SharpDevelop.Editor.CodeCompletion if (binding == null) { binding = (ICodeCompletionBinding)codon.AddIn.CreateObject(codon.Properties["class"]); if (binding == null) - break; + return false; } - return binding.HandleKeyPress(editor, ch); - } - } - return CodeCompletionKeyPressResult.None; - } - - public bool CtrlSpace(ITextEditor editor) - { - string ext = Path.GetExtension(editor.FileName); - foreach (string extension in extensions) { - if (ext.Equals(extension, StringComparison.OrdinalIgnoreCase)) { - if (binding == null) { - binding = (ICodeCompletionBinding)codon.AddIn.CreateObject(codon.Properties["class"]); - if (binding == null) - break; - } - return binding.CtrlSpace(editor); + return true; } } return false; } - } - - /* - public class DefaultCodeCompletionBinding : ICodeCompletionBinding - { - bool enableMethodInsight = true; - bool enableIndexerInsight = true; - bool enableXmlCommentCompletion = true; - bool enableDotCompletion = true; - protected IInsightWindowHandler insightHandler; - public bool EnableMethodInsight { - get { - return enableMethodInsight; - } - set { - enableMethodInsight = value; - } - } - - public bool EnableIndexerInsight { - get { - return enableIndexerInsight; - } - set { - enableIndexerInsight = value; - } - } - - public bool EnableXmlCommentCompletion { - get { - return enableXmlCommentCompletion; - } - set { - enableXmlCommentCompletion = value; - } - } - - public bool EnableDotCompletion { - get { - return enableDotCompletion; - } - set { - enableDotCompletion = value; - } - } - - public virtual CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) + public CodeCompletionKeyPressResult HandleKeyPress(ITextEditor editor, char ch) { - switch (ch) { - case '(': - if (enableMethodInsight && CodeCompletionOptions.InsightEnabled) { - IInsightWindow insightWindow = editor.ShowInsightWindow(new MethodInsightProvider().ProvideInsight(editor)); - if (insightWindow != null && insightHandler != null) { - insightHandler.InitializeOpenedInsightWindow(editor, insightWindow); - insightHandler.HighlightParameter(insightWindow, 0); - } - return CodeCompletionKeyPressResult.Completed; - } - break; - case '[': - if (enableIndexerInsight && CodeCompletionOptions.InsightEnabled) { - IInsightWindow insightWindow = editor.ShowInsightWindow(new IndexerInsightProvider().ProvideInsight(editor)); - if (insightWindow != null && insightHandler != null) - insightHandler.InitializeOpenedInsightWindow(editor, insightWindow); - return CodeCompletionKeyPressResult.Completed; - } - break; - case '<': - if (enableXmlCommentCompletion) { - new CommentCompletionItemProvider().ShowCompletion(editor); - return CodeCompletionKeyPressResult.Completed; - } - break; - case '.': - if (enableDotCompletion) { - new DotCodeCompletionItemProvider().ShowCompletion(editor); - return CodeCompletionKeyPressResult.Completed; - } - break; - case ' ': - if (CodeCompletionOptions.KeywordCompletionEnabled) { - string word = editor.GetWordBeforeCaret(); - if (!string.IsNullOrEmpty(word)) { - if (HandleKeyword(editor, word)) - return CodeCompletionKeyPressResult.Completed; - } - } - break; - } - return CodeCompletionKeyPressResult.None; + if (MatchesExtension(editor)) + return binding.HandleKeyPress(editor, ch); + else + return CodeCompletionKeyPressResult.None; } - public virtual bool HandleKeyword(ITextEditor editor, string word) + public bool HandleKeyPressed(ITextEditor editor, char ch) { - // DefaultCodeCompletionBinding does not support Keyword handling, but this - // method can be overridden - return false; + if (MatchesExtension(editor)) + return binding.CtrlSpace(editor); + else + return false; } - public virtual bool CtrlSpace(ITextEditor editor) + public bool CtrlSpace(ITextEditor editor) { - return false; + if (MatchesExtension(editor)) + return binding.CtrlSpace(editor); + else + return false; } - }*/ + } } diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsHelper.cs b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsHelper.cs index 7c55a7c847..9b0314ce5e 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsHelper.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsHelper.cs @@ -40,16 +40,14 @@ namespace ICSharpCode.SharpDevelop.Refactoring public static ContextActionsPopup MakePopupWithOverrides(IMember member) { - #warning Reimplement MakePopupWithOverrides - throw new NotImplementedException(); - /*var derivedClassesTree = RefactoringService.FindDerivedClassesTree(member.DeclaringType); + var derivedClassesTree = FindReferenceService.BuildDerivedTypesGraph(member.DeclaringTypeDefinition).ConvertToDerivedTypeTree(); var popupViewModel = new ContextActionsViewModel { Title = MenuService.ConvertLabel(StringParser.Parse( "${res:SharpDevelop.Refactoring.OverridesOf}", - new StringTagPair("Name", member.FullyQualifiedName)) - )}; - popupViewModel.Actions = new OverridesPopupTreeViewModelBuilder(member).BuildTreeViewModel(derivedClassesTree); - return new ContextActionsPopup { Actions = popupViewModel, Symbol = member };*/ + new StringTagPair("Name", member.FullName)) + )}; + popupViewModel.Actions = new OverridesPopupTreeViewModelBuilder(member).BuildTreeViewModel(derivedClassesTree.Children); + return new ContextActionsPopup { Actions = popupViewModel, Symbol = member }; } class PopupViewModelBuilder @@ -92,7 +90,6 @@ namespace ICSharpCode.SharpDevelop.Refactoring } } - /* class OverridesPopupTreeViewModelBuilder : PopupViewModelBuilder { IMember member; @@ -106,13 +103,13 @@ namespace ICSharpCode.SharpDevelop.Refactoring protected ContextActionViewModel MakeGoToMemberAction(ITypeDefinition containingClass, ObservableCollection childActions) { - var overridenMember = MemberLookupHelper.FindSimilarMember(containingClass, this.member); - if (overridenMember == null || overridenMember.Region.IsEmpty) + IMember derivedMember = FindReferenceService.GetDerivedMember(member, containingClass); + if (derivedMember == null) return null; return new ContextActionViewModel { - Action = new GoToMemberAction(overridenMember, this.LabelAmbience), - Image = ClassBrowserIconService.GetIcon(overridenMember).ImageSource, + Action = new GoToMemberAction(derivedMember, this.LabelAmbience), + Image = ClassBrowserIconService.GetIcon(derivedMember).ImageSource, Comment = string.Format("(in {0})", containingClass.FullName), ChildActions = childActions }; @@ -124,6 +121,6 @@ namespace ICSharpCode.SharpDevelop.Refactoring classTree.Select( node => MakeGoToMemberAction(node.Content, BuildTreeViewModel(node.Children))).Where(action => action != null)); } - }*/ + } } } diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs index 20c456646e..a607f9fe90 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/FindReferenceService.cs @@ -15,7 +15,8 @@ using ICSharpCode.SharpDevelop.Project; namespace ICSharpCode.SharpDevelop.Refactoring { /// - /// Description of FindReferenceService. + /// Language-independent finds references implementation. + /// This call will call into the individual language bindings to perform the job. /// public static class FindReferenceService { @@ -85,6 +86,9 @@ namespace ICSharpCode.SharpDevelop.Refactoring } } + /// + /// Finds references to a local variable. + /// public static void FindReferences(IVariable variable, Action callback) { if (variable == null) @@ -96,8 +100,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring ParseInformation pi = ParserService.Parse(fileName); if (pi == null || parser == null) return; - IProject project = ParserService.GetProject(pi.ProjectContent); - var context = project != null ? project.TypeResolveContext : ParserService.GetDefaultTypeResolveContext(); + var context = ParserService.GetTypeResolveContext(pi.ProjectContent); parser.FindLocalReferences(pi, variable, context, callback); } #endregion @@ -192,6 +195,122 @@ namespace ICSharpCode.SharpDevelop.Refactoring return dict; } #endregion + + #region GetBaseMember + /// + /// Gets the base member that has the same signature. + /// + public static IMember GetBaseMember(IMember member) + { + return GetBaseMembers(member, false).FirstOrDefault(); + } + + /// + /// Gets all base members that have the same signature. + /// + /// + /// List of base members with the same signature. The member from the derived-most base class is returned first. + /// + public static IEnumerable GetBaseMembers(IMember member, bool includeImplementedInterfaces) + { + if (member == null) + throw new ArgumentNullException("member"); + member = member.MemberDefinition; + IMethod method = member as IMethod; + IProperty property = member as IProperty; + IEvent ev = member as IEvent; + IField field = member as IField; + using (var ctx = ParserService.GetTypeResolveContext(member.ProjectContent).Synchronize()) { + IEnumerable allBaseTypes; + if (includeImplementedInterfaces) { + allBaseTypes = member.DeclaringTypeDefinition.GetAllBaseTypes(ctx); + } else { + allBaseTypes = member.DeclaringTypeDefinition.GetNonInterfaceBaseTypes(ctx); + } + foreach (IType baseType in allBaseTypes.Reverse()) { + if (baseType == member.DeclaringTypeDefinition) + continue; + + if (method != null) { + foreach (IMethod baseMethod in baseType.GetMethods( + ctx, m => m.Name == method.Name && m.Parameters.Count == method.Parameters.Count && m.TypeParameters.Count == method.TypeParameters.Count, + GetMemberOptions.IgnoreInheritedMembers)) + { + if (ParameterListComparer.Compare(ctx, method, baseMethod)) + yield return baseMethod; + } + } + if (property != null) { + foreach (IProperty baseProperty in baseType.GetProperties( + ctx, p => p.Name == property.Name && p.Parameters.Count == property.Parameters.Count, + GetMemberOptions.IgnoreInheritedMembers)) + { + if (ParameterListComparer.Compare(ctx, property, baseProperty)) + yield return baseProperty; + } + } + if (ev != null) { + IEvent baseEvent = baseType.GetEvents(ctx, e => e.Name == ev.Name, GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); + if (baseEvent != null) + yield return baseEvent; + } + // Fields can't be overridden, but we handle them anyways, just for consistence + if (field != null) { + IField baseField = baseType.GetFields(ctx, f => f.Name == field.Name, GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); + if (baseField != null) + yield return baseField; + } + } + } + } + #endregion + + #region GetDerivedMember + /// + /// Finds the member declared in 'derivedType' that has the same signature (could override) 'baseMember'. + /// + public static IMember GetDerivedMember(IMember baseMember, ITypeDefinition derivedType) + { + if (baseMember == null) + throw new ArgumentNullException("baseMember"); + baseMember = baseMember.MemberDefinition; + IMethod method = baseMember as IMethod; + if (method != null) { + foreach (IMethod derivedMethod in derivedType.Methods) { + if (derivedMethod.Name == method.Name && derivedMethod.Parameters.Count == method.Parameters.Count) { + if (derivedMethod.TypeParameters.Count == method.TypeParameters.Count) { + // The method could override the base method: + if (FindReferenceService.GetBaseMembers(derivedMethod, true).Any(m => m.MemberDefinition == baseMember)) + return derivedMethod; + } + } + } + } + IProperty property = baseMember as IProperty; + if (property != null) { + foreach (IProperty derivedProperty in derivedType.Properties) { + if (derivedProperty.Name == property.Name && derivedProperty.Parameters.Count == property.Parameters.Count) { + // The property could override the base property: + if (FindReferenceService.GetBaseMembers(derivedProperty, true).Any(m => m.MemberDefinition == baseMember)) + return derivedProperty; + } + } + } + if (baseMember is IEvent) { + foreach (IEvent derivedEvent in derivedType.Events) { + if (derivedEvent.Name == baseMember.Name) + return derivedEvent; + } + } + if (baseMember is IField) { + foreach (IField derivedField in derivedType.Fields) { + if (derivedField.Name == baseMember.Name) + return derivedField; + } + } + return null; + } + #endregion } public class SymbolSearchArgs diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/GoToMemberAction.cs b/src/Main/Base/Project/Src/Services/RefactoringService/GoToMemberAction.cs index 96853f7f48..04bfdf4d39 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/GoToMemberAction.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/GoToMemberAction.cs @@ -2,10 +2,11 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop.Parser; namespace ICSharpCode.SharpDevelop.Refactoring { - /* public class GoToMemberAction : IContextAction { public string Title { get; private set; } @@ -18,7 +19,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring if (member == null) throw new ArgumentNullException("member"); this.Member = member; - this.Title = ambience.Convert(member); + this.Title = ambience.ConvertEntity(member, ParserService.CurrentTypeResolveContext); } public void Execute() @@ -26,5 +27,4 @@ namespace ICSharpCode.SharpDevelop.Refactoring NavigationService.NavigateTo(this.Member); } } - */ } diff --git a/src/Main/Base/Project/Src/Util/ExtensionMethods.cs b/src/Main/Base/Project/Src/Util/ExtensionMethods.cs index 1f5ce4902d..96666e2a0d 100644 --- a/src/Main/Base/Project/Src/Util/ExtensionMethods.cs +++ b/src/Main/Base/Project/Src/Util/ExtensionMethods.cs @@ -72,6 +72,7 @@ namespace ICSharpCode.SharpDevelop /// /// Runs an action for all elements in the input. /// + [Obsolete("Please use a regular foreach loop instead. ForEach() is executed for its side-effects, and side-effects mix poorly with a functional programming style.")] public static void ForEach(this IEnumerable input, Action action) { if (input == null)