Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into main-to-nightly
Browse files Browse the repository at this point in the history
  • Loading branch information
TheAngryByrd committed May 6, 2024
2 parents 8422032 + e9ad5eb commit 07e4c43
Show file tree
Hide file tree
Showing 17 changed files with 267 additions and 82 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jobs:
uses: actions/setup-dotnet@v2
with:
dotnet-version: |
8.0.x
7.0.x
6.0.x
include-prerelease: true
Expand All @@ -29,7 +30,7 @@ jobs:
- name: Run Build
run: dotnet pack -c Release -o ./bin
env:
BuildNet7: true
BuildNet8: true

- name: Get Changelog Entry
id: changelog_reader
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Changelog

## [0.72.3] - 2024-05-05

### Added

* [FSAC publishes a net8.0 TFM version of the tool as well, to prevent issues when running across TargetFrameworks](https://github.com/ionide/FsAutoComplete/pull/1281)
* [Long-running actions like typechecking specific files can now be cancelled by users](https://github.com/ionide/FsAutoComplete/pull/1274) (thanks @TheAngryByrd)

### Fixed

* [Fix restoring multiple script file NuGet dependencies in parallel](https://github.com/ionide/FsAutoComplete/pull/1275) (thanks @TheAngryByrd)

## [0.72.2] - 2024-04-30

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<NoWarn>$(NoWarn);57</NoWarn> <!-- Enable experimental compiler features -->
<WarnOn Condition="'$(Configuration)' != 'Debug'">$(WarnOn);1182</WarnOn> <!-- Unused
variables,https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/compiler-options#opt-in-warnings -->

<NoWarn>$(NoWarn);FS0044</NoWarn> <!-- Ignore deprecations -->
<WarnOn>$(WarnOn);3390</WarnOn><!-- Malformed XML doc comments -->
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<ChangelogFile>$(MSBuildThisFileDirectory)CHANGELOG.md</ChangelogFile>
Expand Down
3 changes: 2 additions & 1 deletion paket.dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ nuget Expecto.Diff
nuget YoloDev.Expecto.TestSdk
nuget AltCover
nuget GitHubActionsTestLogger
nuget Ionide.LanguageServerProtocol >= 0.4.20
nuget Ionide.LanguageServerProtocol >= 0.4.23
nuget Microsoft.Extensions.Caching.Memory
nuget OpenTelemetry.Api >= 1.3.2
nuget OpenTelemetry.Exporter.OpenTelemetryProtocol >= 1.3.2 # 1.4 bumps to 7.0 versions of System.Diagnostics libs, so can't use it
nuget OpenTelemetry.Instrumentation.Runtime
nuget LinkDotNet.StringBuilder 1.18.0
nuget CommunityToolkit.HighPerformance
nuget System.Security.Cryptography.Pkcs 6.0.4
Expand Down
4 changes: 3 additions & 1 deletion paket.lock
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ NUGET
System.Reflection.Metadata (>= 5.0)
Ionide.Analyzers (0.10)
Ionide.KeepAChangelog.Tasks (0.1.8) - copy_local: true
Ionide.LanguageServerProtocol (0.4.20)
Ionide.LanguageServerProtocol (0.4.23)
FSharp.Core (>= 6.0)
Newtonsoft.Json (>= 13.0.1)
StreamJsonRpc (>= 2.16.36)
Expand Down Expand Up @@ -357,6 +357,8 @@ NUGET
Grpc (>= 2.44 < 3.0) - restriction: || (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net7.0) (>= net462)) (&& (== net7.0) (< netstandard2.1)) (&& (== net8.0) (>= net462)) (&& (== net8.0) (< netstandard2.1)) (== netstandard2.0) (&& (== netstandard2.1) (>= net462))
Grpc.Net.Client (>= 2.43 < 3.0) - restriction: || (== net6.0) (== net7.0) (== net8.0) (&& (== netstandard2.0) (>= netstandard2.1)) (== netstandard2.1)
OpenTelemetry (>= 1.3.2)
OpenTelemetry.Instrumentation.Runtime (1.0)
OpenTelemetry.Api (>= 1.3 < 2.0)
Perfolizer (0.2.1)
System.Memory (>= 4.5.3)
runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.2)
Expand Down
1 change: 0 additions & 1 deletion src/FsAutoComplete.Core/CompilerServiceInterface.fs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,6 @@ type FSharpCompilerServiceChecker(hasAnalyzers, typecheckCacheSize, parallelRefe
}



member __.ScriptTypecheckRequirementsChanged =
scriptTypecheckRequirementsChanged.Publish

Expand Down
14 changes: 8 additions & 6 deletions src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -578,8 +578,8 @@ type AdaptiveFSharpLspServer
// Otherwise we'll fail here and our retry logic will come into place
do!
match p.Context with
| Some({ triggerKind = CompletionTriggerKind.TriggerCharacter } as context) ->
volatileFile.Source.TryGetChar pos = context.triggerCharacter
| Some({ TriggerKind = CompletionTriggerKind.TriggerCharacter } as context) ->
volatileFile.Source.TryGetChar pos = context.TriggerCharacter
| _ -> true
|> Result.requireTrue $"TextDocumentCompletion was sent before TextDocumentDidChange"

Expand Down Expand Up @@ -2979,25 +2979,27 @@ type AdaptiveFSharpLspServer

override x.Dispose() = disposables.Dispose()

member this.WorkDoneProgressCancel(token: ProgressToken) : Async<unit> =
member this.WorkDoneProgressCancel(param: WorkDoneProgressCancelParams) : Async<unit> =
async {

let tags = [ "ProgressToken", box token ]
let tags = [ "WorkDoneProgressCancelParams", box param ]
use trace = fsacActivitySource.StartActivityForType(thisType, tags = tags)

try
logger.info (
Log.setMessage "WorkDoneProgressCancel Request: {params}"
>> Log.addContextDestructured "params" token
>> Log.addContextDestructured "params" param.token
)

state.CancelServerProgress param.token

with e ->
trace |> Tracing.recordException e

logException
e
(Log.setMessage "WorkDoneProgressCancel Request Errored {p}"
>> Log.addContextDestructured "token" token)
>> Log.addContextDestructured "token" param.token)

return ()
}
Expand Down
110 changes: 94 additions & 16 deletions src/FsAutoComplete/LspServers/AdaptiveServerState.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace FsAutoComplete.Lsp
open System
open System.IO
open System.Threading
open System.Collections.Generic
open FsAutoComplete
open FsAutoComplete.CodeFix
open FsAutoComplete.Logging
Expand Down Expand Up @@ -39,6 +40,68 @@ open FSharp.Compiler.Syntax
open FsAutoComplete.ProjectWorkspace


/// <summary>Handle tracking in-flight ServerProgressReport and allow cancellation of actions if a client decides to.</summary>
type ServerProgressLookup() =
// This is a dictionary of all the progress reports that are currently active
// Use a WeakReference to avoid memory leaks
let progressTable = Dictionary<ProgressToken, WeakReference<ServerProgressReport>>()

// Although it's small data, we don't want to keep it around forever
let timer =
new Timers.Timer((TimeSpan.FromMinutes 1.).TotalMilliseconds, AutoReset = true)

let timerSub =
timer.Elapsed.Subscribe(fun _ ->
lock progressTable (fun () ->
let derefs =
progressTable
|> Seq.filter (fun (KeyValue(_, reporters)) ->
match reporters.TryGetTarget() with
| (true, _) -> false
| _ -> true)
|> Seq.toList

for (KeyValue(tokens, _)) in derefs do
progressTable.Remove(tokens) |> ignore<bool>))


/// <summary>Creates a ServerProgressReport and keeps track of it.</summary>
/// <param name="lspClient">The FSharpLspClient to communicate to the client with.</param>
/// <param name="token">Optional token. It will be generated otherwise.</param>
/// <param name="cancellable">Informs that the ServerProgressReport is cancellable to the client.</param>
/// <returns></returns>
member x.CreateProgressReport(lspClient: FSharpLspClient, ?token: ProgressToken, ?cancellable: bool) =
let progress =
new ServerProgressReport(lspClient, ?token = token, ?cancellableDefault = cancellable)

lock progressTable (fun () ->
progressTable.Add(progress.ProgressToken, new WeakReference<ServerProgressReport>(progress)))

progress


/// <summary>Signal a ServerProgressReport to Cancel it's CancellationTokenSource.</summary>
/// <param name="token">The ProgressToken used to identify the ServerProgressReport</param>
///
/// <remarks>
/// See <see href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_workDoneProgress_cancel">LSP Spec on WorkDoneProgress Cancel</see> for more information.
/// </remarks>
member x.Cancel(token: ProgressToken) =
lock progressTable (fun () ->
match progressTable.TryGetValue(token) with
| true, weakRef ->
match weakRef.TryGetTarget() with
| true, progress ->
progress.Cancel()
progressTable.Remove(token) |> ignore<bool>
| _ -> ()
| _ -> ())

interface IDisposable with
member x.Dispose() =
timerSub.Dispose()
timer.Dispose()

[<RequireQualifiedAccess>]
type WorkspaceChosen =
| Projs of HashSet<string<LocalPath>>
Expand Down Expand Up @@ -101,6 +164,8 @@ type AdaptiveState
let logger = LogProvider.getLoggerFor<AdaptiveState> ()
let thisType = typeof<AdaptiveState>
let disposables = new Disposables.CompositeDisposable()
let progressLookup = new ServerProgressLookup()
do disposables.Add progressLookup


let projectSelector = cval<IFindProject> (FindFirstProject())
Expand Down Expand Up @@ -324,10 +389,12 @@ type AdaptiveState
let checkUnusedOpens =
asyncEx {
try
use progress = new ServerProgressReport(lspClient)
use progress = progressLookup.CreateProgressReport(lspClient, cancellable = true)
do! progress.Begin($"Checking unused opens {fileName}...", message = filePathUntag)

let! unused = UnusedOpens.getUnusedOpens (tyRes.GetCheckResults, getSourceLine)
let! unused =
UnusedOpens.getUnusedOpens (tyRes.GetCheckResults, getSourceLine)
|> Async.withCancellation progress.CancellationToken

let! ct = Async.CancellationToken
notifications.Trigger(NotificationEvent.UnusedOpens(filePath, (unused |> List.toArray), file.Version), ct)
Expand All @@ -338,11 +405,15 @@ type AdaptiveState
let checkUnusedDeclarations =
asyncEx {
try
use progress = new ServerProgressReport(lspClient)
use progress = progressLookup.CreateProgressReport(lspClient, cancellable = true)
do! progress.Begin($"Checking unused declarations {fileName}...", message = filePathUntag)

let isScript = Utils.isAScript (filePathUntag)
let! unused = UnusedDeclarations.getUnusedDeclarations (tyRes.GetCheckResults, isScript)

let! unused =
UnusedDeclarations.getUnusedDeclarations (tyRes.GetCheckResults, isScript)
|> Async.withCancellation progress.CancellationToken

let unused = unused |> Seq.toArray

let! ct = Async.CancellationToken
Expand All @@ -354,10 +425,13 @@ type AdaptiveState
let checkSimplifiedNames =
asyncEx {
try
use progress = new ServerProgressReport(lspClient)
use progress = progressLookup.CreateProgressReport(lspClient, cancellable = true)
do! progress.Begin($"Checking simplifying of names {fileName}...", message = filePathUntag)

let! simplified = SimplifyNames.getSimplifiableNames (tyRes.GetCheckResults, getSourceLine)
let! simplified =
SimplifyNames.getSimplifiableNames (tyRes.GetCheckResults, getSourceLine)
|> Async.withCancellation progress.CancellationToken

let simplified = Array.ofSeq simplified
let! ct = Async.CancellationToken
notifications.Trigger(NotificationEvent.SimplifyNames(filePath, simplified, file.Version), ct)
Expand All @@ -368,7 +442,7 @@ type AdaptiveState
let checkUnnecessaryParentheses =
asyncEx {
try
use progress = new ServerProgressReport(lspClient)
use progress = progressLookup.CreateProgressReport(lspClient)
do! progress.Begin($"Checking for unnecessary parentheses {fileName}...", message = filePathUntag)

let unnecessaryParentheses =
Expand Down Expand Up @@ -1520,9 +1594,9 @@ type AdaptiveState
>> Log.addContextDestructured "date" (file.LastTouched)
)

let! ct = Async.CancellationToken

use progressReport = new ServerProgressReport(lspClient)
use progressReport =
progressLookup.CreateProgressReport(lspClient, cancellable = true)

let simpleName = Path.GetFileName(UMX.untag file.Source.FileName)
do! progressReport.Begin($"Typechecking {simpleName}", message = $"{file.Source.FileName}")
Expand All @@ -1542,6 +1616,8 @@ type AdaptiveState
)


let! ct = Async.CancellationToken

notifications.Trigger(NotificationEvent.FileParsed(file.Source.FileName), ct)

match result with
Expand Down Expand Up @@ -2219,8 +2295,6 @@ type AdaptiveState

let! projs = getProjectOptionsForFile sourceFilePath |> AsyncAVal.forceAsync

let rootToken = sourceFilePath |> getOpenFileTokenOrDefault

let projs =
projs
|> Result.toOption
Expand All @@ -2236,7 +2310,8 @@ type AdaptiveState

let mutable checksCompleted = 0

use progressReporter = new ServerProgressReport(lspClient)
use progressReporter =
progressLookup.CreateProgressReport(lspClient, cancellable = true)

let percentage numerator denominator =
if denominator = 0 then
Expand All @@ -2254,6 +2329,7 @@ type AdaptiveState
&& file.Contains "AssemblyAttributes.fs" |> not)

let checksToPerformLength = innerChecks.Length
let rootToken = sourceFilePath |> getOpenFileTokenOrDefault

innerChecks
|> Array.map (fun (snap, file) ->
Expand All @@ -2262,14 +2338,13 @@ type AdaptiveState
use joinedToken =
if file = sourceFilePath then
// dont reset the token for the incoming file as it would cancel the whole operation
CancellationTokenSource.CreateLinkedTokenSource(rootToken)
CancellationTokenSource.CreateLinkedTokenSource(rootToken, progressReporter.CancellationToken)
else
// only cancel other files
// If we have multiple saves from separate root files we want only one to be running
let token = resetCancellationToken file None // Dont dispose, we're a renter not an owner
let fileToken = resetCancellationToken file None
// and join with the root token as well since we want to cancel the whole operation if the root files changes
CancellationTokenSource.CreateLinkedTokenSource(rootToken, token)
// CancellationTokenSource.CreateLinkedTokenSource(rootToken)
CancellationTokenSource.CreateLinkedTokenSource(rootToken, fileToken, progressReporter.CancellationToken)

try
let! _ =
Expand Down Expand Up @@ -2440,6 +2515,9 @@ type AdaptiveState

member x.GlyphToSymbolKind = glyphToSymbolKind |> AVal.force

member x.CancelServerProgress(progressToken: ProgressToken) = progressLookup.Cancel progressToken


interface IDisposable with
member this.Dispose() =

Expand Down
8 changes: 8 additions & 0 deletions src/FsAutoComplete/LspServers/AdaptiveServerState.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,12 @@ type AdaptiveState =
member GetDeclarations: filename: string<LocalPath> -> Async<Result<NavigationTopLevelDeclaration array, string>>
member GetAllDeclarations: unit -> Async<(string<LocalPath> * NavigationTopLevelDeclaration array) array>
member GlyphToSymbolKind: (FSharpGlyph -> SymbolKind option)
/// <summary>
/// Signals the server to cancel an operation that is associated with the given progress token.
/// </summary>
///
/// <remarks>
/// See <see href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_workDoneProgress_cancel">LSP Spec on WorkDoneProgress Cancel</see> for more information.
/// </remarks>
member CancelServerProgress: progressToken: ProgressToken -> unit
interface IDisposable
2 changes: 1 addition & 1 deletion src/FsAutoComplete/LspServers/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ module Async =
let! ct2 = Async.CancellationToken
use cts = CancellationTokenSource.CreateLinkedTokenSource(ct, ct2)
let tcs = new TaskCompletionSource<'a>()
use _reg = cts.Token.Register(fun () -> tcs.TrySetCanceled() |> ignore)
use _reg = cts.Token.Register(fun () -> tcs.TrySetCanceled(cts.Token) |> ignore)

let a =
async {
Expand Down
Loading

0 comments on commit 07e4c43

Please sign in to comment.