Skip to content

Commit c45b8fc

Browse files
Add VSC UI Toolkit (ionide#1721)
* add vscode ui toolkit to the release directory * add webview helpers * update the fsi watcher to use the webview helpers Co-authored-by: Krzysztof Cieślak <[email protected]>
1 parent 68c6ceb commit c45b8fc

File tree

5 files changed

+352
-120
lines changed

5 files changed

+352
-120
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,7 @@ fsacpath.txt
8383

8484
build/bin
8585
build/obj
86+
87+
# include vscode ui toolkit minified release file
88+
!release/toolkit.min.js
8689
release/fsharp.js.LICENSE.txt

release/toolkit.min.js

+30
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Components/Fsi.fs

+117-120
Original file line numberDiff line numberDiff line change
@@ -60,39 +60,28 @@ module Fsi =
6060
()
6161

6262
module Watcher =
63+
open Webviews
6364
let mutable panel: WebviewPanel option = None
6465

65-
let setContent str =
66-
panel
67-
|> Option.iter (fun p ->
68-
let str =
69-
sprintf
70-
"""
71-
<html>
72-
<head>
73-
<meta>
74-
<style>
75-
table { border-collapse: collapse;}
76-
th {
77-
border-left: 1px solid var(--vscode-editor-foreground);
78-
border-right: 1px solid var(--vscode-editor-foreground);
79-
padding: 5px;
80-
}
81-
td {
82-
border: 1px solid var(--vscode-editor-foreground);
83-
min-width: 100px;
84-
padding: 5px;
85-
}
86-
</style>
87-
</head>
88-
<body>
89-
%s
90-
</body>
91-
</html>
92-
"""
93-
str
94-
95-
p.webview.html <- str)
66+
let updateContent
67+
(context: ExtensionContext)
68+
(varsContent: (string * string) option)
69+
(typesContent: (string * string) option)
70+
(funcsContent: (string * string) option)
71+
=
72+
let (vars, varsScript) = defaultArg varsContent ("", "")
73+
let (types, typesScript) = defaultArg typesContent ("", "")
74+
let (funcs, funcsScript) = defaultArg funcsContent ("", "")
75+
76+
match panel with
77+
| Some panel ->
78+
FsWebview.render (
79+
context,
80+
panel,
81+
$"{vars}{types}{funcs}",
82+
scripts = [ varsScript; typesScript; funcsScript ]
83+
)
84+
| None -> ()
9685

9786
let openPanel () =
9887
promise {
@@ -113,18 +102,22 @@ module Fsi =
113102
match panel with
114103
| Some p -> p.reveal (!! -2, true)
115104
| None ->
116-
let opts =
117-
createObj
118-
[ "enableCommandUris" ==> true
119-
"enableFindWidget" ==> true
120-
"retainContextWhenHidden" ==> true ]
121-
122105
let viewOpts =
123106
createObj
124107
[ "preserveFocus" ==> true
125108
"viewColumn" ==> -2 ]
126109

127-
let p = window.createWebviewPanel ("fsiWatcher", "FSI Watcher", !!viewOpts, opts)
110+
let p =
111+
FsWebview.create (
112+
"fsiWatcher",
113+
"FSI Watcher",
114+
!!viewOpts,
115+
enableScripts = true,
116+
enableFindWidget = true,
117+
enableCommandUris = true,
118+
retainContextWhenHidden = true
119+
)
120+
128121
let onClose () = panel <- None
129122

130123
p.onDidDispose.Invoke(!!onClose) |> ignore
@@ -140,42 +133,44 @@ module Fsi =
140133
path.join (VSCodeExtension.ionidePluginPath (), "watcher", "funcs.txt")
141134

142135

143-
let handler () =
144-
let mutable varsContent = ""
145-
let mutable typesContent = ""
146-
let mutable funcsContent = ""
147-
136+
let handler (context: ExtensionContext) =
137+
let mutable varsContent = None
138+
let mutable typesContent = None
139+
let mutable funcsContent = None
148140

149141
node.fs.readFile (
150142
varsUri,
151143
(fun _ buf ->
152144
if not (Utils.isUndefined buf) then
153145
let cnt = buf.ToString()
154146

155-
varsContent <-
156-
cnt
157-
|> String.split [| '\n' |]
158-
|> Seq.map (fun row ->
159-
let x = row.Split([| "###IONIDESEP###" |], StringSplitOptions.None)
160-
161-
sprintf
162-
"<tr><td>%s</td><td><code>%s</code></td><td><code>%s</code></td><td>%s</td></tr>"
163-
x.[0]
164-
x.[1]
165-
x.[2]
166-
x.[3])
167-
|> String.concat "\n"
168-
|> sprintf
169-
"""</br><h3>Declared values</h3></br><table style="width:100%%"><tr><th style="width: 12%%">Name</th><th style="width: 65%%">Value</th><th style="width: 20%%">Type</th><th>Step</th></tr>%s</table>"""
170-
171-
172-
setContent (
173-
varsContent
174-
+ "\n\n"
175-
+ funcsContent
176-
+ "\n\n"
177-
+ typesContent
178-
))
147+
if String.IsNullOrWhiteSpace cnt then
148+
varsContent <- None
149+
else
150+
151+
let datagridContent =
152+
cnt
153+
|> String.split [| '\n' |]
154+
|> Array.map (fun row ->
155+
let x = row.Split([| "###IONIDESEP###" |], StringSplitOptions.None)
156+
157+
box
158+
{| name = x[0]
159+
value = x[1]
160+
Type = x[2]
161+
step = x[3] |})
162+
// ensure column order
163+
let headers =
164+
[| "Name", "name"
165+
"Value", "value"
166+
"Type", "Type"
167+
"Step", "step" |]
168+
169+
let grid, script = VsHtml.datagrid ("vars-content", datagridContent, headers)
170+
171+
varsContent <- Some(html $"<h3>Declared values</h3>{grid}", script)
172+
173+
updateContent context varsContent typesContent funcsContent)
179174
)
180175

181176
node.fs.readFile (
@@ -184,30 +179,33 @@ module Fsi =
184179
if not (Utils.isUndefined buf) then
185180
let cnt = buf.ToString()
186181

187-
funcsContent <-
188-
cnt
189-
|> String.split [| '\n' |]
190-
|> Seq.map (fun row ->
191-
let x = row.Split([| "###IONIDESEP###" |], StringSplitOptions.None)
192-
193-
sprintf
194-
"<tr><td>%s</td><td><code>%s</code></td><td><code>%s</code></td><td>%s</td></tr>"
195-
x.[0]
196-
x.[1]
197-
x.[2]
198-
x.[3])
199-
|> String.concat "\n"
200-
|> sprintf
201-
"""</br><h3>Declared functions</h3></br><table style="width:100%%"><tr><th style="width: 12%%">Name</th><th style="width: 65%%">Parameters</th><th style="width: 20%%">Returned type</th><th>Step</th></tr>%s</table>"""
202-
203-
204-
setContent (
205-
varsContent
206-
+ "\n\n"
207-
+ funcsContent
208-
+ "\n\n"
209-
+ typesContent
210-
))
182+
if String.IsNullOrWhiteSpace cnt then
183+
funcsContent <- None
184+
else
185+
let datagridContent =
186+
cnt
187+
|> String.split [| '\n' |]
188+
|> Array.map (fun row ->
189+
let x = row.Split([| "###IONIDESEP###" |], StringSplitOptions.None)
190+
191+
box
192+
{| name = x[0]
193+
parameters = x[1]
194+
returnType = x[2]
195+
step = x[3] |})
196+
197+
let grid, script =
198+
VsHtml.datagrid (
199+
"funcs-content",
200+
datagridContent,
201+
[| "Name", "name"
202+
"Parameters", "parameters"
203+
"Return Type", "returnType" |]
204+
)
205+
206+
funcsContent <- Some(html $"<h3>Declared functions</h3>{grid}", script)
207+
208+
updateContent context varsContent typesContent funcsContent)
211209
)
212210

213211
node.fs.readFile (
@@ -216,40 +214,39 @@ module Fsi =
216214
if not (Utils.isUndefined buf) then
217215
let cnt = buf.ToString()
218216

219-
typesContent <-
220-
if String.IsNullOrWhiteSpace cnt then
221-
""
222-
else
217+
if String.IsNullOrWhiteSpace cnt then
218+
typesContent <- None
219+
else
220+
let extractSignature (str: string) =
221+
if str.Contains "#|#" then
222+
"| " + str.Replace("#|#", "</br>| ")
223+
else
224+
str
225+
226+
let datagridContent =
223227
cnt
224228
|> String.split [| '\n' |]
225-
|> Seq.map (fun row ->
229+
|> Array.map (fun row ->
226230
let x = row.Split([| "###IONIDESEP###" |], StringSplitOptions.None)
227231

228-
let signature =
229-
if x.[1].Contains "#|#" then
230-
"| " + x.[1].Replace("#|#", "</br>| ")
231-
else
232-
x.[1]
233-
234-
sprintf "<tr><td>%s</td><td><code>%s</code></td><td>%s</td></tr>" x.[0] signature x.[2])
235-
|> String.concat "\n"
236-
|> sprintf
237-
"""</br><h3>Declared types</h3></br><table style="width:100%%"><tr><th style="width: 12%%">Name</th><th style="width: 85%%">Signature</th><th>Step</th></tr>%s</table>"""
238-
239-
240-
setContent (
241-
varsContent
242-
+ "\n\n"
243-
+ funcsContent
244-
+ "\n\n"
245-
+ typesContent
246-
))
232+
let signature = extractSignature x[1]
233+
234+
box
235+
{| Name = x[0]
236+
Signature = signature
237+
Step = x[2] |})
238+
239+
let grid, script = VsHtml.datagrid ("types-content", datagridContent)
240+
241+
typesContent <- Some(html $"<h3>Declared types</h3>{grid}", script)
242+
243+
updateContent context varsContent typesContent funcsContent)
247244
)
248245

249-
let activate dispsables =
250-
fs.watchFile (varsUri, (fun st st2 -> handler ()))
251-
fs.watchFile (typesUri, (fun st st2 -> handler ()))
252-
fs.watchFile (funcUri, (fun st st2 -> handler ()))
246+
let activate context dispsables =
247+
fs.watchFile (varsUri, (fun st st2 -> handler context))
248+
fs.watchFile (typesUri, (fun st st2 -> handler context))
249+
fs.watchFile (funcUri, (fun st st2 -> handler context))
253250

254251
let mutable fsiOutput: Terminal option = None
255252
let mutable fsiOutputPID: int option = None
@@ -635,7 +632,7 @@ module Fsi =
635632
}
636633

637634
let activate (context: ExtensionContext) =
638-
Watcher.activate (!!context.subscriptions)
635+
Watcher.activate context (!!context.subscriptions)
639636
SdkScriptsNotify.activate context
640637

641638
window.registerTerminalProfileProvider ("ionide-fsharp.fsi", provider)

0 commit comments

Comments
 (0)