20 people like it.
    Like the snippet!
  
  Pretty Print Source Code Errors
  A snippet that allows you to pretty print source code errors
  |  1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
59: 
60: 
61: 
62: 
63: 
64: 
65: 
66: 
67: 
68: 
69: 
70: 
71: 
72: 
73: 
74: 
75: 
76: 
77: 
78: 
79: 
80: 
81: 
82: 
83: 
84: 
85: 
86: 
87: 
88: 
89: 
90: 
91: 
92: 
 | module SourceErrorPrinter = 
  
  open System
  open System.Text
  let private splitLines (input:string) = 
    let input = (string input).Replace("\r\n", "\n").Replace("\r", "\n")
    RegularExpressions.Regex.Split(input, "\n")
  let private lineNum (pad:int) (input:int) = 
    (string input).PadLeft(pad, '0')
  let private makeArrow (times:int) =
    let result = new StringBuilder(times)
    result.Insert(0, "-", times).ToString() + "^"
  
  let sprintError (above:int, below:int) (line:int, column:int) (source:string) =
    let source = source |> splitLines
    let result = ref ""
    if line <= source.Length && line > 0 then
      let text = source.[line - 1]
      let nrLength = (line+below |> string).Length
      
      // Deal with lines above the error one
      for i = 1 to above do
        let prevLine = line - i
        if prevLine >= 1 then
          let num = prevLine |> lineNum nrLength
          let text = sprintf "%s: %s\n" num source.[prevLine-1] 
          result := text + !result
      // Error line and arrow
      let arrow = nrLength + column + 1 |> makeArrow
      let num = line |> lineNum nrLength
      let text = sprintf "%s: %s\n%s\n" num text arrow
      result := !result + text
      // Deal with lines below the error one
      for i = 1 to below do
        let nextLine = line + i
        if nextLine <= source.Length then
          let num = nextLine |> lineNum nrLength
          let text = sprintf "%s: %s\n" num source.[nextLine-1] 
          result := !result + text
    !result
// Example, a piece of source code from IronJS 
let source = @"/// Adds a catch variable to the scope
let addCatchLocal name (s:S) = 
  match s |> locals |> Map.tryFind name with
  | None -> 
    s |> addLocal name None
    let local = s |> locals |> Map.find name 
    let local = local |> Local.decreaseActive
    s |> replaceLocal local
  | Some local ->
    let index = LocalIndex.New (s |> localCount) None
    let local = local |> Local.addIndex index
    s := 
      {!s with 
        LocalCount = index.Index + 1
        Locals = s |> locals |> Map.add name local}"
// We partially appli SourceErrorPrinter.sprintError with
// a tuple that is the lines to show before and after
// the errornous line
let linesBefore = 2
let linesAfter = 3
let sprintError = SourceErrorPrinter.sprintError (linesBefore, linesAfter)
// We can use our function like this to print error on line 12, column 15
// from source and then just pipe it into printfn
source |> sprintError (12, 15) |> printfn "%s"
(*Gives an error like this:
10:   | Some local ->
11:     let index = LocalIndex.New (s |> localCount) None
12:     let local = local |> Local.addIndex index
------------------^
13: 
14:     s := 
15:       {!s with 
*)
 | 
namespace System
namespace System.Text
val private splitLines : input:string -> string []
Full name: Script.SourceErrorPrinter.splitLines
val input : string
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = String
Full name: Microsoft.FSharp.Core.string
namespace System.Text.RegularExpressions
Multiple items
type Regex =
  new : pattern:string -> Regex + 1 overload
  member GetGroupNames : unit -> string[]
  member GetGroupNumbers : unit -> int[]
  member GroupNameFromNumber : i:int -> string
  member GroupNumberFromName : name:string -> int
  member IsMatch : input:string -> bool + 1 overload
  member Match : input:string -> Match + 2 overloads
  member Matches : input:string -> MatchCollection + 1 overload
  member Options : RegexOptions
  member Replace : input:string * replacement:string -> string + 5 overloads
  ...
Full name: System.Text.RegularExpressions.Regex
--------------------
RegularExpressions.Regex(pattern: string) : unit
RegularExpressions.Regex(pattern: string, options: RegularExpressions.RegexOptions) : unit
RegularExpressions.Regex.Split(input: string, pattern: string) : string []
RegularExpressions.Regex.Split(input: string, pattern: string, options: RegularExpressions.RegexOptions) : string []
val private lineNum : pad:int -> input:int -> string
Full name: Script.SourceErrorPrinter.lineNum
val pad : int
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
val input : int
val private makeArrow : times:int -> string
Full name: Script.SourceErrorPrinter.makeArrow
val times : int
val result : StringBuilder
Multiple items
type StringBuilder =
  new : unit -> StringBuilder + 5 overloads
  member Append : value:string -> StringBuilder + 18 overloads
  member AppendFormat : format:string * arg0:obj -> StringBuilder + 4 overloads
  member AppendLine : unit -> StringBuilder + 1 overload
  member Capacity : int with get, set
  member Chars : int -> char with get, set
  member Clear : unit -> StringBuilder
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EnsureCapacity : capacity:int -> int
  member Equals : sb:StringBuilder -> bool
  ...
Full name: System.Text.StringBuilder
--------------------
StringBuilder() : unit
StringBuilder(capacity: int) : unit
StringBuilder(value: string) : unit
StringBuilder(value: string, capacity: int) : unit
StringBuilder(capacity: int, maxCapacity: int) : unit
StringBuilder(value: string, startIndex: int, length: int, capacity: int) : unit
StringBuilder.Insert(index: int, value: obj) : StringBuilder
   (+0 other overloads)
StringBuilder.Insert(index: int, value: uint64) : StringBuilder
   (+0 other overloads)
StringBuilder.Insert(index: int, value: uint32) : StringBuilder
   (+0 other overloads)
StringBuilder.Insert(index: int, value: uint16) : StringBuilder
   (+0 other overloads)
StringBuilder.Insert(index: int, value: decimal) : StringBuilder
   (+0 other overloads)
StringBuilder.Insert(index: int, value: float) : StringBuilder
   (+0 other overloads)
StringBuilder.Insert(index: int, value: float32) : StringBuilder
   (+0 other overloads)
StringBuilder.Insert(index: int, value: int64) : StringBuilder
   (+0 other overloads)
StringBuilder.Insert(index: int, value: int) : StringBuilder
   (+0 other overloads)
StringBuilder.Insert(index: int, value: char []) : StringBuilder
   (+0 other overloads)
val sprintError : above:int * below:int -> line:int * column:int -> source:string -> string
Full name: Script.SourceErrorPrinter.sprintError
val above : int
val below : int
val line : int
val column : int
val source : string
val source : string []
val result : string ref
Multiple items
val ref : value:'T -> 'T ref
Full name: Microsoft.FSharp.Core.Operators.ref
--------------------
type 'T ref = Ref<'T>
Full name: Microsoft.FSharp.Core.ref<_>
property Array.Length: int
val text : string
val nrLength : int
val i : int
val prevLine : int
val num : string
val sprintf : format:Printf.StringFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val arrow : string
val nextLine : int
val source : string
Full name: Script.source
val linesBefore : int
Full name: Script.linesBefore
val linesAfter : int
Full name: Script.linesAfter
val sprintError : (int * int -> string -> string)
Full name: Script.sprintError
module SourceErrorPrinter
from Script
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
  
  
  More information