From c42bf520b8f2f9923ec46d9282cbd5adbd77771a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20Zgodzi=C5=84ski?= Date: Sun, 5 Jun 2011 17:28:03 +0200 Subject: [PATCH] Highlighting references to clicked variable or parameter. --- .../Ast/AstMethodBodyBuilder.cs | 3 +- .../Ast/TextOutputFormatter.cs | 61 +++++++++++++++++-- .../Ast/Transforms/DeclareVariables.cs | 40 +++++++----- .../Ast/Transforms/DelegateConstruction.cs | 17 ++++-- .../Transforms/PatternStatementTransform.cs | 13 ++-- ICSharpCode.Decompiler/ITextOutput.cs | 2 +- ICSharpCode.Decompiler/PlainTextOutput.cs | 2 +- ILSpy/AvalonEdit/TextMarkerService.cs | 4 +- ILSpy/TextView/AvalonEditTextOutput.cs | 9 ++- ILSpy/TextView/DecompilerTextView.cs | 28 ++++++++- ILSpy/TextView/ReferenceElementGenerator.cs | 5 +- 11 files changed, 144 insertions(+), 40 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 1958b083a..6e5f5ff99 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -128,6 +128,7 @@ namespace ICSharpCode.Decompiler.Ast else type = AstBuilder.ConvertType(v.Type); var newVarDecl = new VariableDeclarationStatement(type, v.Name); + newVarDecl.Variables.Single().AddAnnotation(v); astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); } @@ -210,7 +211,7 @@ namespace ICSharpCode.Decompiler.Ast Type = AstBuilder.ConvertType(catchClause.ExceptionType), VariableName = catchClause.ExceptionVariable == null ? null : catchClause.ExceptionVariable.Name, Body = TransformBlock(catchClause) - }); + }.WithAnnotation(catchClause.ExceptionVariable)); } } if (tryCatchNode.FinallyBlock != null) diff --git a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs index 7c8ca1230..a866cf8e3 100644 --- a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs +++ b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs @@ -43,12 +43,26 @@ namespace ICSharpCode.Decompiler.Ast public void WriteIdentifier(string identifier) { - MemberReference memberRef = GetCurrentMemberReference(); - - if (memberRef != null) + object memberRef = GetCurrentMemberReference(); + + if (memberRef != null) { output.WriteReference(identifier, memberRef); - else - output.Write(identifier); + return; + } + + var definition = GetCurrentLocalDefinition(); + if (definition != null) { + output.WriteDefinition(identifier, definition); + return; + } + + memberRef = GetCurrentLocalReference(); + if (memberRef != null) { + output.WriteReference(identifier, memberRef, true); + return; + } + + output.Write(identifier); } MemberReference GetCurrentMemberReference() @@ -60,6 +74,43 @@ namespace ICSharpCode.Decompiler.Ast } return memberRef; } + + object GetCurrentLocalReference() + { + AstNode node = nodeStack.Peek(); + ILVariable variable = node.Annotation(); + if (variable != null) { + if (variable.OriginalParameter != null) + return variable.OriginalParameter; + //if (variable.OriginalVariable != null) + // return variable.OriginalVariable; + return variable; + } + return null; + } + + object GetCurrentLocalDefinition() + { + AstNode node = nodeStack.Peek(); + var parameterDef = node.Annotation(); + if (parameterDef != null) + return parameterDef; + + if (node is VariableInitializer || node is CatchClause || node is ForeachStatement) { + var variable = node.Annotation(); + if (variable != null) { + if (variable.OriginalParameter != null) + return variable.OriginalParameter; + //if (variable.OriginalVariable != null) + // return variable.OriginalVariable; + return variable; + } else { + + } + } + + return null; + } public void WriteKeyword(string keyword) { diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs index ed2144085..db11f488f 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; +using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Analysis; @@ -35,6 +36,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms { public AstType Type; public string Name; + public ILVariable Variable; public AssignmentExpression ReplacedAssignment; public Statement InsertionPoint; @@ -57,9 +59,12 @@ namespace ICSharpCode.Decompiler.Ast.Transforms foreach (var v in variablesToDeclare) { if (v.ReplacedAssignment == null) { BlockStatement block = (BlockStatement)v.InsertionPoint.Parent; + var vds = new VariableDeclarationStatement((AstType)v.Type.Clone(), v.Name); + if(v.Variable != null) + vds.Variables.First().AddAnnotation(v.Variable); block.Statements.InsertBefore( v.InsertionPoint, - new VariableDeclarationStatement((AstType)v.Type.Clone(), v.Name)); + vds); } } // First do all the insertions, then do all the replacements. This is necessary because a replacement might remove our reference point from the AST. @@ -67,9 +72,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms if (v.ReplacedAssignment != null) { // We clone the right expression so that it doesn't get removed from the old ExpressionStatement, // which might be still in use by the definite assignment graph. + var variableInit = new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()) + .CopyAnnotationsFrom(v.ReplacedAssignment) + .CopyAnnotationsFrom(v.ReplacedAssignment.Left) + .WithAnnotation(v.Variable); VariableDeclarationStatement varDecl = new VariableDeclarationStatement { Type = (AstType)v.Type.Clone(), - Variables = { new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment) } + Variables = { variableInit } }; ExpressionStatement es = v.ReplacedAssignment.Parent as ExpressionStatement; if (es != null) { @@ -102,7 +111,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms foreach (VariableDeclarationStatement varDecl in variables) { string variableName = varDecl.Variables.Single().Name; bool allowPassIntoLoops = varDecl.Variables.Single().Annotation() == null; - DeclareVariableInBlock(daa, block, varDecl.Type, variableName, allowPassIntoLoops); + ILVariable ilVariable = varDecl.Variables.Single().Annotation(); + DeclareVariableInBlock(daa, block, varDecl.Type, variableName, allowPassIntoLoops, ilVariable); } } } @@ -111,7 +121,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } } - void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, bool allowPassIntoLoops) + void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, bool allowPassIntoLoops, ILVariable ilVariable) { // declarationPoint: The point where the variable would be declared, if we decide to declare it in this block Statement declarationPoint = null; @@ -127,52 +137,52 @@ namespace ICSharpCode.Decompiler.Ast.Transforms ForStatement forStmt = stmt as ForStatement; if (forStmt != null && forStmt.Initializers.Count == 1) { // handle the special case of moving a variable into the for initializer - if (TryConvertAssignmentExpressionIntoVariableDeclaration(forStmt.Initializers.Single(), type, variableName)) + if (TryConvertAssignmentExpressionIntoVariableDeclaration(forStmt.Initializers.Single(), type, variableName, ilVariable)) continue; } UsingStatement usingStmt = stmt as UsingStatement; if (usingStmt != null && usingStmt.ResourceAcquisition is AssignmentExpression) { // handle the special case of moving a variable into a using statement - if (TryConvertAssignmentExpressionIntoVariableDeclaration((Expression)usingStmt.ResourceAcquisition, type, variableName)) + if (TryConvertAssignmentExpressionIntoVariableDeclaration((Expression)usingStmt.ResourceAcquisition, type, variableName, ilVariable)) continue; } foreach (AstNode child in stmt.Children) { BlockStatement subBlock = child as BlockStatement; if (subBlock != null) { - DeclareVariableInBlock(daa, subBlock, type, variableName, allowPassIntoLoops); + DeclareVariableInBlock(daa, subBlock, type, variableName, allowPassIntoLoops, ilVariable); } else if (HasNestedBlocks(child)) { foreach (BlockStatement nestedSubBlock in child.Children.OfType()) { - DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, allowPassIntoLoops); + DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, allowPassIntoLoops, ilVariable); } } } } } else { // Try converting an assignment expression into a VariableDeclarationStatement - if (!TryConvertAssignmentExpressionIntoVariableDeclaration(declarationPoint, type, variableName)) { + if (!TryConvertAssignmentExpressionIntoVariableDeclaration(declarationPoint, type, variableName, ilVariable)) { // Declare the variable in front of declarationPoint - variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, InsertionPoint = declarationPoint }); + variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, InsertionPoint = declarationPoint, Variable = ilVariable }); } } } - bool TryConvertAssignmentExpressionIntoVariableDeclaration(Statement declarationPoint, AstType type, string variableName) + bool TryConvertAssignmentExpressionIntoVariableDeclaration(Statement declarationPoint, AstType type, string variableName, ILVariable ilVariable) { // convert the declarationPoint into a VariableDeclarationStatement ExpressionStatement es = declarationPoint as ExpressionStatement; if (es != null) { - return TryConvertAssignmentExpressionIntoVariableDeclaration(es.Expression, type, variableName); + return TryConvertAssignmentExpressionIntoVariableDeclaration(es.Expression, type, variableName, ilVariable); } return false; } - - bool TryConvertAssignmentExpressionIntoVariableDeclaration(Expression expression, AstType type, string variableName) + + bool TryConvertAssignmentExpressionIntoVariableDeclaration(Expression expression, AstType type, string variableName, ILVariable ilVariable) { AssignmentExpression ae = expression as AssignmentExpression; if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { IdentifierExpression ident = ae.Left as IdentifierExpression; if (ident != null && ident.Identifier == variableName) { - variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ReplacedAssignment = ae }); + variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ReplacedAssignment = ae, Variable = ilVariable }); return true; } } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 0e431e3f8..17cca7650 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -386,7 +386,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } // Now create variables for all fields of the display class (except for those that we already handled as parameters) - List> variablesToDeclare = new List>(); + List> variablesToDeclare = new List>(); foreach (FieldDefinition field in type.Fields) { if (field.IsStatic) continue; // skip static fields @@ -397,8 +397,14 @@ namespace ICSharpCode.Decompiler.Ast.Transforms capturedVariableName = capturedVariableName.Substring(10); EnsureVariableNameIsAvailable(blockStatement, capturedVariableName); currentlyUsedVariableNames.Add(capturedVariableName); - variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), capturedVariableName)); - dict[field] = new IdentifierExpression(capturedVariableName); + ILVariable ilVar = new ILVariable + { + IsGenerated = true, + Name = capturedVariableName, + Type = field.FieldType, + }; + variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), ilVar)); + dict[field] = new IdentifierExpression(capturedVariableName).WithAnnotation(ilVar); } // Now figure out where the closure was accessed and use the simpler replacement expression there: @@ -414,15 +420,16 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works): Statement insertionPoint = blockStatement.Statements.FirstOrDefault(); foreach (var tuple in variablesToDeclare) { - var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2); + var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2.Name); newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation()); + newVarDecl.Variables.Single().AddAnnotation(tuple.Item2); blockStatement.Statements.InsertBefore(insertionPoint, newVarDecl); } } currentlyUsedVariableNames.RemoveRange(numberOfVariablesOutsideBlock, currentlyUsedVariableNames.Count - numberOfVariablesOutsideBlock); return null; } - + void EnsureVariableNameIsAvailable(AstNode currentNode, string name) { int pos = currentlyUsedVariableNames.IndexOf(name); diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index 3d5c0e596..900ef0b4f 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -227,6 +227,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms Name = variableName, Initializer = m1.Get("initializer").Single().Detach() }.CopyAnnotationsFrom(node.Expression) + .WithAnnotation(m1.Get("variable").Single().Annotation()) } }.CopyAnnotationsFrom(node); } else { @@ -382,7 +383,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms VariableName = itemVar.Identifier, InExpression = m.Get("collection").Single().Detach(), EmbeddedStatement = newBody - }; + }.WithAnnotation(itemVarDecl.Variables.Single().Annotation()); if (foreachStatement.InExpression is BaseReferenceExpression) { foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression); } @@ -471,10 +472,12 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // We just care that we can move it in front of the loop: if (declarationPoint != loop) return null; - - ForeachStatement foreachStatement = new ForeachStatement(); - foreachStatement.VariableType = itemVarDecl.Type.Clone(); - foreachStatement.VariableName = itemVar.Identifier; + + ForeachStatement foreachStatement = new ForeachStatement + { + VariableType = itemVarDecl.Type.Clone(), + VariableName = itemVar.Identifier, + }.WithAnnotation(itemVarDecl.Variables.Single().Annotation()); BlockStatement body = new BlockStatement(); foreachStatement.EmbeddedStatement = body; ((BlockStatement)node.Parent).Statements.InsertBefore(node, foreachStatement); diff --git a/ICSharpCode.Decompiler/ITextOutput.cs b/ICSharpCode.Decompiler/ITextOutput.cs index 608a45c64..9c030862a 100644 --- a/ICSharpCode.Decompiler/ITextOutput.cs +++ b/ICSharpCode.Decompiler/ITextOutput.cs @@ -32,7 +32,7 @@ namespace ICSharpCode.Decompiler void Write(string text); void WriteLine(); void WriteDefinition(string text, object definition); - void WriteReference(string text, object reference); + void WriteReference(string text, object reference, bool isLocal = false); void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false); void MarkFoldEnd(); diff --git a/ICSharpCode.Decompiler/PlainTextOutput.cs b/ICSharpCode.Decompiler/PlainTextOutput.cs index 86a28a5c8..1ddb6a924 100644 --- a/ICSharpCode.Decompiler/PlainTextOutput.cs +++ b/ICSharpCode.Decompiler/PlainTextOutput.cs @@ -104,7 +104,7 @@ namespace ICSharpCode.Decompiler Write(text); } - public void WriteReference(string text, object reference) + public void WriteReference(string text, object reference, bool isLocal) { Write(text); } diff --git a/ILSpy/AvalonEdit/TextMarkerService.cs b/ILSpy/AvalonEdit/TextMarkerService.cs index caffc37ac..fd67cb22e 100644 --- a/ILSpy/AvalonEdit/TextMarkerService.cs +++ b/ILSpy/AvalonEdit/TextMarkerService.cs @@ -124,7 +124,7 @@ namespace ICSharpCode.ILSpy.AvalonEdit int lineStart = line.Offset; int lineEnd = lineStart + line.Length; foreach (TextMarker marker in markers.FindOverlappingSegments(lineStart, line.Length).Reverse()) { - if (!marker.IsVisible(marker.Bookmark)) + if (marker.Bookmark != null && !marker.IsVisible(marker.Bookmark)) continue; Brush foregroundBrush = null; @@ -167,7 +167,7 @@ namespace ICSharpCode.ILSpy.AvalonEdit int viewStart = visualLines.First().FirstDocumentLine.Offset; int viewEnd = visualLines.Last().LastDocumentLine.Offset + visualLines.Last().LastDocumentLine.Length; foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart).Reverse()) { - if (!marker.IsVisible(marker.Bookmark)) + if (marker.Bookmark != null && !marker.IsVisible(marker.Bookmark)) continue; if (marker.BackgroundColor != null) { diff --git a/ILSpy/TextView/AvalonEditTextOutput.cs b/ILSpy/TextView/AvalonEditTextOutput.cs index 7b247ddb9..7aeedfed3 100644 --- a/ILSpy/TextView/AvalonEditTextOutput.cs +++ b/ILSpy/TextView/AvalonEditTextOutput.cs @@ -36,6 +36,8 @@ namespace ICSharpCode.ILSpy.TextView sealed class ReferenceSegment : TextSegment { public object Reference; + public bool IsLocal; + public bool IsLocalTarget; } /// @@ -197,17 +199,20 @@ namespace ICSharpCode.ILSpy.TextView public void WriteDefinition(string text, object definition) { WriteIndent(); + int start = this.TextLength; b.Append(text); + int end = this.TextLength; this.DefinitionLookup.AddDefinition(definition, this.TextLength); + references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = definition, IsLocal = true, IsLocalTarget = true }); } - public void WriteReference(string text, object reference) + public void WriteReference(string text, object reference, bool isLocal) { WriteIndent(); int start = this.TextLength; b.Append(text); int end = this.TextLength; - references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = reference }); + references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = reference, IsLocal = isLocal }); } public void MarkFoldStart(string collapsedText, bool defaultCollapsed) diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index fa2a9ce8c..cea5e1749 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -29,6 +29,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; +using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Threading; using System.Xml; @@ -68,11 +69,13 @@ namespace ICSharpCode.ILSpy.TextView ILSpyTreeNode[] decompiledNodes; DefinitionLookup definitionLookup; + TextSegmentCollection references; CancellationTokenSource currentCancellationTokenSource; internal readonly IconBarManager manager; readonly IconBarMargin iconMargin; readonly TextMarkerService textMarkerService; + readonly List localReferenceMarks = new List(); [ImportMany(typeof(ITextEditorListener))] IEnumerable textEditorListeners; @@ -315,7 +318,8 @@ namespace ICSharpCode.ILSpy.TextView { Debug.WriteLine("Showing {0} characters of output", textOutput.TextLength); Stopwatch w = Stopwatch.StartNew(); - + + ClearLocalReferenceMarks(); textEditor.ScrollToHome(); if (foldingManager != null) { FoldingManager.Uninstall(foldingManager); @@ -324,6 +328,7 @@ namespace ICSharpCode.ILSpy.TextView textEditor.Document = null; // clear old document while we're changing the highlighting uiElementGenerator.UIElements = textOutput.UIElements; referenceElementGenerator.References = textOutput.References; + references = textOutput.References; definitionLookup = textOutput.DefinitionLookup; textEditor.SyntaxHighlighting = highlighting; @@ -609,6 +614,19 @@ namespace ICSharpCode.ILSpy.TextView internal void JumpToReference(ReferenceSegment referenceSegment) { object reference = referenceSegment.Reference; + if (referenceSegment.IsLocal) { + ClearLocalReferenceMarks(); + if (references != null) { + foreach (var r in references) { + if (r.Reference == reference) { + var mark = textMarkerService.Create(r.StartOffset, r.Length); + mark.BackgroundColor = r.IsLocalTarget ? Colors.LightSeaGreen : Colors.GreenYellow; + localReferenceMarks.Add(mark); + } + } + } + return; + } if (definitionLookup != null) { int pos = definitionLookup.GetDefinitionPosition(reference); if (pos >= 0) { @@ -624,6 +642,14 @@ namespace ICSharpCode.ILSpy.TextView } MainWindow.Instance.JumpToReference(reference); } + + void ClearLocalReferenceMarks() + { + foreach (var mark in localReferenceMarks) { + textMarkerService.Remove(mark); + } + localReferenceMarks.Clear(); + } /// /// Filters all ReferenceSegments that are no real links. diff --git a/ILSpy/TextView/ReferenceElementGenerator.cs b/ILSpy/TextView/ReferenceElementGenerator.cs index e83d89f64..330bdaa0c 100644 --- a/ILSpy/TextView/ReferenceElementGenerator.cs +++ b/ILSpy/TextView/ReferenceElementGenerator.cs @@ -102,7 +102,7 @@ namespace ICSharpCode.ILSpy.TextView protected override void OnQueryCursor(QueryCursorEventArgs e) { e.Handled = true; - e.Cursor = Cursors.Hand; + e.Cursor = referenceSegment.IsLocal ? Cursors.Arrow : Cursors.Hand; } /// @@ -110,7 +110,8 @@ namespace ICSharpCode.ILSpy.TextView { if (e.ChangedButton == MouseButton.Left && !e.Handled) { parent.JumpToReference(referenceSegment); - e.Handled = true; + if(!referenceSegment.IsLocal) + e.Handled = true; } }