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 16 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: 2 additions & 1 deletion build/paket.references
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ Fake.IO.Zip
Fake.Api.GitHub
Fake.Tools.Git
Fake.JavaScript.Yarn
Octokit
Octokit
MSBuild.StructuredLogger
3 changes: 2 additions & 1 deletion paket.dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ group build
nuget Fake.Api.GitHub
nuget Fake.Tools.Git
nuget Fake.JavaScript.Yarn
nuget Octokit <= 0.49.0
nuget Octokit <= 0.49.0
nuget MSBuild.StructuredLogger
29 changes: 14 additions & 15 deletions paket.lock
Original file line number Diff line number Diff line change
Expand Up @@ -178,24 +178,25 @@ NUGET
FSharp.Core (>= 4.7.2)
System.Reactive (>= 5.0 < 6.0)
FSharp.Core (7.0.200)
Microsoft.Build.Framework (17.4)
System.Security.Permissions (>= 6.0)
Microsoft.Build.Utilities.Core (17.4)
Microsoft.Build.Framework (>= 17.4)
Microsoft.NET.StringTools (>= 17.4)
System.Collections.Immutable (>= 6.0)
System.Configuration.ConfigurationManager (>= 6.0)
Microsoft.NET.StringTools (17.4)
System.Memory (>= 4.5.5)
System.Runtime.CompilerServices.Unsafe (>= 6.0)
Microsoft.Build.Framework (17.6.3)
System.Security.Permissions (>= 7.0)
Microsoft.Build.Utilities.Core (17.6.3)
Microsoft.Build.Framework (>= 17.6.3)
Microsoft.NET.StringTools (>= 17.6.3)
Microsoft.VisualStudio.Setup.Configuration.Interop (>= 3.2.2146)
System.Collections.Immutable (>= 7.0)
System.Configuration.ConfigurationManager (>= 7.0)
System.Security.Permissions (>= 7.0)
Microsoft.NET.StringTools (17.6.3)
Microsoft.VisualStudio.Setup.Configuration.Interop (3.6.2115)
Microsoft.Win32.Registry (5.0)
System.Security.AccessControl (>= 5.0)
System.Security.Principal.Windows (>= 5.0)
Microsoft.Win32.SystemEvents (7.0)
Mono.Posix.NETStandard (1.0)
MSBuild.StructuredLogger (2.1.768)
Microsoft.Build.Framework (>= 16.10)
Microsoft.Build.Utilities.Core (>= 16.10)
MSBuild.StructuredLogger (2.1.844)
Microsoft.Build.Framework (>= 17.5)
Microsoft.Build.Utilities.Core (>= 17.5)
Newtonsoft.Json (13.0.2)
NuGet.Common (6.4)
NuGet.Frameworks (>= 6.4)
Expand All @@ -222,9 +223,7 @@ NUGET
System.Drawing.Common (7.0)
Microsoft.Win32.SystemEvents (>= 7.0)
System.Formats.Asn1 (7.0)
System.Memory (4.5.5)
System.Reactive (5.0)
System.Runtime.CompilerServices.Unsafe (6.0)
System.Security.AccessControl (6.0)
System.Security.Cryptography.Cng (5.0)
System.Formats.Asn1 (>= 5.0)
Expand Down
22 changes: 11 additions & 11 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 @@ -1213,7 +1215,7 @@
},
{
"command": "fsharp.debugDefaultProject",
"when": "fsharp.project.any"
"when": "fsharp.project.any \u0026\u0026 fsharp.debugger.available"
},
{
"command": "fsharp.chooseDefaultProject",
Expand Down Expand Up @@ -1261,7 +1263,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 @@ -1456,7 +1458,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 @@ -1536,12 +1538,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 @@ -1739,9 +1741,7 @@
"engines": {
"vscode": "^0.10.0"
},
"extensionDependencies": [
"ms-dotnettools.csharp"
],
"extensionDependencies": [],
"homepage": "http://ionide.io",
"icon": "images/logo.png",
"license": "MIT",
Expand All @@ -1754,4 +1754,4 @@
"url": "https://github.com/ionide/ionide-vscode-fsharp.git"
},
"version": "7.8.5"
}
}
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
4 changes: 3 additions & 1 deletion src/Components/LineLens/LineLens.fs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ module DecorationUpdate =
LanguageService.signatureData uri range.StartLine (range.StartColumn - 1)
|> Async.AwaitPromise

return signaturesResult |> Option.map (fun r -> range|>CodeRange.fromDTO, formatSignature r.Data)
return
signaturesResult
|> Option.map (fun r -> range |> CodeRange.fromDTO, formatSignature r.Data)
with e ->
logger.Error("Error getting signature %o", e)
return None
Expand Down
13 changes: 0 additions & 13 deletions src/Components/MSBuild.fs
Original file line number Diff line number Diff line change
Expand Up @@ -542,19 +542,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