Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Break direct C# dependency and instead light up debug/build functionality at runtime #1903

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions build/paket.references
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,5 @@ Fake.IO.Zip
Fake.Api.GitHub
Fake.Tools.Git
Fake.JavaScript.Yarn
Octokit

Octokit
MSBuild.StructuredLogger
102 changes: 51 additions & 51 deletions paket.dependencies
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
source https://api.nuget.org/v3/index.json
storage: none
framework: netstandard2.0

nuget Fable.Core
nuget Fable.Promise
nuget Fable.Node
nuget Fable.Browser.Dom

nuget Fable.HtmlConverter

nuget Thoth.Json 7.0

git https://github.com/ionide/ionide-fsgrammar.git master

github ionide/ionide-vscode-helpers:8e81bc03f11f07b8e0811b3d4598eadc78f32f2f src/Fable.Import.VSCode.fs
github ionide/ionide-vscode-helpers:8e81bc03f11f07b8e0811b3d4598eadc78f32f2f src/Helpers.fs
github ionide/ionide-vscode-helpers:8e81bc03f11f07b8e0811b3d4598eadc78f32f2f src/Fable.Import.Showdown.fs
github ionide/ionide-vscode-helpers:8e81bc03f11f07b8e0811b3d4598eadc78f32f2f src/Fable.Import.VSCode.LanguageServer.fs

group fsac
source https://api.nuget.org/v3/index.json
storage: packages
framework: netstandard2.0
nuget fsautocomplete

group build
source https://api.nuget.org/v3/index.json
storage: none
framework: net7.0

nuget Fake.Core.Target
nuget Fake.Core.Process
nuget Fake.Core.ReleaseNotes
nuget Fake.Core.Environment
nuget Fake.Core.UserInput
nuget Fake.DotNet.Cli
nuget Fake.DotNet.AssemblyInfoFile
nuget Fake.DotNet.Paket
nuget Fake.DotNet.MsBuild
nuget Fake.IO.FileSystem
nuget Fake.IO.Zip
nuget Fake.Api.GitHub
nuget Fake.Tools.Git
nuget Fake.JavaScript.Yarn
nuget Octokit <= 0.49.0

// Enforce updated binlog parser until Fake gets an update, so we're not stuck
// on an old sdk version.
// ref: https://github.com/fsprojects/FAKE/issues/2744
nuget MSBuild.StructuredLogger >= 2.1.784
source https://api.nuget.org/v3/index.json
storage: none
framework: netstandard2.0
nuget Fable.Core
nuget Fable.Promise
nuget Fable.Node
nuget Fable.Browser.Dom
nuget Fable.HtmlConverter
nuget Thoth.Json 7.0
git https://github.com/ionide/ionide-fsgrammar.git master
github ionide/ionide-vscode-helpers:8e81bc03f11f07b8e0811b3d4598eadc78f32f2f src/Fable.Import.VSCode.fs
github ionide/ionide-vscode-helpers:8e81bc03f11f07b8e0811b3d4598eadc78f32f2f src/Helpers.fs
github ionide/ionide-vscode-helpers:8e81bc03f11f07b8e0811b3d4598eadc78f32f2f src/Fable.Import.Showdown.fs
github ionide/ionide-vscode-helpers:8e81bc03f11f07b8e0811b3d4598eadc78f32f2f src/Fable.Import.VSCode.LanguageServer.fs
group fsac
source https://api.nuget.org/v3/index.json
storage: packages
framework: netstandard2.0
nuget fsautocomplete
group build
source https://api.nuget.org/v3/index.json
storage: none
framework: net7.0
nuget Fake.Core.Target
nuget Fake.Core.Process
nuget Fake.Core.ReleaseNotes
nuget Fake.Core.Environment
nuget Fake.Core.UserInput
nuget Fake.DotNet.Cli
nuget Fake.DotNet.AssemblyInfoFile
nuget Fake.DotNet.Paket
nuget Fake.DotNet.MsBuild
nuget Fake.IO.FileSystem
nuget Fake.IO.Zip
nuget Fake.Api.GitHub
nuget Fake.Tools.Git
nuget Fake.JavaScript.Yarn
nuget Octokit <= 0.49.0
// Enforce updated binlog parser until Fake gets an update, so we're not stuck
// on an old sdk version.
// ref: https://github.com/fsprojects/FAKE/issues/2744
nuget MSBuild.StructuredLogger >= 2.1.784
20 changes: 10 additions & 10 deletions release/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,8 @@
"dark": "./images/debug-mono-dark.svg",
"light": "./images/debug-mono-light.svg"
},
"title": "Debug"
"title": "Debug",
"enablement": "fsharp.debugger.available"
},
{
"command": "fsharp.explorer.project.setDefault",
Expand All @@ -350,7 +351,8 @@
{
"command": "fsharp.debugDefaultProject",
"icon": "./images/debug.png",
"title": "F#: Debug Default Project"
"title": "F#: Debug Default Project",
"enablement": "fsharp.debugger.available"
},
{
"command": "fsharp.chooseDefaultProject",
Expand Down Expand Up @@ -1218,7 +1220,7 @@
},
{
"command": "fsharp.debugDefaultProject",
"when": "fsharp.project.any"
"when": "fsharp.project.any \u0026\u0026 fsharp.debugger.available"
},
{
"command": "fsharp.chooseDefaultProject",
Expand Down Expand Up @@ -1266,7 +1268,7 @@
{
"command": "fsharp.debugDefaultProject",
"group": "fsharp",
"when": "fsharp.project.any \u0026\u0026 config.FSharp.enableTouchBar"
"when": "fsharp.project.any \u0026\u0026 config.FSharp.enableTouchBar \u0026\u0026 fsharp.debugger.available"
},
{
"command": "fsharp.runDefaultProject",
Expand Down Expand Up @@ -1461,7 +1463,7 @@
{
"command": "fsharp.explorer.solution.clean",
"group": "1_navigation@3",
"when": "viewItem == ionide.projectExplorer.solution"
"when": "viewItem == ionide.projectExplorer.solution "
},
{
"command": "fsharp.explorer.solution.restore",
Expand Down Expand Up @@ -1541,12 +1543,12 @@
{
"command": "fsharp.explorer.project.debug",
"group": "1_run@2",
"when": "viewItem == ionide.projectExplorer.projectExe"
"when": "viewItem == ionide.projectExplorer.projectExe \u0026\u0026 fsharp.debugger.available"
},
{
"command": "fsharp.explorer.project.debug",
"group": "inline@2",
"when": "viewItem == ionide.projectExplorer.projectExe"
"when": "viewItem == ionide.projectExplorer.projectExe \u0026\u0026 fsharp.debugger.available"
},
{
"command": "fsharp.explorer.project.setDefault",
Expand Down Expand Up @@ -1744,9 +1746,7 @@
"engines": {
"vscode": "^0.10.0"
},
"extensionDependencies": [
"ms-dotnettools.csharp"
],
"extensionDependencies": [],
"homepage": "http://ionide.io",
"icon": "images/logo.png",
"license": "MIT",
Expand Down
86 changes: 86 additions & 0 deletions src/Components/CSharpExtensionSupport.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
namespace Ionide.VSCode.FSharp

open Fable.Import.VSCode
open Fable.Import.VSCode.Vscode

module CSharpExtension =

let private msCSharpExtensionName = "ms-dotnettools.csharp"
let private openvsixCSharpExtensionName = "muhammad-sammy.csharp"

let private resolvedCSharpExtensionName =
if env.appName = "Visual Studio Code" then
msCSharpExtensionName
else
openvsixCSharpExtensionName

let mutable private hasLookedForCSharp = false
let mutable private hasCSharp = false
let mutable private csharpExtension: Extension<obj> = null
let mutable private hasWarned = false

let private csharpAvailableContext: bool -> unit =
let fn = Context.cachedSetter "fsharp.debugger.available"

fun value ->
hasCSharp <- value
fn value

let isCSharpAvailable () = hasCSharp

let tryFindCSharpExtension () =
if not hasLookedForCSharp then
match extensions.getExtension resolvedCSharpExtensionName with
| None -> csharpAvailableContext false
| Some e ->
csharpExtension <- e
csharpAvailableContext true

hasLookedForCSharp <- true

hasCSharp

let warnAboutMissingCSharpExtension () =
if not hasWarned then
window.showWarningMessage (
$"The C# extension isn't installed, so debugging and some build tools will not be available. Consider installing the C# extension to enable those features.",
[| "Install C# Extension" |]
)
|> Promise.ofThenable
|> Promise.bind (fun c ->
if c = Some "Install C# Extension" then
commands.executeCommand ("extension.open", [| Some(box resolvedCSharpExtensionName) |])
|> Promise.ofThenable
else
Promise.empty)
|> Promise.catch (fun e ->
printfn $"Error installing C# extension: {Fable.Core.JS.JSON.stringify e}"
Promise.empty)
|> ignore<Fable.Core.JS.Promise<_>>

hasWarned <- true
Comment on lines +31 to +61
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two are designed to be cheap to call, so that components that rely on the debugger can match/call them inside processing loops naievely instead of having to be truly 'reactive'


let private notifyUserThatDebuggingWorks () =
window.showInformationMessage (
$"The C# extension is installed, so debugging and build tools are now available for F# projects."
)
|> ignore<Thenable<_>>

let activate (context: ExtensionContext) =
// when extensions are installed or removed we need to update our state for the C# extension
// so enablement/disablement works correctly
context.Subscribe(
extensions.onDidChange.Invoke(fun _ ->
let previousCSharpValue = hasCSharp
hasLookedForCSharp <- false
let currentCSharpValue = tryFindCSharpExtension ()

match previousCSharpValue, currentCSharpValue with
| false, true -> notifyUserThatDebuggingWorks ()
| true, false ->
hasWarned <- false
warnAboutMissingCSharpExtension ()
| _ -> ()

None)
)
62 changes: 34 additions & 28 deletions src/Components/Debugger.fs
Original file line number Diff line number Diff line change
Expand Up @@ -336,34 +336,39 @@ module Debugger =
{ new DebugConfigurationProvider with
override x.provideDebugConfigurations(folder: option<WorkspaceFolder>, token: option<CancellationToken>) =
let generate () =
promise {
logger.Info("Evaluating launch settings configurations for %O", folder)
let projects = Project.getLoaded ()
let! msbuildTasks = tasks.fetchTasks (msbuildTasksFilter)

let tasks =
projects
|> List.collect (fun (p: Project) ->
[ let projectFile = node.path.basename p.Project

let buildTaskForProject =
msbuildTasks
|> Seq.tryFind (fun t ->
t.group = Some vscode.TaskGroup.Build && t.name = projectFile)
// emit configurations for any launchsettings for this project
match readSettingsForProject p with
| Some launchSettings ->
yield! configsForProject (p, launchSettings, buildTaskForProject)
| None -> ()
// emit a default configuration for this project if it is an executable
match defaultConfigForProject (p, buildTaskForProject) with
| Some p -> yield p
| None -> () ])

return ResizeArray tasks
}

generate () // this bix/unbox is a hack because JS types
match CSharpExtension.tryFindCSharpExtension () with
| false ->
CSharpExtension.warnAboutMissingCSharpExtension ()
promise { return ResizeArray() }
| true ->
promise {
logger.Info("Evaluating launch settings configurations for %O", folder)
let projects = Project.getLoaded ()
let! msbuildTasks = tasks.fetchTasks (msbuildTasksFilter)

let tasks =
projects
|> List.collect (fun (p: Project) ->
[ let projectFile = node.path.basename p.Project

let buildTaskForProject =
msbuildTasks
|> Seq.tryFind (fun t ->
t.group = Some vscode.TaskGroup.Build && t.name = projectFile)
// emit configurations for any launchsettings for this project
match readSettingsForProject p with
| Some launchSettings ->
yield! configsForProject (p, launchSettings, buildTaskForProject)
| None -> ()
// emit a default configuration for this project if it is an executable
match defaultConfigForProject (p, buildTaskForProject) with
| Some p -> yield p
| None -> () ])

return ResizeArray tasks
}
Comment on lines +339 to +369
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all the same logic, just wrapped in a 'if debugging exists then .. else' block


generate () // this box/unbox is a hack because JS types
|> box
|> unbox

Expand All @@ -386,6 +391,7 @@ module Debugger =
ProviderResult.Some(U2.Case1 debugConfiguration) }

let activate (c: ExtensionContext) =

commands.registerCommand ("fsharp.runDefaultProject", (buildAndRunDefault) |> objfy2)
|> c.Subscribe

Expand Down
13 changes: 0 additions & 13 deletions src/Components/MSBuild.fs
Original file line number Diff line number Diff line change
Expand Up @@ -486,19 +486,6 @@ module MSBuild =
let registerCommand com (action: unit -> _) =
commands.registerCommand (com, action |> objfy2) |> context.Subscribe

let registerCommand2 com (action: obj -> obj -> _) =
commands.registerCommand (com, action |> objfy3) |> context.Subscribe

/// typed msbuild cmd. Optional project and msbuild host
let typedMsbuildCmd f projOpt =
let p =
if JS.isDefined projOpt then
Some(unbox<string> (projOpt))
else
None

fun _ -> f p

tasks.registerTaskProvider ("msbuild", msbuildBuildTaskProvider)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two functions were unused.

|> context.Subscribe

Expand Down
Loading