From 84f1c159aafc3a926c56e65737a6205325444170 Mon Sep 17 00:00:00 2001 From: Ross McKinlay Date: Sat, 1 Aug 2015 15:44:38 +0100 Subject: [PATCH 1/3] implement custom display strrings and custom display callback --- FsEye/Fsi/Eye.fs | 9 +++++++++ FsEye/WatchModel.fs | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/FsEye/Fsi/Eye.fs b/FsEye/Fsi/Eye.fs index 33d4bee..5b3231c 100644 --- a/FsEye/Fsi/Eye.fs +++ b/FsEye/Fsi/Eye.fs @@ -63,6 +63,14 @@ type Eye() as this = Async.Start(computation, listenerCts.Token) null + ///Add or replace a custom callback handler used to create the display strings for instances of objects + member __.SetFormatter(f) = WatchModel.customPrinter <- Some f + + ///Add or replace a custom display string for a type in the format: "Some text {PropertyA} more text and {PropertyB}" + member __.SetDisplayString(ty:System.Type, displayString:string) = + if WatchModel.customSprintLookup.ContainsKey ty then WatchModel.customSprintLookup.Remove ty |> ignore + WatchModel.customSprintLookup.Add(ty, WatchModel.generateSprintFunction(displayString)) + ///Add or update a watch with the given name, value, and type. member __.Watch(name, value:obj, ty) = resources.EyeForm.Watch(name, value, ty) @@ -133,6 +141,7 @@ type Eye() as this = ///Manages plugins and plugin watch viewers member this.PluginManager = resources.PluginManager + [] [] diff --git a/FsEye/WatchModel.fs b/FsEye/WatchModel.fs index f97fd8d..cbff645 100644 --- a/FsEye/WatchModel.fs +++ b/FsEye/WatchModel.fs @@ -128,9 +128,37 @@ and Watch = | DataMember {ExpressionInfo=ei} -> Some(ei) | Organizer _ -> None + open System.Text.RegularExpressions + +// dictionary of custom display functions +let customSprintLookup = new System.Collections.Generic.Dictionarystring>() + +// custom callback function for overriding display strings +let mutable customPrinter = None : (obj -> string option) option + +// create a function that will replace {Property} with the .ToString() of the properties for an object instace +let generateSprintFunction displayString = + let r = new Regex("""\{(.*?)\}""",RegexOptions.IgnoreCase|||RegexOptions.Singleline); + // replace instances of {PropertyName} with the property value via reflection + // eg : "hello world {John} and maybe {Dave}" + let rec getResults (m:Match) = + [if m.Success then + yield m.Value + yield! getResults (m.NextMatch())] + let results = getResults (r.Match displayString) + fun (o:obj) -> + let getValue name = + if o = null then "null" else + let prop = o.GetType().GetProperty(name) + if prop = null then sprintf "could not find property %s" name else + let i = prop.GetGetMethod().Invoke(o, [||]) + if i = null then "null" else i.ToString() + (displayString,results) + ||> List.fold(fun acc item -> acc.Replace(item, getValue(item.Replace("{","").Replace("}","")))) + ///Sprint the given value with the given Type. Precondition: Type cannot be null. -let private sprintValue (value:obj) (ty:Type) = +let private sprintValue (value:obj) (ty:Type) = if ty =& null then nullArg "ty cannot be null" @@ -144,7 +172,16 @@ let private sprintValue (value:obj) (ty:Type) = if typeof.IsAssignableFrom(ty) then sprintf "typeof<%s>" (value :?> Type).FSharpName else - sprintf "%A" value |> cleanString + // always use a custom display string function if it exists + match customSprintLookup.TryGetValue ty with + | true, f -> f value + | _ -> + // attempt to use callback function if set + customPrinter + |> Option.bind(fun f -> f value) + |> function + | Some s -> s + | None -> sprintf "%A" value |> cleanString ///Create lazy seq of children s for a typical valued let rec createChildren ownerValue (ownerTy:Type) = From 722f563745647537a17ba33f600715e6c3918c81 Mon Sep 17 00:00:00 2001 From: pezi Date: Sun, 2 Aug 2015 15:24:23 +0100 Subject: [PATCH 2/3] add exception handler to callback --- FsEye/WatchModel.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FsEye/WatchModel.fs b/FsEye/WatchModel.fs index cbff645..2f646c7 100644 --- a/FsEye/WatchModel.fs +++ b/FsEye/WatchModel.fs @@ -178,7 +178,7 @@ let private sprintValue (value:obj) (ty:Type) = | _ -> // attempt to use callback function if set customPrinter - |> Option.bind(fun f -> f value) + |> Option.bind(fun f -> try f value with ex -> Some (ex.Message)) |> function | Some s -> s | None -> sprintf "%A" value |> cleanString From 7c50fae520ebb0fed2e8d799b194ff5b6b7447cd Mon Sep 17 00:00:00 2001 From: pezi Date: Sun, 2 Aug 2015 15:38:45 +0100 Subject: [PATCH 3/3] add removal methods --- FsEye/Fsi/Eye.fs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/FsEye/Fsi/Eye.fs b/FsEye/Fsi/Eye.fs index 5b3231c..6f81d46 100644 --- a/FsEye/Fsi/Eye.fs +++ b/FsEye/Fsi/Eye.fs @@ -66,10 +66,17 @@ type Eye() as this = ///Add or replace a custom callback handler used to create the display strings for instances of objects member __.SetFormatter(f) = WatchModel.customPrinter <- Some f + ///Remove the current custom formatter callback handler + member __.RemoveFormatter() = WatchModel.customPrinter <- None + ///Add or replace a custom display string for a type in the format: "Some text {PropertyA} more text and {PropertyB}" - member __.SetDisplayString(ty:System.Type, displayString:string) = + member __.SetDisplayString(ty, displayString) = if WatchModel.customSprintLookup.ContainsKey ty then WatchModel.customSprintLookup.Remove ty |> ignore WatchModel.customSprintLookup.Add(ty, WatchModel.generateSprintFunction(displayString)) + + //Removes a custom display string for a given type + member __.RemoveDisplayString(ty) = + if WatchModel.customSprintLookup.ContainsKey ty then WatchModel.customSprintLookup.Remove ty |> ignore ///Add or update a watch with the given name, value, and type. member __.Watch(name, value:obj, ty) =