Skip to content

Commit

Permalink
Add VSC UI Toolkit (#1721)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
AngelMunoz and Krzysztof-Cieslak authored Jul 12, 2022
1 parent 68c6ceb commit c45b8fc
Show file tree
Hide file tree
Showing 5 changed files with 352 additions and 120 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,7 @@ fsacpath.txt

build/bin
build/obj

# include vscode ui toolkit minified release file
!release/toolkit.min.js
release/fsharp.js.LICENSE.txt
30 changes: 30 additions & 0 deletions release/toolkit.min.js

Large diffs are not rendered by default.

237 changes: 117 additions & 120 deletions src/Components/Fsi.fs
Original file line number Diff line number Diff line change
Expand Up @@ -60,39 +60,28 @@ module Fsi =
()

module Watcher =
open Webviews
let mutable panel: WebviewPanel option = None

let setContent str =
panel
|> Option.iter (fun p ->
let str =
sprintf
"""
<html>
<head>
<meta>
<style>
table { border-collapse: collapse;}
th {
border-left: 1px solid var(--vscode-editor-foreground);
border-right: 1px solid var(--vscode-editor-foreground);
padding: 5px;
}
td {
border: 1px solid var(--vscode-editor-foreground);
min-width: 100px;
padding: 5px;
}
</style>
</head>
<body>
%s
</body>
</html>
"""
str

p.webview.html <- str)
let updateContent
(context: ExtensionContext)
(varsContent: (string * string) option)
(typesContent: (string * string) option)
(funcsContent: (string * string) option)
=
let (vars, varsScript) = defaultArg varsContent ("", "")
let (types, typesScript) = defaultArg typesContent ("", "")
let (funcs, funcsScript) = defaultArg funcsContent ("", "")

match panel with
| Some panel ->
FsWebview.render (
context,
panel,
$"{vars}{types}{funcs}",
scripts = [ varsScript; typesScript; funcsScript ]
)
| None -> ()

let openPanel () =
promise {
Expand All @@ -113,18 +102,22 @@ module Fsi =
match panel with
| Some p -> p.reveal (!! -2, true)
| None ->
let opts =
createObj
[ "enableCommandUris" ==> true
"enableFindWidget" ==> true
"retainContextWhenHidden" ==> true ]

let viewOpts =
createObj
[ "preserveFocus" ==> true
"viewColumn" ==> -2 ]

let p = window.createWebviewPanel ("fsiWatcher", "FSI Watcher", !!viewOpts, opts)
let p =
FsWebview.create (
"fsiWatcher",
"FSI Watcher",
!!viewOpts,
enableScripts = true,
enableFindWidget = true,
enableCommandUris = true,
retainContextWhenHidden = true
)

let onClose () = panel <- None

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


let handler () =
let mutable varsContent = ""
let mutable typesContent = ""
let mutable funcsContent = ""

let handler (context: ExtensionContext) =
let mutable varsContent = None
let mutable typesContent = None
let mutable funcsContent = None

node.fs.readFile (
varsUri,
(fun _ buf ->
if not (Utils.isUndefined buf) then
let cnt = buf.ToString()

varsContent <-
cnt
|> String.split [| '\n' |]
|> Seq.map (fun row ->
let x = row.Split([| "###IONIDESEP###" |], StringSplitOptions.None)

sprintf
"<tr><td>%s</td><td><code>%s</code></td><td><code>%s</code></td><td>%s</td></tr>"
x.[0]
x.[1]
x.[2]
x.[3])
|> String.concat "\n"
|> sprintf
"""</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>"""


setContent (
varsContent
+ "\n\n"
+ funcsContent
+ "\n\n"
+ typesContent
))
if String.IsNullOrWhiteSpace cnt then
varsContent <- None
else

let datagridContent =
cnt
|> String.split [| '\n' |]
|> Array.map (fun row ->
let x = row.Split([| "###IONIDESEP###" |], StringSplitOptions.None)

box
{| name = x[0]
value = x[1]
Type = x[2]
step = x[3] |})
// ensure column order
let headers =
[| "Name", "name"
"Value", "value"
"Type", "Type"
"Step", "step" |]

let grid, script = VsHtml.datagrid ("vars-content", datagridContent, headers)

varsContent <- Some(html $"<h3>Declared values</h3>{grid}", script)

updateContent context varsContent typesContent funcsContent)
)

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

funcsContent <-
cnt
|> String.split [| '\n' |]
|> Seq.map (fun row ->
let x = row.Split([| "###IONIDESEP###" |], StringSplitOptions.None)

sprintf
"<tr><td>%s</td><td><code>%s</code></td><td><code>%s</code></td><td>%s</td></tr>"
x.[0]
x.[1]
x.[2]
x.[3])
|> String.concat "\n"
|> sprintf
"""</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>"""


setContent (
varsContent
+ "\n\n"
+ funcsContent
+ "\n\n"
+ typesContent
))
if String.IsNullOrWhiteSpace cnt then
funcsContent <- None
else
let datagridContent =
cnt
|> String.split [| '\n' |]
|> Array.map (fun row ->
let x = row.Split([| "###IONIDESEP###" |], StringSplitOptions.None)

box
{| name = x[0]
parameters = x[1]
returnType = x[2]
step = x[3] |})

let grid, script =
VsHtml.datagrid (
"funcs-content",
datagridContent,
[| "Name", "name"
"Parameters", "parameters"
"Return Type", "returnType" |]
)

funcsContent <- Some(html $"<h3>Declared functions</h3>{grid}", script)

updateContent context varsContent typesContent funcsContent)
)

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

typesContent <-
if String.IsNullOrWhiteSpace cnt then
""
else
if String.IsNullOrWhiteSpace cnt then
typesContent <- None
else
let extractSignature (str: string) =
if str.Contains "#|#" then
"| " + str.Replace("#|#", "</br>| ")
else
str

let datagridContent =
cnt
|> String.split [| '\n' |]
|> Seq.map (fun row ->
|> Array.map (fun row ->
let x = row.Split([| "###IONIDESEP###" |], StringSplitOptions.None)

let signature =
if x.[1].Contains "#|#" then
"| " + x.[1].Replace("#|#", "</br>| ")
else
x.[1]

sprintf "<tr><td>%s</td><td><code>%s</code></td><td>%s</td></tr>" x.[0] signature x.[2])
|> String.concat "\n"
|> sprintf
"""</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>"""


setContent (
varsContent
+ "\n\n"
+ funcsContent
+ "\n\n"
+ typesContent
))
let signature = extractSignature x[1]

box
{| Name = x[0]
Signature = signature
Step = x[2] |})

let grid, script = VsHtml.datagrid ("types-content", datagridContent)

typesContent <- Some(html $"<h3>Declared types</h3>{grid}", script)

updateContent context varsContent typesContent funcsContent)
)

let activate dispsables =
fs.watchFile (varsUri, (fun st st2 -> handler ()))
fs.watchFile (typesUri, (fun st st2 -> handler ()))
fs.watchFile (funcUri, (fun st st2 -> handler ()))
let activate context dispsables =
fs.watchFile (varsUri, (fun st st2 -> handler context))
fs.watchFile (typesUri, (fun st st2 -> handler context))
fs.watchFile (funcUri, (fun st st2 -> handler context))

let mutable fsiOutput: Terminal option = None
let mutable fsiOutputPID: int option = None
Expand Down Expand Up @@ -635,7 +632,7 @@ module Fsi =
}

let activate (context: ExtensionContext) =
Watcher.activate (!!context.subscriptions)
Watcher.activate context (!!context.subscriptions)
SdkScriptsNotify.activate context

window.registerTerminalProfileProvider ("ionide-fsharp.fsi", provider)
Expand Down
Loading

0 comments on commit c45b8fc

Please sign in to comment.