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