From ce31426dcb3b1f98343cf6137ccce30059d63226 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 18 Apr 2006 15:49:32 +0000 Subject: [PATCH] Applied patch by Christian Hornung: Fixes for refactoring indexer expressions git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/2.0@1320 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Project/Src/Dom/LanguageProperties.cs | 15 ++++ .../RefactoringMenuBuilder.cs | 44 ++++++---- .../RefactoringService/RefactoringService.cs | 84 +++++++++++++++---- .../Commands/ClassMemberMenuBuilder.cs | 13 ++- 4 files changed, 122 insertions(+), 34 deletions(-) diff --git a/src/Main/Base/Project/Src/Dom/LanguageProperties.cs b/src/Main/Base/Project/Src/Dom/LanguageProperties.cs index 4564e38ffd..8d0954479a 100644 --- a/src/Main/Base/Project/Src/Dom/LanguageProperties.cs +++ b/src/Main/Base/Project/Src/Dom/LanguageProperties.cs @@ -115,6 +115,15 @@ namespace ICSharpCode.SharpDevelop.Dom } } + /// + /// Gets the token that denotes a possible beginning of an indexer expression. + /// + public virtual string IndexerExpressionStartToken { + get { + return "["; + } + } + public virtual bool ShowInNamespaceCompletion(IClass c) { return true; @@ -187,6 +196,12 @@ namespace ICSharpCode.SharpDevelop.Dom } } + public override string IndexerExpressionStartToken { + get { + return "("; + } + } + public override bool ShowInNamespaceCompletion(IClass c) { foreach (IAttribute attr in c.Attributes) { diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs index 66e2b437f5..7149a8c4fc 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs @@ -67,22 +67,35 @@ namespace ICSharpCode.SharpDevelop.Refactoring } // Include menu for member that has been clicked on - ResolveResult rr = ResolveAtCaret(textEditorControl, textArea); + IExpressionFinder expressionFinder = ParserService.GetExpressionFinder(textEditorControl.FileName); + ExpressionResult expressionResult; + ResolveResult rr; + int insertIndex = resultItems.Count; // Insert items at this position to get the outermost expression first, followed by the inner expressions (if any). + expressionResult = FindFullExpressionAtCaret(textArea, expressionFinder); + repeatResolve: + rr = ResolveExpressionAtCaret(textArea, expressionResult); item = null; if (rr is MethodResolveResult) { item = MakeItem(definitions, ((MethodResolveResult)rr).GetMethodIfSingleOverload()); } else if (rr is MemberResolveResult) { - item = MakeItem(definitions, ((MemberResolveResult)rr).ResolvedMember); + MemberResolveResult mrr = (MemberResolveResult)rr; + item = MakeItem(definitions, mrr.ResolvedMember); + if (RefactoringService.FixIndexerExpression(expressionFinder, ref expressionResult, mrr)) { + if (item != null) { + resultItems.Insert(insertIndex, item); + } + // Include menu for the underlying expression of the + // indexer expression as well. + goto repeatResolve; + } } else if (rr is TypeResolveResult) { item = MakeItem(definitions, ((TypeResolveResult)rr).ResolvedClass); } else if (rr is LocalResolveResult) { item = MakeItem((LocalResolveResult)rr, caretLine + 1 == ((LocalResolveResult)rr).Field.Region.BeginLine); + insertIndex = 0; // Insert local variable menu item at the topmost position. } if (item != null) { - if (rr is LocalResolveResult) - resultItems.Insert(0, item); - else - resultItems.Add(item); + resultItems.Insert(insertIndex, item); } // Include menu for current class and method @@ -200,16 +213,19 @@ namespace ICSharpCode.SharpDevelop.Refactoring return item; } - ResolveResult ResolveAtCaret(TextEditorControl textEditorControl, TextArea textArea) + static ExpressionResult FindFullExpressionAtCaret(TextArea textArea, IExpressionFinder expressionFinder) + { + if (expressionFinder != null) { + return expressionFinder.FindFullExpression(textArea.Document.TextContent, textArea.Caret.Offset); + } else { + return new ExpressionResult(null); + } + } + + static ResolveResult ResolveExpressionAtCaret(TextArea textArea, ExpressionResult expressionResult) { - IExpressionFinder expressionFinder = ParserService.GetExpressionFinder(textEditorControl.FileName); - if (expressionFinder == null) - return null; - IDocument doc = textArea.Document; - string textContent = doc.TextContent; - ExpressionResult expressionResult = expressionFinder.FindFullExpression(textContent, textArea.Caret.Offset); if (expressionResult.Expression != null) { - return ParserService.Resolve(expressionResult, textArea.Caret.Line + 1, textArea.Caret.Column + 1, textArea.MotherTextEditorControl.FileName, textContent); + return ParserService.Resolve(expressionResult, textArea.Caret.Line + 1, textArea.Caret.Column + 1, textArea.MotherTextEditorControl.FileName, textArea.Document.TextContent); } return null; } diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs index a3ececceda..1d452610e0 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringService.cs @@ -149,6 +149,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring { string lowerFileContent = fileContent.ToLower(); string searchedText; // the text that is searched for + bool searchingIndexer = false; if (member == null) { searchedText = parentClass.Name.ToLower(); @@ -158,28 +159,40 @@ namespace ICSharpCode.SharpDevelop.Refactoring // (examples: derived classes, partial classes) if (member is IMethod && ((IMethod)member).IsConstructor) searchedText = parentClass.Name.ToLower(); - else - searchedText = member.Name.ToLower(); + else { + if (member is IProperty && ((IProperty)member).IsIndexer) { + searchingIndexer = true; + searchedText = GetIndexerExpressionStartToken(fileName); + } else { + searchedText = member.Name.ToLower(); + } + } } int pos = -1; + int exprPos; IExpressionFinder expressionFinder = null; while ((pos = lowerFileContent.IndexOf(searchedText, pos + 1)) >= 0) { - if (pos > 0 && char.IsLetterOrDigit(fileContent, pos - 1)) { - continue; // memberName is not a whole word (a.SomeName cannot reference Name) - } - if (pos < fileContent.Length - searchedText.Length - 1 - && char.IsLetterOrDigit(fileContent, pos + searchedText.Length)) - { - continue; // memberName is not a whole word (a.Name2 cannot reference Name) + if (!searchingIndexer) { + if (pos > 0 && char.IsLetterOrDigit(fileContent, pos - 1)) { + continue; // memberName is not a whole word (a.SomeName cannot reference Name) + } + if (pos < fileContent.Length - searchedText.Length - 1 + && char.IsLetterOrDigit(fileContent, pos + searchedText.Length)) + { + continue; // memberName is not a whole word (a.Name2 cannot reference Name) + } + exprPos = pos; + } else { + exprPos = pos-1; // indexer expressions are found by resolving the part before the indexer } if (expressionFinder == null) { expressionFinder = ParserService.GetExpressionFinder(fileName); } - ExpressionResult expr = expressionFinder.FindFullExpression(fileContent, pos); + ExpressionResult expr = expressionFinder.FindFullExpression(fileContent, exprPos); if (expr.Expression != null) { - Point position = GetPosition(fileContent, pos); + Point position = GetPosition(fileContent, exprPos); repeatResolve: // TODO: Optimize by re-using the same resolver if multiple expressions were // found in this file (the resolver should parse all methods at once) @@ -189,18 +202,14 @@ namespace ICSharpCode.SharpDevelop.Refactoring // find reference to local variable if (IsReferenceToLocalVariable(rr, member)) { list.Add(new Reference(fileName, pos, searchedText.Length, expr.Expression, rr)); + } else if (FixIndexerExpression(expressionFinder, ref expr, mrr)) { + goto repeatResolve; } } else if (member != null) { // find reference to member if (IsReferenceToMember(member, rr)) { list.Add(new Reference(fileName, pos, searchedText.Length, expr.Expression, rr)); - } else if (mrr != null && mrr.ResolvedMember is IProperty && ((IProperty)mrr.ResolvedMember).IsIndexer) { - // we got an indexer call as expression ("objectList[0].ToString()[2]") - // strip the index from the expression to resolve the underlying expression - string newExpr = expressionFinder.RemoveLastPart(expr.Expression); - if (newExpr.Length >= expr.Expression.Length) - throw new ApplicationException("new expression must be shorter than old expression"); - expr.Expression = newExpr; + } else if (FixIndexerExpression(expressionFinder, ref expr, mrr)) { goto repeatResolve; } } else { @@ -221,6 +230,45 @@ namespace ICSharpCode.SharpDevelop.Refactoring } } + /// + /// Makes the given ExpressionResult point to the underlying expression if + /// the expression is an indexer expression. + /// + /// true, if the expression was an indexer expression and has been changed, false otherwise. + public static bool FixIndexerExpression(IExpressionFinder expressionFinder, ref ExpressionResult expr, MemberResolveResult mrr) + { + if (mrr != null && mrr.ResolvedMember is IProperty && ((IProperty)mrr.ResolvedMember).IsIndexer) { + // we got an indexer call as expression ("objectList[0].ToString()[2]") + // strip the index from the expression to resolve the underlying expression + string newExpr = expressionFinder.RemoveLastPart(expr.Expression); + if (newExpr.Length >= expr.Expression.Length) { + throw new ApplicationException("new expression must be shorter than old expression"); + } + expr.Expression = newExpr; + return true; + } + return false; + } + + /// + /// Determines the token that denotes a possible beginning of an indexer + /// expression in the specified file. + /// + static string GetIndexerExpressionStartToken(string fileName) + { + if (fileName != null) { + ParseInformation pi = ParserService.GetParseInformation(fileName); + if (pi != null && + pi.MostRecentCompilationUnit != null && + pi.MostRecentCompilationUnit.ProjectContent != null && + pi.MostRecentCompilationUnit.ProjectContent.Language != null) { + return pi.MostRecentCompilationUnit.ProjectContent.Language.IndexerExpressionStartToken; + } + } + LoggingService.Warn("RefactoringService: unable to determine the correct indexer expression start token for file '"+fileName+"'"); + return LanguageProperties.CSharp.IndexerExpressionStartToken; + } + static Point GetPosition(string fileContent, int pos) { int line = 1; diff --git a/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs b/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs index e4fb0890eb..a01ba7a29a 100644 --- a/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs +++ b/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs @@ -46,7 +46,8 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands && !FindReferencesAndRenameHelper.IsReadOnly(member.DeclaringType); if (method == null || !method.IsConstructor) { - if (!FindReferencesAndRenameHelper.IsReadOnly(member.DeclaringType)) { + if (!FindReferencesAndRenameHelper.IsReadOnly(member.DeclaringType) && + !(member is IProperty && ((IProperty)member).IsIndexer)) { cmd = new MenuCommand("${res:SharpDevelop.Refactoring.RenameCommand}", Rename); cmd.Tag = member; list.Add(cmd); @@ -223,7 +224,15 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands { MenuCommand item = (MenuCommand)sender; IMember member = (IMember)item.Tag; - FindReferencesAndRenameHelper.ShowAsSearchResults("References to " + member.Name, RefactoringService.FindReferences(member, null)); + string memberName; + if (member is IProperty && ((IProperty)member).IsIndexer) { + // The name of the default indexer is always "Indexer" in C#. + // Add the type name to clarify which indexer is referred to. + memberName = member.Name + " of " + member.DeclaringType.Name; + } else { + memberName = member.Name; + } + FindReferencesAndRenameHelper.ShowAsSearchResults("References to " + memberName, RefactoringService.FindReferences(member, null)); } } }