, true) with
- | null | [| |] -> None
- | res ->
- let attr = (res.[0] :?> StructuredFormatDisplayAttribute)
- let txt = attr.Value
- if isNull txt || txt.Length <= 1 then
- None
- else
- let messageRegexPattern = @"^(?.*?)(?.*?)(?.*)$"
- let illFormedBracketPattern = @"(?
- // there isn't a match on the regex looking for a property, so now let's make sure we don't have an ill-formed format string (i.e. mismatched/stray brackets)
- let illFormedMatch = System.Text.RegularExpressions.Regex.IsMatch(txt, illFormedBracketPattern)
- match illFormedMatch with
- | true -> None // there are mismatched brackets, bail out
- | false when layouts.Length > 1 -> Some (spaceListL (List.rev ((wordL (tagText(replaceEscapedBrackets(txt))) :: layouts))))
- | false -> Some (wordL (tagText(replaceEscapedBrackets(txt))))
- | true ->
- // we have a hit on a property reference
- let preText = replaceEscapedBrackets(m.Groups.["pre"].Value) // everything before the first opening bracket
- let postText = m.Groups.["post"].Value // Everything after the closing bracket
- let prop = replaceEscapedBrackets(m.Groups.["prop"].Value) // Unescape everything between the opening and closing brackets
-
- match catchExn (fun () -> getProperty ty x prop) with
- | Choice2Of2 e -> Some (wordL (tagText("")))
- | Choice1Of2 alternativeObj ->
- try
- let alternativeObjL =
- match alternativeObj with
- // A particular rule is that if the alternative property
- // returns a string, we turn off auto-quoting and escaping of
- // the string, i.e. just treat the string as display text.
- // This allows simple implementations of
- // such as
- //
- // []
- // type BigInt(signInt:int, v : BigNat) =
- // member x.StructuredDisplayString = x.ToString()
- //
- | :? string as s -> sepL (tagText s)
- | _ ->
- // recursing like this can be expensive, so let's throttle it severely
- sameObjL (depthLim/10) Precedence.BracketIfTuple (alternativeObj, alternativeObj.GetType())
- countNodes 0 // 0 means we do not count the preText and postText
-
- let postTextMatch = System.Text.RegularExpressions.Regex.Match(postText, messageRegexPattern)
- // the postText for this node will be everything up to the next occurrence of an opening brace, if one exists
- let currentPostText =
- match postTextMatch.Success with
- | false -> postText
- | true -> postTextMatch.Groups.["pre"].Value
-
- let newLayouts = (sepL (tagText preText) ^^ alternativeObjL ^^ sepL (tagText currentPostText)) :: layouts
- match postText with
- | "" ->
- //We are done, build a space-delimited layout from the collection of layouts we've accumulated
- Some (spaceListL (List.rev newLayouts))
- | remainingPropertyText when postTextMatch.Success ->
-
- // look for stray brackets in the text before the next opening bracket
- let strayClosingMatch = System.Text.RegularExpressions.Regex.IsMatch(postTextMatch.Groups.["pre"].Value, illFormedBracketPattern)
- match strayClosingMatch with
- | true -> None
- | false ->
- // More to process, keep going, using the postText starting at the next instance of a '{'
- let openingBracketIndex = postTextMatch.Groups.["prop"].Index-1
- buildObjMessageL remainingPropertyText.[openingBracketIndex..] newLayouts
- | remaingPropertyText ->
- // make sure we don't have any stray brackets
- let strayClosingMatch = System.Text.RegularExpressions.Regex.IsMatch(remaingPropertyText, illFormedBracketPattern)
- match strayClosingMatch with
- | true -> None
- | false ->
- // We are done, there's more text but it doesn't contain any more properties, we need to remove escaped brackets now though
- // since that wasn't done when creating currentPostText
- Some (spaceListL (List.rev ((sepL (tagText preText) ^^ alternativeObjL ^^ sepL (tagText(replaceEscapedBrackets(remaingPropertyText)))) :: layouts)))
- with _ ->
- None
- // Seed with an empty layout with a space to the left for formatting purposes
- buildObjMessageL txt [leftL (tagText "")]
-#if COMPILER // This is the PrintIntercepts extensibility point currently revealed by fsi.exe's AddPrinter
+ // Lazy values. VS2008 used StructuredFormatDisplayAttribute to show via ToString. Dev10 (no attr) needs a special case.
+ let ty = x.GetType()
+ if ty.IsGenericType && ty.GetGenericTypeDefinition() = typedefof> then
+ Some (wordL (tagText(x.ToString())))
+ else
+ // Try the StructuredFormatDisplayAttribute extensibility attribute
+ match ty.GetCustomAttributes (typeof, true) with
+ | null | [| |] -> None
+ | res ->
+ structuredFormatObjectL showMode ty depthLim (res.[0] :?> StructuredFormatDisplayAttribute) x
+
+#if COMPILER
+ // This is the PrintIntercepts extensibility point currently revealed by fsi.exe's AddPrinter
let res =
match res with
| Some _ -> res
| None ->
- let env = { new IEnvironment with
- member env.GetLayout(y) = objL (depthLim-1) Precedence.BracketIfTuple (y, y.GetType())
- member env.MaxColumns = opts.PrintLength
- member env.MaxRows = opts.PrintLength }
+ let env =
+ { new IEnvironment with
+ member _.GetLayout(y) = nestedObjL (depthLim-1) Precedence.BracketIfTuple (y, y.GetType())
+ member _.MaxColumns = opts.PrintLength
+ member _.MaxRows = opts.PrintLength }
opts.PrintIntercepts |> List.tryPick (fun intercept -> intercept env x)
#endif
let res =
match res with
| Some res -> res
- | None -> reprL showMode (depthLim-1) prec info x
- path .Remove(x) |> ignore;
+ | None -> reprL showMode (depthLim-1) prec info x
+
+ path.Remove(x) |> ignore
res
- with
- e ->
- countNodes 1
- wordL (tagText("Error: " + e.Message))
-
- and recdAtomicTupleL depthLim recd =
- // tuples up args to UnionConstruction or ExceptionConstructor. no node count.
- match recd with
- | [(_,x)] -> objL depthLim Precedence.BracketIfTupleOrNotAtomic x
- | txs -> leftL Literals.leftParen ^^ commaListL (List.map (snd >> objL depthLim Precedence.BracketIfTuple) txs) ^^ rightL Literals.rightParen
-
- and bracketIfL b basicL =
- if b then (leftL Literals.leftParen) ^^ basicL ^^ (rightL Literals.rightParen) else basicL
-
- and reprL showMode depthLim prec repr x (* x could be null *) =
- let showModeFilter lay = match showMode with ShowAll -> lay | ShowTopLevelBinding -> emptyL
- match repr with
- | TupleValue (tupleType, vals) ->
- let basicL = sepListL (rightL Literals.comma) (List.map (objL depthLim Precedence.BracketIfTuple ) vals)
- let fields = bracketIfL (prec <= Precedence.BracketIfTuple) basicL
- match tupleType with
- | TupleType.Value -> structL ^^ fields
- | TupleType.Reference -> fields
-
- | RecordValue items ->
- let itemL (name,x,ty) =
- countNodes 1 // record labels are counted as nodes. [REVIEW: discussion under 4090].
- (tagRecordField name,objL depthLim Precedence.BracketIfTuple (x, ty))
- makeRecordL (List.map itemL items)
-
- | ConstructorValue (_,constr,recd) when // x is List. Note: "null" is never a valid list value.
- x<>null && isListType (x.GetType()) ->
- match constr with
- | "Cons" ->
- let (x,xs) = unpackCons recd
- let project xs = getListValueInfo bindingFlags xs
- let itemLs = objL depthLim Precedence.BracketIfTuple x :: boundedUnfoldL (objL depthLim Precedence.BracketIfTuple) project stopShort xs (opts.PrintLength - 1)
- makeListL itemLs
- | _ ->
- countNodes 1
- wordL (tagPunctuation "[]")
-
- | ConstructorValue(declaringType,nm,recd) ->
- countNodes 1
- let caseName =
- match declaringType with
- | None ->
- wordL (tagMethod nm)
- | Some declaringType ->
- wordL (tagClass declaringType.Name) ^^ sepL (tagPunctuation ".") ^^ wordL (tagMethod nm)
- match recd with
- | [] -> caseName
- | recd -> (caseName --- recdAtomicTupleL depthLim recd) |> bracketIfL (prec <= Precedence.BracketIfTupleOrNotAtomic)
-
- | ExceptionValue(ty,recd) ->
- countNodes 1
- let name = ty.Name
- match recd with
- | [] -> (wordL (tagClass name))
- | recd -> (wordL (tagClass name) --- recdAtomicTupleL depthLim recd) |> bracketIfL (prec <= Precedence.BracketIfTupleOrNotAtomic)
+ with
+ e ->
+ countNodes 1
+ wordL (tagText("Error: " + e.Message))
+
+ // Format an object that has a layout specified by StructuredFormatAttribute
+ and structuredFormatObjectL showMode ty depthLim (attr: StructuredFormatDisplayAttribute) (obj: obj) =
+ let txt = attr.Value
+ if isNull txt || txt.Length <= 1 then
+ None
+ else
+ let messageRegexPattern = @"^(?.*?)(?.*?)(?.*)$"
+ let illFormedBracketPattern = @"(?
- // Q: should function printing include the ty.Name? It does not convey much useful info to most users, e.g. "clo@0_123".
- countNodes 1
- wordL (tagText("")) |> showModeFilter
-
- | ObjectValue(obj) ->
- match obj with
- | null ->
- countNodes 1
- // If this is the root element, wrap the null with angle brackets
- if depthLim = opts.PrintDepth - 1 then
- wordL (tagText "")
- else nullL
- | _ ->
- let ty = obj.GetType()
- match obj with
- | :? string as s ->
- countNodes 1
+ let rec buildObjMessageL (txt:string) (layouts:Layout list) =
+
+ let replaceEscapedBrackets (txt:string) =
+ txt.Replace("\{", "{").Replace("\}", "}")
+
+ // to simplify support for escaped brackets, switch to using a Regex to simply parse the text as the following regex groups:
+ // 1) Everything up to the first opening bracket not preceded by a "\", lazily
+ // 2) Everything between that opening bracket and a closing bracket not preceded by a "\", lazily
+ // 3) Everything after that closing bracket
+ let m = System.Text.RegularExpressions.Regex.Match(txt, messageRegexPattern)
+ if not m.Success then
+ // there isn't a match on the regex looking for a property, so now let's make sure we don't have an ill-formed format string (i.e. mismatched/stray brackets)
+ let illFormedMatch = System.Text.RegularExpressions.Regex.IsMatch(txt, illFormedBracketPattern)
+ if illFormedMatch then
+ None // there are mismatched brackets, bail out
+ elif layouts.Length > 1 then Some (spaceListL (List.rev ((wordL (tagText(replaceEscapedBrackets(txt))) :: layouts))))
+ else Some (wordL (tagText(replaceEscapedBrackets(txt))))
+ else
+ // we have a hit on a property reference
+ let preText = replaceEscapedBrackets(m.Groups.["pre"].Value) // everything before the first opening bracket
+ let postText = m.Groups.["post"].Value // Everything after the closing bracket
+ let prop = replaceEscapedBrackets(m.Groups.["prop"].Value) // Unescape everything between the opening and closing brackets
+
+ match catchExn (fun () -> getProperty ty obj prop) with
+ | Choice2Of2 e -> Some (wordL (tagText("")))
+ | Choice1Of2 alternativeObj ->
+ try
+ let alternativeObjL =
+ match alternativeObj with
+ // A particular rule is that if the alternative property
+ // returns a string, we turn off auto-quoting and escaping of
+ // the string, i.e. just treat the string as display text.
+ // This allows simple implementations of
+ // such as
+ //
+ // []
+ // type BigInt(signInt:int, v: BigNat) =
+ // member x.StructuredDisplayString = x.ToString()
+ //
+ | :? string as s -> sepL (tagText s)
+ | _ ->
+ // recursing like this can be expensive, so let's throttle it severely
+ objL showMode (depthLim/10) Precedence.BracketIfTuple (alternativeObj, alternativeObj.GetType())
+ countNodes 0 // 0 means we do not count the preText and postText
+
+ let postTextMatch = System.Text.RegularExpressions.Regex.Match(postText, messageRegexPattern)
+ // the postText for this node will be everything up to the next occurrence of an opening brace, if one exists
+ let currentPostText =
+ match postTextMatch.Success with
+ | false -> postText
+ | true -> postTextMatch.Groups.["pre"].Value
+
+ let newLayouts = (sepL (tagText preText) ^^ alternativeObjL ^^ sepL (tagText currentPostText)) :: layouts
+ match postText with
+ | "" ->
+ //We are done, build a space-delimited layout from the collection of layouts we've accumulated
+ Some (spaceListL (List.rev newLayouts))
+
+ | remainingPropertyText when postTextMatch.Success ->
+
+ // look for stray brackets in the text before the next opening bracket
+ let strayClosingMatch = System.Text.RegularExpressions.Regex.IsMatch(postTextMatch.Groups.["pre"].Value, illFormedBracketPattern)
+ if strayClosingMatch then
+ None
+ else
+ // More to process, keep going, using the postText starting at the next instance of a '{'
+ let openingBracketIndex = postTextMatch.Groups.["prop"].Index-1
+ buildObjMessageL remainingPropertyText.[openingBracketIndex..] newLayouts
+
+ | remaingPropertyText ->
+ // make sure we don't have any stray brackets
+ let strayClosingMatch = System.Text.RegularExpressions.Regex.IsMatch(remaingPropertyText, illFormedBracketPattern)
+ if strayClosingMatch then
+ None
+ else
+ // We are done, there's more text but it doesn't contain any more properties, we need to remove escaped brackets now though
+ // since that wasn't done when creating currentPostText
+ Some (spaceListL (List.rev ((sepL (tagText preText) ^^ alternativeObjL ^^ sepL (tagText(replaceEscapedBrackets(remaingPropertyText)))) :: layouts)))
+ with _ ->
+ None
+
+ // Seed with an empty layout with a space to the left for formatting purposes
+ buildObjMessageL txt [leftL (tagText "")]
+
+ and recdAtomicTupleL depthLim recd =
+ // tuples up args to UnionConstruction or ExceptionConstructor. no node count.
+ match recd with
+ | [(_,x)] -> nestedObjL depthLim Precedence.BracketIfTupleOrNotAtomic x
+ | txs -> leftL Literals.leftParen ^^ commaListL (List.map (snd >> nestedObjL depthLim Precedence.BracketIfTuple) txs) ^^ rightL Literals.rightParen
+
+ and bracketIfL flag basicL =
+ if flag then (leftL Literals.leftParen) ^^ basicL ^^ (rightL Literals.rightParen) else basicL
+
+ and tupleValueL depthLim prec vals tupleType =
+ let basicL = sepListL (rightL Literals.comma) (List.map (nestedObjL depthLim Precedence.BracketIfTuple ) (Array.toList vals))
+ let fields = bracketIfL (prec <= Precedence.BracketIfTuple) basicL
+ match tupleType with
+ | TupleType.Value -> structL ^^ fields
+ | TupleType.Reference -> fields
+
+ and recordValueL depthLim items =
+ let itemL (name, x, ty) =
+ countNodes 1
+ tagRecordField name,nestedObjL depthLim Precedence.BracketIfTuple (x, ty)
+ makeRecordL (List.map itemL items)
+
+ and listValueL depthLim constr recd =
+ match constr with
+ | "Cons" ->
+ let (x,xs) = unpackCons recd
+ let project xs = getListValueInfo bindingFlags xs
+ let itemLs = nestedObjL depthLim Precedence.BracketIfTuple x :: boundedUnfoldL (nestedObjL depthLim Precedence.BracketIfTuple) project stopShort xs (opts.PrintLength - 1)
+ makeListL itemLs
+ | _ ->
+ countNodes 1
+ wordL (tagPunctuation "[]")
+
+ and unionCaseValueL depthLim prec (declaringType: Type option) unionCaseName recd =
+ countNodes 1
+ let caseName =
+ match declaringType with
+ | None ->
+ wordL (tagMethod unionCaseName)
+ | Some declaringType ->
+ wordL (tagClass declaringType.Name) ^^ sepL (tagPunctuation ".") ^^ wordL (tagMethod unionCaseName)
+ match recd with
+ | [] -> caseName
+ | recd -> (caseName --- recdAtomicTupleL depthLim recd) |> bracketIfL (prec <= Precedence.BracketIfTupleOrNotAtomic)
+
+ and fsharpExceptionL depthLim prec (exceptionType: Type) recd =
+ countNodes 1
+ let name = exceptionType.Name
+ match recd with
+ | [] -> (wordL (tagClass name))
+ | recd -> (wordL (tagClass name) --- recdAtomicTupleL depthLim recd) |> bracketIfL (prec <= Precedence.BracketIfTupleOrNotAtomic)
+
+ and showModeFilter showMode layout =
+ match showMode with
+ | ShowAll -> layout
+ | ShowTopLevelBinding -> emptyL
+
+ and functionClosureL showMode (closureType: Type) =
+ // Q: should function printing include the ty.Name? It does not convey much useful info to most users, e.g. "clo@0_123".
+ countNodes 1
+ wordL (tagText("")) |> showModeFilter showMode
+
+ and stringValueL (s: string) =
+ countNodes 1
#if COMPILER
- if s.Length + 2(*quotes*) <= opts.StringLimit then
- // With the quotes, it fits within the limit.
- wordL (tagStringLiteral(formatString s))
- else
- // When a string is considered too long to print, there is a choice: what to print?
- // a) -- follows
- // b) -- follows and gives just the length
- // c) "abcdefg"+[n chars] -- gives a prefix and the remaining chars
- wordL (tagStringLiteral(formatStringInWidth opts.StringLimit s))
+ if s.Length + 2(*quotes*) <= opts.StringLimit then
+ // With the quotes, it fits within the limit.
+ wordL (tagStringLiteral(formatString s))
+ else
+ // When a string is considered too long to print, there is a choice: what to print?
+ // a) -- follows
+ // b) -- follows and gives just the length
+ // c) "abcdefg"+[n chars] -- gives a prefix and the remaining chars
+ wordL (tagStringLiteral(formatStringInWidth opts.StringLimit s))
#else
- wordL (tagStringLiteral (formatString s))
-#endif
- | :? Array as arr ->
- let ty = arr.GetType().GetElementType()
- match arr.Rank with
- | 1 ->
- let n = arr.Length
- let b1 = arr.GetLowerBound(0)
- let project depthLim = if depthLim=(b1+n) then None else Some ((box (arr.GetValue(depthLim)), ty),depthLim+1)
- let itemLs = boundedUnfoldL (objL depthLim Precedence.BracketIfTuple) project stopShort b1 opts.PrintLength
- makeArrayL (if b1 = 0 then itemLs else wordL (tagText("bound1="+string_of_int b1)) :: itemLs)
- | 2 ->
- let n1 = arr.GetLength(0)
- let n2 = arr.GetLength(1)
- let b1 = arr.GetLowerBound(0)
- let b2 = arr.GetLowerBound(1)
- let project2 x y =
- if x>=(b1+n1) || y>=(b2+n2) then None
- else Some ((box (arr.GetValue(x,y)), ty),y+1)
- let rowL x = boundedUnfoldL (objL depthLim Precedence.BracketIfTuple) (project2 x) stopShort b2 opts.PrintLength |> makeListL
- let project1 x = if x>=(b1+n1) then None else Some (x,x+1)
- let rowsL = boundedUnfoldL rowL project1 stopShort b1 opts.PrintLength
- makeArray2L (if b1=0 && b2 = 0 then rowsL else wordL (tagText("bound1=" + string_of_int b1)) :: wordL(tagText("bound2=" + string_of_int b2)) :: rowsL)
- | n ->
- makeArrayL [wordL (tagText("rank=" + string_of_int n))]
+ wordL (tagStringLiteral (formatString s))
+#endif
+
+ and arrayValueL depthLim (arr: Array) =
+ let ty = arr.GetType().GetElementType()
+ match arr.Rank with
+ | 1 ->
+ let n = arr.Length
+ let b1 = arr.GetLowerBound(0)
+ let project depthLim = if depthLim=(b1+n) then None else Some ((box (arr.GetValue(depthLim)), ty),depthLim+1)
+ let itemLs = boundedUnfoldL (nestedObjL depthLim Precedence.BracketIfTuple) project stopShort b1 opts.PrintLength
+ makeArrayL (if b1 = 0 then itemLs else wordL (tagText("bound1="+string_of_int b1)) :: itemLs)
+ | 2 ->
+ let n1 = arr.GetLength(0)
+ let n2 = arr.GetLength(1)
+ let b1 = arr.GetLowerBound(0)
+ let b2 = arr.GetLowerBound(1)
+ let project2 x y =
+ if x>=(b1+n1) || y>=(b2+n2) then None
+ else Some ((box (arr.GetValue(x,y)), ty),y+1)
+ let rowL x = boundedUnfoldL (nestedObjL depthLim Precedence.BracketIfTuple) (project2 x) stopShort b2 opts.PrintLength |> makeListL
+ let project1 x = if x>=(b1+n1) then None else Some (x,x+1)
+ let rowsL = boundedUnfoldL rowL project1 stopShort b1 opts.PrintLength
+ makeArray2L (if b1=0 && b2 = 0 then rowsL else wordL (tagText("bound1=" + string_of_int b1)) :: wordL(tagText("bound2=" + string_of_int b2)) :: rowsL)
+ | n ->
+ makeArrayL [wordL (tagText("rank=" + string_of_int n))]
- // Format 'set' and 'map' nicely
- | _ when
- (ty.IsGenericType && (ty.GetGenericTypeDefinition() = typedefof