diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 297144498..88ccd41d4 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -211,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 ec83e695c..25a878d13 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/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 cfbd5751a..f143ea371 100644 --- a/ICSharpCode.Decompiler/ITextOutput.cs +++ b/ICSharpCode.Decompiler/ITextOutput.cs @@ -31,7 +31,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 d8b001167..c520b0f50 100644 --- a/ICSharpCode.Decompiler/PlainTextOutput.cs +++ b/ICSharpCode.Decompiler/PlainTextOutput.cs @@ -99,7 +99,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.SharpDevelop.LGPL/AvalonEdit/TextMarkerService.cs b/ILSpy.SharpDevelop.LGPL/AvalonEdit/TextMarkerService.cs index 40abc720a..2750dc0e1 100644 --- a/ILSpy.SharpDevelop.LGPL/AvalonEdit/TextMarkerService.cs +++ b/ILSpy.SharpDevelop.LGPL/AvalonEdit/TextMarkerService.cs @@ -123,7 +123,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; @@ -166,7 +166,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 996e182bc..6ce170d2a 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; } /// @@ -204,17 +206,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 0c16ac2ea..96283611f 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 = null; @@ -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; } }