diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
index a5e2d752b..9eac3d0bd 100644
--- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
@@ -1465,8 +1465,27 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitIfInstruction(IfInstruction inst, TranslationContext context)
{
var condition = TranslateCondition(inst.Condition);
- var trueBranch = Translate(inst.TrueInst);
var falseBranch = Translate(inst.FalseInst);
+ if (falseBranch.Type.IsKnownType(KnownTypeCode.Boolean)) {
+ if (inst.TrueInst.MatchLdcI4(1)) {
+ // "a ? true : b" ==> "a || b"
+ return new BinaryOperatorExpression(
+ condition,
+ BinaryOperatorType.ConditionalOr,
+ falseBranch)
+ .WithILInstruction(inst)
+ .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean)));
+ } else if (inst.TrueInst.MatchLdcI4(0)) {
+ // "a ? false : b" ==> "!a && b"
+ return new BinaryOperatorExpression(
+ LogicNot(condition),
+ BinaryOperatorType.ConditionalAnd,
+ falseBranch)
+ .WithILInstruction(inst)
+ .WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean)));
+ }
+ }
+ var trueBranch = Translate(inst.TrueInst);
IType targetType;
if (!trueBranch.Type.Equals(SpecialType.NullType) && !falseBranch.Type.Equals(SpecialType.NullType) && !trueBranch.Type.Equals(falseBranch.Type)) {
targetType = compilation.FindType(inst.ResultType.ToKnownTypeCode());
diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
index 0b1caa4e2..7c59cfdf7 100644
--- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
+++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
@@ -32,7 +32,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// Blocks should be basic blocks prior to this transform.
/// After this transform, they will be extended basic blocks.
///
- public class ConditionDetection : IILTransform
+ public class ConditionDetection : IILTransform, ISingleStep
{
public void Run(ILFunction function, ILTransformContext context)
{
@@ -41,11 +41,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
+ public int MaxStepCount { get; set; } = int.MaxValue;
+ Stepper stepper;
+
BlockContainer currentContainer;
ControlFlowNode[] controlFlowGraph;
void Run(BlockContainer container, ILTransformContext context)
{
+ stepper = new Stepper(MaxStepCount);
currentContainer = container;
controlFlowGraph = LoopDetection.BuildCFG(container);
Dominance.ComputeDominance(controlFlowGraph[0], context.CancellationToken);
@@ -54,7 +58,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
currentContainer = null;
container.Blocks.RemoveAll(b => b.Parent != container || b.Instructions.Count == 0);
}
-
+
///
/// Builds structured control flow for the block associated with the control flow node.
///
@@ -93,6 +97,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
block.Instructions.RemoveAt(block.Instructions.Count - 1);
block.Instructions.AddRange(targetBlock.Instructions);
targetBlock.Instructions.Clear();
+ stepper.Stepped();
}
}
@@ -106,6 +111,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
ifInst.TrueInst = exitInst;
exitInst = block.Instructions.Last();
ifInst.Condition = new LogicNot(ifInst.Condition);
+ stepper.Stepped();
}
ILInstruction trueExitInst;
@@ -122,6 +128,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
targetBlock.Instructions.RemoveAt(targetBlock.Instructions.Count - 1);
trueExitInst = null;
}
+ stepper.Stepped();
} else {
trueExitInst = ifInst.TrueInst;
}
@@ -142,6 +149,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
Debug.Assert(trueExitInst == ifInst.TrueInst);
ifInst.TrueInst = new Nop { ILRange = ifInst.TrueInst.ILRange };
}
+ stepper.Stepped();
}
}
if (ifInst.FalseInst.OpCode != OpCode.Nop && ifInst.FalseInst.ILRange.Start < ifInst.TrueInst.ILRange.Start
@@ -151,6 +159,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
ifInst.TrueInst = ifInst.FalseInst;
ifInst.FalseInst = oldTrue;
ifInst.Condition = new LogicNot(ifInst.Condition);
+ stepper.Stepped();
}
}
diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
index 39472bce2..8f5af702e 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
@@ -77,12 +77,10 @@ namespace ICSharpCode.Decompiler.IL
///
/// Apply a list of transforms to this function.
///
- public void RunTransforms(IEnumerable transforms, ILTransformContext context, Func stopTransform = null)
+ public void RunTransforms(IEnumerable transforms, ILTransformContext context)
{
foreach (var transform in transforms) {
context.CancellationToken.ThrowIfCancellationRequested();
- if (stopTransform != null && stopTransform(transform))
- break;
transform.Run(this, context);
this.CheckInvariant(ILPhase.Normal);
}
diff --git a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
index 43030cad2..b364f8a25 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
@@ -25,11 +25,14 @@ namespace ICSharpCode.Decompiler.IL
/// If statement / conditional expression. if (condition) trueExpr else falseExpr
///
/// The condition must return StackType.I4, use comparison instructions like Ceq to check if other types are non-zero.
- /// Phase-1 execution of an IfInstruction consists of phase-1 execution of the condition.
- /// Phase-2 execution of an IfInstruction will phase-2-execute the condition.
- /// If the condition evaluates to a non-zero, the TrueInst is executed (both phase-1 and phase-2).
- /// If the condition evaluates to zero, the FalseInst is executed (both phase-1 and phase-2).
+ ///
+ /// If the condition evaluates to non-zero, the TrueInst is executed.
+ /// If the condition evaluates to zero, the FalseInst is executed.
/// The return value of the IfInstruction is the return value of the TrueInst or FalseInst.
+ ///
+ /// IfInstruction is also used to represent logical operators:
+ /// "a || b" ==> if (a) (ldc.i4 1) else (b)
+ /// "a && b" ==> if (logic.not(a)) (ldc.i4 0) else (b)
///
partial class IfInstruction : ILInstruction
{
diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
index 0a7621470..20041a9cb 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
@@ -155,7 +155,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
v.Name = contextPrefix + v.Name;
}
- function.RunTransforms(CSharpDecompiler.GetILTransforms(), context, t => t is DelegateConstruction);
+ function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is DelegateConstruction)), context);
function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter)));
// handle nested lambdas
((IILTransform)new DelegateConstruction()).Run(function, new ILTransformContext { Settings = context.Settings, CancellationToken = context.CancellationToken, TypeSystem = localTypeSystem });
diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
index 585b6287a..8228ec30f 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
@@ -30,10 +30,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
///
/// Should run after inlining so that the expression patterns can be detected.
///
- public class ExpressionTransforms : ILVisitor, IILTransform
+ public class ExpressionTransforms : ILVisitor, IILTransform, ISingleStep
{
+ public int MaxStepCount { get; set; } = int.MaxValue;
+ Stepper stepper;
+
void IILTransform.Run(ILFunction function, ILTransformContext context)
{
+ stepper = new Stepper(MaxStepCount);
function.AcceptVisitor(this);
}
@@ -82,6 +86,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
inst.Kind = ComparisonKind.Inequality;
else if (inst.Kind == ComparisonKind.LessThanOrEqual)
inst.Kind = ComparisonKind.Equality;
+ stepper.Stepped();
}
}
@@ -93,6 +98,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// conv.i4(ldlen array) => ldlen.i4(array)
inst.AddILRange(inst.Argument.ILRange);
inst.ReplaceWith(new LdLen(inst.TargetType.GetStackType(), array) { ILRange = inst.ILRange });
+ stepper.Stepped();
}
}
@@ -107,6 +113,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
arg.AddILRange(inst.ILRange);
arg.AddILRange(inst.Argument.ILRange);
inst.ReplaceWith(arg);
+ stepper.Stepped();
} else if (inst.Argument is Comp) {
Comp comp = (Comp)inst.Argument;
if (comp.InputType != StackType.F || comp.Kind.IsEqualityOrInequality()) {
@@ -114,6 +121,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
comp.Kind = comp.Kind.Negate();
comp.AddILRange(inst.ILRange);
inst.ReplaceWith(comp);
+ stepper.Stepped();
}
}
}
@@ -130,6 +138,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
newObj.Arguments.AddRange(inst.Arguments.Skip(1));
var expr = new StObj(inst.Arguments[0], newObj, inst.Method.DeclaringType);
inst.ReplaceWith(expr);
+ stepper.Stepped();
// Both the StObj and the NewObj may trigger further rules, so continue visiting the replacement:
VisitStObj(expr);
} else {
@@ -142,6 +151,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
LdcDecimal decimalConstant;
if (TransformDecimalCtorToConstant(inst, out decimalConstant)) {
inst.ReplaceWith(decimalConstant);
+ stepper.Stepped();
return;
}
base.VisitNewObj(inst);
@@ -186,6 +196,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// stobj(ldloca(v), ...)
// => stloc(v, ...)
inst.ReplaceWith(new StLoc(v, inst.Value));
+ stepper.Stepped();
}
ILInstruction target;
@@ -195,6 +206,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// stobj(target, binary.op(ldobj(target), ...))
// => compound.op(target, ...)
inst.ReplaceWith(new CompoundAssignmentInstruction(binary.Operator, binary.Left, binary.Right, t, binary.CheckForOverflow, binary.Sign, CompoundAssignmentType.EvaluatesToNewValue));
+ stepper.Stepped();
}
}
@@ -211,7 +223,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILVariable v1, v2;
ILInstruction value1, value2;
if (trueInst.Instructions[0].MatchStLoc(out v1, out value1) && falseInst.Instructions[0].MatchStLoc(out v2, out value2) && v1 == v2) {
- inst.ReplaceWith(new StLoc(v1, new IfInstruction(inst.Condition, value1, value2)));
+ inst.ReplaceWith(new StLoc(v1, new IfInstruction(new LogicNot(inst.Condition), value2, value1)));
+ stepper.Stepped();
}
}
}
diff --git a/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs
index 32ae5cf2d..f1384e303 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs
@@ -21,15 +21,67 @@ using System.Threading;
namespace ICSharpCode.Decompiler.IL.Transforms
{
+ ///
+ /// Parameter class holding various arguments for .
+ ///
public class ILTransformContext
{
public IDecompilerTypeSystem TypeSystem { get; set; }
public DecompilerSettings Settings { get; set; }
public CancellationToken CancellationToken { get; set; }
}
-
+
public interface IILTransform
{
void Run(ILFunction function, ILTransformContext context);
}
+
+ ///
+ /// Interface for transforms that can be "single-stepped" for debug purposes.
+ ///
+ /// After the transform has performed MaxStepCount steps, it throws a
+ /// .
+ ///
+ public interface ISingleStep
+ {
+ ///
+ /// Limits the number of steps performed by the transform.
+ ///
+ /// The default is int.MaxValue = unlimited.
+ ///
+ int MaxStepCount { get; set; }
+ }
+
+ ///
+ /// Exception thrown when an IL transform runs into the limit.
+ ///
+ public class StepLimitReachedException : Exception
+ {
+ }
+
+ ///
+ /// Helper struct for use in transforms that implement ISingleStep.
+ ///
+ internal struct Stepper
+ {
+ public readonly int MaxStepCount;
+ public int StepCount;
+
+ public Stepper(int maxStepCount)
+ {
+ this.StepCount = 0;
+ this.MaxStepCount = maxStepCount;
+ if (maxStepCount == 0)
+ throw new StepLimitReachedException();
+ }
+
+ ///
+ /// Called immediately after a transform step was taken.
+ ///
+ public void Stepped()
+ {
+ if (++StepCount == MaxStepCount)
+ throw new StepLimitReachedException();
+ }
+ }
}
diff --git a/ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs
index 02edd0b2f..2806db3f7 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs
@@ -25,10 +25,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
///
/// Repeats the child transforms until the ILAst no longer changes.
///
- public class LoopingTransform : IILTransform
+ public class LoopingTransform : IILTransform, ISingleStep
{
+ public int MaxStepCount { get; set; } = int.MaxValue;
readonly IReadOnlyCollection children;
-
+
public LoopingTransform(params IILTransform[] children)
{
this.children = children;
@@ -36,9 +37,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public void Run(ILFunction function, ILTransformContext context)
{
+ var stepper = new Stepper(MaxStepCount);
do {
function.ResetDirty();
function.RunTransforms(children, context);
+ stepper.Stepped();
} while (function.IsDirty);
}
diff --git a/ICSharpCode.Decompiler/Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler/Tests/Helpers/Tester.cs
index 2d09d385a..9d35f2cfa 100644
--- a/ICSharpCode.Decompiler/Tests/Helpers/Tester.cs
+++ b/ICSharpCode.Decompiler/Tests/Helpers/Tester.cs
@@ -38,17 +38,18 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
public enum CompilerOptions
{
None,
- Optimize,
- UseDebug,
- Force32Bit
+ Optimize = 0x1,
+ UseDebug = 0x2,
+ Force32Bit = 0x4
}
[Flags]
public enum AssemblerOptions
{
None,
- UseDebug,
- Force32Bit
+ UseDebug = 0x1,
+ Force32Bit = 0x2,
+ Library = 0x4,
}
public static class Tester
@@ -56,9 +57,16 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
public static string AssembleIL(string sourceFileName, AssemblerOptions options = AssemblerOptions.UseDebug)
{
string ilasmPath = Path.Combine(Environment.GetEnvironmentVariable("windir"), @"Microsoft.NET\Framework\v4.0.30319\ilasm.exe");
- string outputFile = Path.GetFileNameWithoutExtension(sourceFileName) + ".exe";
-
+ string outputFile = Path.GetFileNameWithoutExtension(sourceFileName);
string otherOptions = " ";
+ if (options.HasFlag(AssemblerOptions.Library)) {
+ outputFile += ".dll";
+ otherOptions += "/dll ";
+ } else {
+ outputFile += ".exe";
+ otherOptions += "/exe ";
+ }
+
if (options.HasFlag(AssemblerOptions.UseDebug)) {
otherOptions += "/debug ";
@@ -68,7 +76,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
}
ProcessStartInfo info = new ProcessStartInfo(ilasmPath);
- info.Arguments = $"/nologo /exe{otherOptions}/output=\"{outputFile}\" \"{sourceFileName}\"";
+ info.Arguments = $"/nologo {otherOptions}/output=\"{outputFile}\" \"{sourceFileName}\"";
info.RedirectStandardError = true;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
@@ -83,6 +91,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
Console.WriteLine("output: " + outputTask.Result);
Console.WriteLine("errors: " + errorTask.Result);
+ Assert.AreEqual(0, process.ExitCode, "ilasm failed");
return outputFile;
}
diff --git a/ICSharpCode.Decompiler/Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler/Tests/PrettyTestRunner.cs
index f10a3d434..1f3a69bd5 100644
--- a/ICSharpCode.Decompiler/Tests/PrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler/Tests/PrettyTestRunner.cs
@@ -68,14 +68,21 @@ namespace ICSharpCode.Decompiler.Tests
Run();
Run(asmOptions: AssemblerOptions.UseDebug);
}
-
+
+ [Test]
+ public void ShortCircuit()
+ {
+ Run();
+ Run(asmOptions: AssemblerOptions.UseDebug);
+ }
+
void Run([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None)
{
var ilFile = Path.Combine(TestCasePath, testName + ".il");
var csFile = Path.Combine(TestCasePath, testName + ".cs");
EnsureSourceFilesExist(Path.Combine(TestCasePath, testName));
- var executable = Tester.AssembleIL(ilFile, asmOptions);
+ var executable = Tester.AssembleIL(ilFile, asmOptions | AssemblerOptions.Library);
var decompiled = Tester.DecompileCSharp(executable);
CodeAssert.FilesAreEqual(csFile, decompiled);
diff --git a/ILSpy/Languages/ILAstLanguage.cs b/ILSpy/Languages/ILAstLanguage.cs
index 4f48c71e1..65828015c 100644
--- a/ILSpy/Languages/ILAstLanguage.cs
+++ b/ILSpy/Languages/ILAstLanguage.cs
@@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler;
@@ -92,16 +93,25 @@ namespace ICSharpCode.ILSpy
yield return new TypedIL();
CSharpDecompiler decompiler = new CSharpDecompiler(ModuleDefinition.CreateModule("Dummy", ModuleKind.Dll), new DecompilerSettings());
for (int i = 0; i <= decompiler.ILTransforms.Count; i++) {
- yield return new BlockIL(decompiler.ILTransforms.Take(i).ToList());
+ yield return MakeDebugLanguage(decompiler.ILTransforms.Take(i));
var loop = decompiler.ILTransforms.ElementAtOrDefault(i) as LoopingTransform;
if (loop != null) {
for (int j = 1; j <= loop.Transforms.Count; j++) {
- yield return new BlockIL(decompiler.ILTransforms.Take(i).Concat(loop.Transforms.Take(j)).ToList());
+ yield return MakeDebugLanguage(decompiler.ILTransforms.Take(i).Concat(loop.Transforms.Take(j)));
}
}
}
}
+ static ILAstLanguage MakeDebugLanguage(IEnumerable transforms)
+ {
+ var list = transforms.ToList();
+ if (list.LastOrDefault() is ISingleStep)
+ return new SingleSteppableIL(list);
+ else
+ return new BlockIL(list);
+ }
+
public override string FileExtension {
get {
return ".il";
@@ -159,6 +169,43 @@ namespace ICSharpCode.ILSpy
il.WriteTo(output);
}
}
+
+ class SingleSteppableIL : ILAstLanguage, ISingleStep
+ {
+ readonly IReadOnlyList transforms;
+ readonly ISingleStep steppingTransform;
+
+ public SingleSteppableIL(IReadOnlyList transforms) : base("ILAst (" + transforms.Last().GetType().Name + ")")
+ {
+ this.transforms = transforms;
+ this.steppingTransform = (ISingleStep)transforms.Last();
+ }
+
+ public int MaxStepCount { get; set; } = int.MaxValue;
+
+ public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)
+ {
+ base.DecompileMethod(method, output, options);
+ output.WriteLine("// This transform support single-stepping. Press '+' or '-' to change the step limit.");
+ output.WriteLine("// Current step limit: " + MaxStepCount);
+ if (!method.HasBody)
+ return;
+ var typeSystem = new DecompilerTypeSystem(method.Module);
+ ILReader reader = new ILReader(typeSystem);
+ ILFunction il = reader.ReadIL(method.Body, options.CancellationToken);
+ steppingTransform.MaxStepCount = this.MaxStepCount;
+ try {
+ il.RunTransforms(transforms, new ILTransformContext { Settings = options.DecompilerSettings, TypeSystem = typeSystem });
+ output.WriteLine("// The transform ran to completion.");
+ } catch (StepLimitReachedException) {
+ output.WriteLine("// The transform was aborted after reaching the step limit.");
+ } finally {
+ steppingTransform.MaxStepCount = int.MaxValue; // unlimit again so that other languages can use the transform
+ }
+ output.WriteLine();
+ il.WriteTo(output);
+ }
+ }
}
#endif
}
diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs
index b09081b1f..941f61cd4 100644
--- a/ILSpy/MainWindow.xaml.cs
+++ b/ILSpy/MainWindow.xaml.cs
@@ -778,8 +778,34 @@ namespace ICSharpCode.ILSpy
return treeView.GetTopLevelSelection().OfType();
}
}
- #endregion
+ protected override void OnPreviewKeyDown(KeyEventArgs e)
+ {
+ if (CurrentLanguage is Decompiler.IL.Transforms.ISingleStep) {
+ var step = (Decompiler.IL.Transforms.ISingleStep)CurrentLanguage;
+ if (e.Key == Key.OemPlus || e.Key == Key.Add) {
+ if (step.MaxStepCount == int.MaxValue) {
+ step.MaxStepCount = 0;
+ } else {
+ step.MaxStepCount++;
+ }
+ DecompileSelectedNodes(recordHistory: false);
+ e.Handled = true;
+ } else if (e.Key == Key.OemMinus || e.Key == Key.Subtract) {
+ if (step.MaxStepCount == 0) {
+ step.MaxStepCount = int.MaxValue;
+ } else {
+ step.MaxStepCount--;
+ }
+ DecompileSelectedNodes(recordHistory: false);
+ e.Handled = true;
+ }
+ } else {
+ base.OnPreviewKeyDown(e);
+ }
+ }
+ #endregion
+
#region Back/Forward navigation
void BackCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs
index e2fd98db1..84da42692 100644
--- a/ILSpy/TextView/DecompilerTextView.cs
+++ b/ILSpy/TextView/DecompilerTextView.cs
@@ -107,9 +107,8 @@ namespace ICSharpCode.ILSpy.TextView
DisplaySettingsPanel.CurrentDisplaySettings.PropertyChanged += CurrentDisplaySettings_PropertyChanged;
// SearchPanel
- SearchPanel.Install(textEditor.TextArea);
- // TODO: re-enable the RegisterCommands call after updating to AvalonEdit 5.0.3
- // .RegisterCommands(Application.Current.MainWindow.CommandBindings);
+ SearchPanel.Install(textEditor.TextArea)
+ .RegisterCommands(Application.Current.MainWindow.CommandBindings);
ShowLineMargin();