forked from fsprojects/FSharp.Formatting
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcodeformat.fsx
More file actions
167 lines (135 loc) · 5.73 KB
/
codeformat.fsx
File metadata and controls
167 lines (135 loc) · 5.73 KB
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
(*** condition: prepare ***)
#nowarn "211"
#I "../src/FSharp.Formatting/bin/Release/netstandard2.1"
#r "FSharp.Formatting.Common.dll"
#r "FSharp.Formatting.Markdown.dll"
#r "FSharp.Formatting.CodeFormat.dll"
#r "FSharp.Formatting.Literate.dll"
(*** condition: fsx ***)
#if FSX
#r "nuget: FSharp.Formatting,{{package-version}}"
#endif // FSX
(*** condition: ipynb ***)
#if IPYNB
#r "nuget: FSharp.Formatting,{{package-version}}"
#endif // IPYNB
(**
Code formatting
==============================
This page demonstrates how to use `FSharp.Formatting.CodeFormat.dll` to tokenize
F# source code, obtain information about the source code (mainly tooltips
from the type-checker) and how to turn the code into a nicely formatted HTML.
First, we need to load the assembly and open necessary namespaces:
*)
open FSharp.Formatting.CodeFormat
open System.Reflection
(**
Starting a background agent
---------------------------
The `FSharp.Formatting.CodeFormat` namespace contains `CodeFormat` type which is the
entry point. The static method `CreateAgent` starts a background worker that
can be called to format snippets repeatedly:
*)
let formattingAgent = CodeFormat.CreateAgent()
(**
If you want to process multiple snippets, it is a good idea to keep the
formatting agent around if possible. The agent needs to load the F# compiler
(which needs to load various files itself) and so this takes a long time. As the above
example shows, you can specify which version of `FSharp.Compiler.dll` to use.
Processing F# source
--------------------
The formatting agent provides a `ParseAndCheckSource` method (together with an asynchronous
version for use from F# and also a version that returns a .NET `Task` for C#).
To call the method, we define a simple F# code as a string:
*)
let source = """
let hello () =
printfn "Hello world"
"""
let snippets, errors = formattingAgent.ParseAndCheckSource("C:\\snippet.fsx", source)
(**
When calling the method, you need to specify a file name and the actual content
of the script file. The file does not have to physically exist. It is used by the
F# compiler to resolve relative references (e.g. `#r`) and to automatically name
the module including all code in the file.
You can also specify additional parameters, such as `*.dll` references, by passing
a third argument with compiler options (e.g. `"-r:Foo.dll -r:Bar.dll"`).
This operation might take some time, so it is a good idea to use an asynchronous
variant of the method. It returns two arrays - the first contains F# snippets
in the source code and the second contains any errors reported by the compiler.
A single source file can include multiple snippets using the same formatting tags
as those used on [fssnip.net](http://www.fssnip.net) as documented in the
[about page](http://www.fssnip.net/pages/About).
Working with returned tokens
----------------------------
Each returned snippet is essentially just a collection of lines and each line
consists of a sequence of tokens. The following snippet prints basic information
about the tokens of our sample snippet:
*)
// Get the first snippet and obtain list of lines
let (Snippet(title, lines)) = snippets |> Seq.head
// Iterate over all lines and all tokens on each line
for (Line(_, tokens)) in lines do
for token in tokens do
match token with
| TokenSpan.Token(kind, code, tip) ->
printf "%s" code
tip |> Option.iter (fun spans ->
printfn "%A" spans)
| TokenSpan.Omitted _
| TokenSpan.Output _
| TokenSpan.Error _ -> ()
printfn ""
(**
The `TokenSpan.Token` is the most important kind of token. It consists of a kind
(identifier, keyword, etc.), the original F# code and tool tip information.
The tool tip is further formatted using a simple document format, but we simply
print the value using the F# pretty printing, so the result looks as follows:
let hello[Literal "val hello : unit -> unit"; ...] () =
printfn[Literal "val printfn : TextWriterFormat<'T> -> 'T"; ...] "Hello world"
The `Omitted` token is generated if you use the special `(*[omit:...]*)` command.
The `Output` token is generated if you use the `// [fsi:...]` command to format
output returned by F# interactive. The `Error` command wraps code that should be
underlined with a red squiggle if the code contains an error.
Generating HTML output
----------------------
Finally, the `CodeFormat` type also includes a method `FormatHtml` that can be used
to generate nice HTML output from an F# snippet. This is used, for example, on
[F# Snippets](http://www.fssnip.net). The following example shows how to call it:
*)
let prefix = "fst"
let html = CodeFormat.FormatHtml(snippets, prefix)
// Print all snippets, in case there is more of them
for snip in html.Snippets do
printfn "%s" snip.Content
// Print HTML code that is generated for ToolTips
printfn "%s" html.ToolTip
(**
If the input contains multiple snippets separated using the `//[snippet:...]` comment, e.g.:
*)
(**
<table class="pre"><tr><td class="lines"><pre class="fssnip">
<span class="l">1: </span>
<span class="l">2: </span>
<span class="l">3: </span>
<span class="l">4: </span>
<span class="l">5: </span>
<span class="l">6: </span>
<span class="l">7: </span>
</pre>
</td>
<td class="snippet"><pre class="fssnip"><span class="c">// [snippet: First sample]</span>
<span class="i">printf</span> <span class="s">"The answer is: %A"</span> <span class="n">42</span>
<span class="c">// [/snippet]</span>
<span class="c">// [snippet: Second sample]</span>
<span class="i">printf</span> <span class="s">"Hello world!"</span>
<span class="c">// [/snippet]</span>
</pre>
</td>
</tr>
</table>
*)
(**
then the formatter returns multiple HTML blocks. However, the generated tool tips
are shared by all snippets (to save space) and so they are returned separately.
*)