Skip to content

Commit 271790c

Browse files
Use background CancellableTask in VS instead of async & asyncMaybe (#15187)
* wip * iteration * iteration: quickinfo, help context * fantomas * todo * moved tasks to editor project, fixed comment colouring bug * fantomas * Fantomas + PR feedback * Update vsintegration/src/FSharp.Editor/Hints/HintService.fs Co-authored-by: Andrii Chebukin <xperiandri@live.ru> * Revert "Update vsintegration/src/FSharp.Editor/Hints/HintService.fs" This reverts commit bf51b31. --------- Co-authored-by: Andrii Chebukin <xperiandri@live.ru>
1 parent 3cdf2d2 commit 271790c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2028
-527
lines changed

.fantomasignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ artifacts/
1515
# For some reason, it tries to format files from remotes (Processing .\.git\refs\remotes\<remote>\FSComp.fsi)
1616
.git/
1717

18-
1918
# Explicitly unformatted implementation
2019
src/Compiler/Checking/AccessibilityLogic.fs
2120
src/Compiler/Checking/AttributeChecking.fs
@@ -98,6 +97,7 @@ src/Compiler/Service/IncrementalBuild.fs
9897
src/Compiler/Service/ServiceAssemblyContent.fs
9998
src/Compiler/Service/ServiceDeclarationLists.fs
10099
src/Compiler/Service/ServiceErrorResolutionHints.fs
100+
vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs
101101

102102
# Fantomas limitations on signature files (to investigate)
103103

vsintegration/src/FSharp.Editor/Classification/ClassificationDefinitions.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ open Microsoft.VisualStudio.Text.Classification
1717
open Microsoft.VisualStudio.Utilities
1818
open Microsoft.CodeAnalysis.Classification
1919

20-
open FSharp.Compiler.CodeAnalysis
2120
open FSharp.Compiler.EditorServices
2221

2322
[<RequireQualifiedAccess>]

vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs

Lines changed: 23 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,17 @@ open System
66
open System.Composition
77
open System.Collections.Generic
88
open System.Collections.Immutable
9-
open System.Diagnostics
109
open System.Threading
1110
open System.Runtime.Caching
1211

1312
open Microsoft.CodeAnalysis
1413
open Microsoft.CodeAnalysis.Classification
15-
open Microsoft.CodeAnalysis.Editor
16-
open Microsoft.CodeAnalysis.Host.Mef
1714
open Microsoft.CodeAnalysis.Text
1815
open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Classification
1916

20-
open FSharp.Compiler.CodeAnalysis
2117
open FSharp.Compiler.EditorServices
2218
open FSharp.Compiler.Tokenization
19+
open CancellableTasks
2320
open Microsoft.VisualStudio.FSharp.Editor.Telemetry
2421

2522
// IEditorClassificationService is marked as Obsolete, but is still supported. The replacement (IClassificationService)
@@ -32,49 +29,11 @@ open Microsoft.VisualStudio.FSharp.Editor.Telemetry
3229
type SemanticClassificationData = SemanticClassificationView
3330
type SemanticClassificationLookup = IReadOnlyDictionary<int, ResizeArray<SemanticClassificationItem>>
3431

35-
[<Sealed>]
36-
type DocumentCache<'Value when 'Value: not struct>() =
37-
/// Anything under two seconds, the caching stops working, meaning it won't actually cache the item.
38-
/// Two seconds is just enough to keep the data around long enough to handle a flood of a requests asking for the same data
39-
/// in a short period of time.
40-
[<Literal>]
41-
let slidingExpirationSeconds = 2.
42-
43-
let cache = new MemoryCache("fsharp-cache")
44-
45-
let policy =
46-
CacheItemPolicy(SlidingExpiration = TimeSpan.FromSeconds slidingExpirationSeconds)
47-
48-
member _.TryGetValueAsync(doc: Document) =
49-
async {
50-
let! ct = Async.CancellationToken
51-
let! currentVersion = doc.GetTextVersionAsync ct |> Async.AwaitTask
52-
53-
match cache.Get(doc.Id.ToString()) with
54-
| null -> return ValueNone
55-
| :? (VersionStamp * 'Value) as value ->
56-
if fst value = currentVersion then
57-
return ValueSome(snd value)
58-
else
59-
return ValueNone
60-
| _ -> return ValueNone
61-
}
62-
63-
member _.SetAsync(doc: Document, value: 'Value) =
64-
async {
65-
let! ct = Async.CancellationToken
66-
let! currentVersion = doc.GetTextVersionAsync ct |> Async.AwaitTask
67-
cache.Set(doc.Id.ToString(), (currentVersion, value), policy)
68-
}
69-
70-
interface IDisposable with
71-
72-
member _.Dispose() = cache.Dispose()
73-
7432
[<Export(typeof<IFSharpClassificationService>)>]
7533
type internal FSharpClassificationService [<ImportingConstructor>] () =
7634

77-
static let getLexicalClassifications (filePath: string, defines, text: SourceText, textSpan: TextSpan, ct) =
35+
static let getLexicalClassifications (filePath: string, defines, text: SourceText, textSpan: TextSpan, ct: CancellationToken) =
36+
7837
let text = text.GetSubText(textSpan)
7938
let result = ImmutableArray.CreateBuilder()
8039

@@ -144,8 +103,7 @@ type internal FSharpClassificationService [<ImportingConstructor>] () =
144103
| _ -> ()
145104

146105
static let toSemanticClassificationLookup (d: SemanticClassificationData) =
147-
let lookup =
148-
System.Collections.Generic.Dictionary<int, ResizeArray<SemanticClassificationItem>>()
106+
let lookup = Dictionary<int, ResizeArray<SemanticClassificationItem>>()
149107

150108
let f (dataItem: SemanticClassificationItem) =
151109
let items =
@@ -160,9 +118,10 @@ type internal FSharpClassificationService [<ImportingConstructor>] () =
160118

161119
d.ForEach(f)
162120

163-
System.Collections.ObjectModel.ReadOnlyDictionary lookup :> IReadOnlyDictionary<_, _>
121+
Collections.ObjectModel.ReadOnlyDictionary lookup :> IReadOnlyDictionary<_, _>
164122

165-
let semanticClassificationCache = new DocumentCache<SemanticClassificationLookup>()
123+
let semanticClassificationCache =
124+
new DocumentCache<SemanticClassificationLookup>("fsharp-semantic-classification-cache")
166125

167126
interface IFSharpClassificationService with
168127
// Do not perform classification if we don't have project options (#defines matter)
@@ -175,11 +134,13 @@ type internal FSharpClassificationService [<ImportingConstructor>] () =
175134
result: List<ClassifiedSpan>,
176135
cancellationToken: CancellationToken
177136
) =
178-
async {
137+
cancellableTask {
179138
use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Syntactic)
180139

140+
let! cancellationToken = CancellableTask.getCurrentCancellationToken ()
141+
181142
let defines, langVersion = document.GetFSharpQuickDefinesAndLangVersion()
182-
let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask
143+
let! sourceText = document.GetTextAsync(cancellationToken)
183144

184145
// For closed documents, only get classification for the text within the span.
185146
// This may be inaccurate for multi-line tokens such as string literals, but this is ok for now
@@ -198,7 +159,10 @@ type internal FSharpClassificationService [<ImportingConstructor>] () =
198159
TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSyntacticCalssifications, eventProps)
199160

200161
if not isOpenDocument then
201-
result.AddRange(getLexicalClassifications (document.FilePath, defines, sourceText, textSpan, cancellationToken))
162+
let classifiedSpans =
163+
getLexicalClassifications (document.FilePath, defines, sourceText, textSpan, cancellationToken)
164+
165+
result.AddRange(classifiedSpans)
202166
else
203167
result.AddRange(
204168
Tokenizer.getClassifiedSpans (
@@ -212,7 +176,7 @@ type internal FSharpClassificationService [<ImportingConstructor>] () =
212176
)
213177
)
214178
}
215-
|> RoslynHelpers.StartAsyncUnitAsTask cancellationToken
179+
|> CancellableTask.startAsTask cancellationToken
216180

217181
member _.AddSemanticClassificationsAsync
218182
(
@@ -221,10 +185,10 @@ type internal FSharpClassificationService [<ImportingConstructor>] () =
221185
result: List<ClassifiedSpan>,
222186
cancellationToken: CancellationToken
223187
) =
224-
async {
188+
cancellableTask {
225189
use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Semantic)
226190

227-
let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask
191+
let! sourceText = document.GetTextAsync(cancellationToken)
228192

229193
// If we are trying to get semantic classification for a document that is not open, get the results from the background and cache it.
230194
// We do this for find all references when it is populating results.
@@ -248,9 +212,11 @@ type internal FSharpClassificationService [<ImportingConstructor>] () =
248212

249213
addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result
250214
| _ ->
251-
let eventProps =
215+
let eventProps: (string * obj) array =
252216
[|
253-
"isOpenDocument", isOpenDocument :> obj
217+
"context.document.project.id", document.Project.Id.Id.ToString()
218+
"context.document.id", document.Id.Id.ToString()
219+
"isOpenDocument", isOpenDocument
254220
"textSpanLength", textSpan.Length
255221
"cacheHit", false
256222
|]
@@ -283,8 +249,7 @@ type internal FSharpClassificationService [<ImportingConstructor>] () =
283249
let classificationData = checkResults.GetSemanticClassification(Some targetRange)
284250
addSemanticClassification sourceText textSpan classificationData result
285251
}
286-
|> Async.Ignore
287-
|> RoslynHelpers.StartAsyncUnitAsTask cancellationToken
252+
|> CancellableTask.startAsTask cancellationToken
288253

289254
// Do not perform classification if we don't have project options (#defines matter)
290255
member _.AdjustStaleClassification(_: SourceText, classifiedSpan: ClassifiedSpan) : ClassifiedSpan = classifiedSpan

vsintegration/src/FSharp.Editor/Commands/HelpContextService.fs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@ open FSharp.Compiler.EditorServices
1414
open FSharp.Compiler.Syntax
1515
open FSharp.Compiler.Text
1616
open Microsoft.CodeAnalysis
17+
open CancellableTasks
1718

1819
[<Shared>]
1920
[<ExportLanguageService(typeof<IHelpContextService>, FSharpConstants.FSharpLanguageName)>]
2021
type internal FSharpHelpContextService [<ImportingConstructor>] () =
2122

22-
static member GetHelpTerm(document: Document, span: TextSpan, tokens: List<ClassifiedSpan>) : Async<string option> =
23-
asyncMaybe {
24-
let! _, check =
25-
document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpHelpContextService))
26-
|> liftAsync
23+
static member GetHelpTerm(document: Document, span: TextSpan, tokens: List<ClassifiedSpan>) : CancellableTask<string> =
24+
cancellableTask {
25+
let! cancellationToken = CancellableTask.getCurrentCancellationToken ()
26+
let! _, check = document.GetFSharpParseAndCheckResultsAsync(nameof (FSharpHelpContextService))
2727

28-
let! sourceText = document.GetTextAsync() |> liftTaskAsync
28+
let! sourceText = document.GetTextAsync(cancellationToken)
2929
let textLines = sourceText.Lines
3030
let lineInfo = textLines.GetLineFromPosition(span.Start)
3131
let line = lineInfo.LineNumber
@@ -76,7 +76,7 @@ type internal FSharpHelpContextService [<ImportingConstructor>] () =
7676
| otherwise -> otherwise, col
7777

7878
match tokenInformation with
79-
| None -> return! None
79+
| None -> return ""
8080
| Some token ->
8181
match token.ClassificationType with
8282
| ClassificationTypeNames.Keyword
@@ -85,25 +85,31 @@ type internal FSharpHelpContextService [<ImportingConstructor>] () =
8585
| ClassificationTypeNames.Comment -> return "comment_FS"
8686
| ClassificationTypeNames.Identifier ->
8787
try
88-
let! (s, colAtEndOfNames, _) = QuickParse.GetCompleteIdentifierIsland false lineText col
88+
let island = QuickParse.GetCompleteIdentifierIsland false lineText col
8989

90-
if check.HasFullTypeCheckInfo then
90+
match island with
91+
| Some (s, colAtEndOfNames, _) when check.HasFullTypeCheckInfo ->
9192
let qualId = PrettyNaming.GetLongNameFromString s
92-
return! check.GetF1Keyword(Line.fromZ line, colAtEndOfNames, lineText, qualId)
93-
else
94-
return! None
93+
94+
let f1Keyword =
95+
check.GetF1Keyword(Line.fromZ line, colAtEndOfNames, lineText, qualId)
96+
97+
return Option.defaultValue "" f1Keyword
98+
99+
| _ -> return ""
95100
with e ->
96101
Assert.Exception e
97-
return! None
98-
| _ -> return! None
102+
return ""
103+
| _ -> return ""
99104
}
100105

101106
interface IHelpContextService with
102107
member this.Language = FSharpConstants.FSharpLanguageLongName
103108
member this.Product = FSharpConstants.FSharpLanguageLongName
104109

105110
member this.GetHelpTermAsync(document, textSpan, cancellationToken) =
106-
asyncMaybe {
111+
cancellableTask {
112+
let! cancellationToken = CancellableTask.getCurrentCancellationToken ()
107113
let! sourceText = document.GetTextAsync(cancellationToken)
108114
let defines, langVersion = document.GetFSharpQuickDefinesAndLangVersion()
109115
let textLine = sourceText.Lines.GetLineFromPosition(textSpan.Start)
@@ -121,7 +127,6 @@ type internal FSharpHelpContextService [<ImportingConstructor>] () =
121127

122128
return! FSharpHelpContextService.GetHelpTerm(document, textSpan, classifiedSpans)
123129
}
124-
|> Async.map (Option.defaultValue "")
125-
|> RoslynHelpers.StartAsyncAsTask cancellationToken
130+
|> CancellableTask.start cancellationToken
126131

127132
member this.FormatSymbol(_symbol) = Unchecked.defaultof<_>

0 commit comments

Comments
 (0)