-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
279 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net47</TargetFramework> | ||
<AssemblyName>LINQPad.DumpEditable</AssemblyName> | ||
<RootNamespace>LINQPad.DumpEditable</RootNamespace> | ||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> | ||
<Version>0.0.1</Version> | ||
<Authors>Ryan Davis</Authors> | ||
<Product>DumpEditable.LINQPad</Product> | ||
<Description>Extensible inline editor output for LINQPad</Description> | ||
<Copyright>(c) Ryan Davis 2019</Copyright> | ||
<PackageLicenseExpression></PackageLicenseExpression> | ||
<PackageProjectUrl>https://github.com/rdavisau/linqpad-dump-editable</PackageProjectUrl> | ||
<RepositoryUrl>https://github.com/rdavisau/linqpad-dump-editable</RepositoryUrl> | ||
<PackageTags>LINQPad, Dump, editor</PackageTags> | ||
<PackageId>DumpEditable.LINQPad</PackageId> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="LINQPad" Version="5.26.1" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Reference Include="Microsoft.VisualBasic" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Reflection; | ||
|
||
namespace LINQPad.DumpEditable | ||
{ | ||
public partial class EditableDumpContainer<T> : DumpContainer | ||
{ | ||
private readonly T _obj; | ||
private readonly bool _failSilently; | ||
private readonly Dictionary<PropertyInfo, Action<T, PropertyInfo, object>> _changeHandlers | ||
= new Dictionary<PropertyInfo, Action<T, PropertyInfo, object>>(); | ||
|
||
private readonly List<EditorRule> _editorRules = new List<EditorRule>(); | ||
|
||
public Action OnChanged { get; set; } | ||
public Action<T, PropertyInfo, object> OnPropertyValueChanged { get; set; } | ||
|
||
public static void AddGlobalEditorRule(EditorRule rule) | ||
=> GlobalEditorRules.Insert(0, rule); | ||
|
||
public void AddChangeHandler<U>(Expression<Func<T, U>> selector, | ||
Action<T, PropertyInfo, U> onChangedAction) | ||
{ | ||
var pi = (selector.Body as MemberExpression)?.Member as PropertyInfo; | ||
if (pi is null) | ||
throw new Exception($"Invalid expression passed to {nameof(AddChangeHandler)}"); | ||
|
||
_changeHandlers[pi] = (obj, p, val) => onChangedAction(obj, p, (U) val); | ||
} | ||
|
||
public void AddEditorRule(EditorRule rule) | ||
=> _editorRules.Insert(0, rule); | ||
|
||
public EditableDumpContainer(T obj, bool failSilently = false) | ||
{ | ||
_obj = obj; | ||
_failSilently = failSilently; | ||
|
||
SetContent(); | ||
} | ||
|
||
private void SetContent() | ||
{ | ||
object content = _obj; | ||
|
||
try | ||
{ | ||
content = | ||
_obj | ||
.GetType() | ||
.GetProperties() | ||
.Select(p => | ||
new | ||
{ | ||
Property = p.Name, | ||
Value = GetEditor(_obj, p) | ||
}) | ||
.ToList(); | ||
} | ||
catch | ||
{ | ||
if (!_failSilently) | ||
throw; | ||
} | ||
|
||
Content = content; | ||
} | ||
|
||
private object GetEditor(object o, PropertyInfo p) | ||
{ | ||
var allRules = Enumerable.Concat(_editorRules, GlobalEditorRules); | ||
|
||
foreach (var editor in allRules) | ||
if (editor.Match(o, p)) | ||
return editor.Editor(o, p, () => | ||
{ | ||
SetContent(); | ||
var newVal = p.GetValue(o); | ||
if (_changeHandlers.TryGetValue(p, out var handler)) | ||
handler.Invoke((T)o,p,newVal); | ||
OnPropertyValueChanged?.Invoke(_obj, p, newVal); | ||
OnChanged?.Invoke();; | ||
}); | ||
|
||
return o; | ||
} | ||
} | ||
|
||
public partial class EditableDumpContainer<T> | ||
{ | ||
private static readonly List<EditorRule> GlobalEditorRules = GetDefaultEditors(); | ||
private static List<EditorRule> GetDefaultEditors() => | ||
new List<EditorRule> | ||
{ | ||
EditorRule.ForEnums(), | ||
EditorRule.ForTypeWithStringBasedEditor<int>(int.TryParse), | ||
EditorRule.ForTypeWithStringBasedEditor<uint>(uint.TryParse), | ||
EditorRule.ForTypeWithStringBasedEditor<short>(short.TryParse), | ||
EditorRule.ForTypeWithStringBasedEditor<ushort>(ushort.TryParse), | ||
EditorRule.ForTypeWithStringBasedEditor<double>(double.TryParse), | ||
EditorRule.ForTypeWithStringBasedEditor<decimal>(decimal.TryParse), | ||
EditorRule.ForTypeWithStringBasedEditor<float>(float.TryParse), | ||
EditorRule.ForTypeWithStringBasedEditor<long>(long.TryParse), | ||
EditorRule.ForTypeWithStringBasedEditor<ulong>(ulong.TryParse), | ||
EditorRule.ForTypeWithStringBasedEditor<DateTime>(DateTime.TryParse), | ||
EditorRule.ForTypeWithStringBasedEditor<DateTimeOffset>(DateTimeOffset.TryParse), | ||
EditorRule.ForTypeWithStringBasedEditor((string input, out string output) => | ||
{ | ||
output = input; | ||
return true; | ||
}), | ||
}; | ||
|
||
} | ||
|
||
public static class EditableDumpContainer | ||
{ | ||
public static EditableDumpContainer<T> For<T>(T obj, bool failSilently = false) | ||
=> new EditableDumpContainer<T>(obj, failSilently); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Reflection; | ||
using Microsoft.VisualBasic; | ||
|
||
namespace LINQPad.DumpEditable | ||
{ | ||
public partial class EditorRule | ||
{ | ||
public Func<object, PropertyInfo, bool> Match { get; set; } | ||
public Func<object, PropertyInfo, Action, object> Editor { get; set; } | ||
} | ||
|
||
public partial class EditorRule | ||
{ | ||
public static EditorRule For(Func<object, PropertyInfo, bool> rule, | ||
Func<object, PropertyInfo, Action, object> getEditor) | ||
=> new EditorRule | ||
{ | ||
Match = rule, | ||
Editor = getEditor, | ||
}; | ||
|
||
public static EditorRule ForType<T>(Func<object, PropertyInfo, Action, object> getEditor) | ||
=> new EditorRule | ||
{ | ||
Match = (o, info) => info.PropertyType == typeof(T), | ||
Editor = getEditor, | ||
}; | ||
public static EditorRule ForEnums() => | ||
EditorRule.For( | ||
(_, p) => p.PropertyType.IsEnum, | ||
(o, p, c) => | ||
Util.HorizontalRun(true, | ||
Enumerable.Concat( | ||
new object[] { p.GetValue(o), "[" }, | ||
p.PropertyType | ||
.GetEnumValues() | ||
.OfType<object>() | ||
.Select(v => new Hyperlinq(() => { p.SetValue(o, v); c(); }, $"{v}"))) | ||
.Concat(new [] { "]" })) | ||
); | ||
|
||
public static EditorRule ForTypeWithStringBasedEditor<T>(ParseFunc<string, T, bool> parseFunc) | ||
=> new EditorRule | ||
{ | ||
Match = (o, info) => info.PropertyType == typeof(T), | ||
Editor = (o, info, changed) => GetStringInputBasedEditor(o, info, changed, parseFunc) | ||
}; | ||
|
||
protected static object GetStringInputBasedEditor<TOut>(object o, PropertyInfo p, Action changeCallback, EditorRule.ParseFunc<string, TOut, bool> parseFunc) | ||
{ | ||
var currVal = p.GetValue(o); | ||
var desc = currVal != null ? $"{currVal}" : "null"; | ||
|
||
var change = new Hyperlinq(() => | ||
{ | ||
var newVal = Interaction.InputBox("Set value for " + p.Name, p.Name, $"{currVal}"); | ||
var canConvert = parseFunc(newVal, out var output); | ||
if (!canConvert) | ||
return; | ||
p.SetValue(o, output); | ||
changeCallback?.Invoke(); | ||
}, desc); | ||
|
||
return Util.HorizontalRun(true, change); | ||
} | ||
|
||
public delegate V ParseFunc<T, U, V>(T input, out U output); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace LINQPad.DumpEditable | ||
{ | ||
public static class Extensions | ||
{ | ||
public static T DumpEditable<T>(this T obj, bool failSilently = false) | ||
=> DumpEditable(obj, out _, failSilently); | ||
|
||
public static T DumpEditable<T>(this T obj, out EditableDumpContainer<T> container, bool failSilently = false) | ||
{ | ||
container = new EditableDumpContainer<T>(obj, failSilently); | ||
container.Dump(); | ||
|
||
return obj; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 16 | ||
VisualStudioVersion = 16.0.28729.10 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DumpEditable", "DumpEditable\DumpEditable.csproj", "{67C4F4C2-21AB-4A15-8419-6F563A3DE873}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{67C4F4C2-21AB-4A15-8419-6F563A3DE873}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{67C4F4C2-21AB-4A15-8419-6F563A3DE873}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{67C4F4C2-21AB-4A15-8419-6F563A3DE873}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{67C4F4C2-21AB-4A15-8419-6F563A3DE873}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {DD4C4D88-1121-4B0C-BC54-6CE368E2C92A} | ||
EndGlobalSection | ||
EndGlobal |