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));
}
}
}