@@ -10,18 +10,22 @@ open System.Threading.Tasks
1010
1111open Microsoft.CodeAnalysis
1212open Microsoft.CodeAnalysis .Completion
13+ open Microsoft.CodeAnalysis .Options
1314open Microsoft.CodeAnalysis .Text
15+ open Microsoft.CodeAnalysis .ExternalAccess .FSharp .Completion
16+
17+ open Microsoft.VisualStudio .Shell
1418
1519open FSharp.Compiler
1620open FSharp.Compiler .Range
1721open FSharp.Compiler .SourceCodeServices
18- open Microsoft.VisualStudio .Text .Adornments
19- open Microsoft.VisualStudio .Text .Editor
22+
2023module Logger = Microsoft.VisualStudio.FSharp.Editor.Logger
2124
2225type internal FSharpCompletionProvider
2326 (
2427 workspace: Workspace,
28+ serviceProvider: SVsServiceProvider,
2529 checkerProvider: FSharpCheckerProvider,
2630 projectInfoManager: FSharpProjectOptionsManager,
2731 assemblyContentProvider: AssemblyContentProvider
@@ -37,20 +41,28 @@ type internal FSharpCompletionProvider
3741 static let [<Literal>] FullNamePropName = " FullName"
3842 static let [<Literal>] IsExtensionMemberPropName = " IsExtensionMember"
3943 static let [<Literal>] NamespaceToOpenPropName = " NamespaceToOpen"
40- static let [<Literal>] IsKeywordPropName = " IsKeyword"
4144 static let [<Literal>] IndexPropName = " Index"
45+ static let [<Literal>] KeywordDescription = " KeywordDescription"
4246
4347 static let keywordCompletionItems =
4448 Keywords.KeywordsWithDescription
4549 |> List.filter ( fun ( keyword , _ ) -> not ( PrettyNaming.IsOperatorName keyword))
4650 |> List.sortBy ( fun ( keyword , _ ) -> keyword)
47-
51+ |> List.mapi ( fun n ( keyword , description ) ->
52+ FSharpCommonCompletionItem.Create(
53+ displayText = keyword,
54+ displayTextSuffix = " " ,
55+ rules = CompletionItemRules.Default,
56+ glyph = Nullable Glyph.Keyword,
57+ sortText = sprintf " %06d " ( 1000000 + n))
58+ .AddProperty( KeywordDescription, description))
4859
4960 let checker = checkerProvider.Checker
50-
61+
5162 let settings : EditorOptions = workspace.Services.GetService()
5263
53- let documentationBuilder = XmlDocumentation.Provider()
64+ let documentationBuilder = XmlDocumentation.CreateDocumentationBuilder( serviceProvider.XMLMemberIndexService)
65+
5466 static let noCommitOnSpaceRules =
5567 // These are important. They make sure we don't _commit_ autocompletion when people don't expect them to. Some examples:
5668 //
@@ -72,16 +84,16 @@ type internal FSharpCompletionProvider
7284
7385 static let mruItems = Dictionary< (* Item.FullName *) string, (* hints *) int>()
7486
75- static member ShouldTriggerCompletionAux ( sourceText : SourceText , caretPosition : int , trigger : Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionTrigger , getInfo : ( unit -> DocumentId * string * string list ), intelliSenseOptions : IntelliSenseOptions ) =
87+ static member ShouldTriggerCompletionAux ( sourceText : SourceText , caretPosition : int , trigger : CompletionTriggerKind , getInfo : ( unit -> DocumentId * string * string list ), intelliSenseOptions : IntelliSenseOptions ) =
7688 if caretPosition = 0 then
7789 false
7890 else
7991 let triggerPosition = caretPosition - 1
80- let triggerChar = trigger.Character
92+ let triggerChar = sourceText .[ triggerPosition ]
8193
82- if trigger.Reason = Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionTriggerReason .Deletion && intelliSenseOptions.ShowAfterCharIsDeleted then
83- Char.IsLetterOrDigit( triggerChar ) || triggerChar = '.'
84- elif not ( trigger.Reason = Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionTriggerReason. Insertion || trigger.Reason = Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionTriggerReason.InvokeAndCommitIfUnique ) then
94+ if trigger = CompletionTriggerKind .Deletion && intelliSenseOptions.ShowAfterCharIsDeleted then
95+ Char.IsLetterOrDigit( sourceText .[ triggerPosition ] ) || triggerChar = '.'
96+ elif not ( trigger = CompletionTriggerKind. Insertion) then
8597 false
8698 else
8799 // Do not trigger completion if it's not single dot, i.e. range expression
@@ -90,10 +102,10 @@ type internal FSharpCompletionProvider
90102 else
91103 let documentId , filePath , defines = getInfo()
92104 CompletionUtils.shouldProvideCompletion( documentId, filePath, defines, sourceText, triggerPosition) &&
93- ( trigger.Reason = Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionTriggerReason.InvokeAndCommitIfUnique || triggerChar = '.' || ( intelliSenseOptions.ShowAfterCharIsTyped && CompletionUtils.isStartingNewWord( sourceText, triggerPosition)))
105+ ( triggerChar = '.' || ( intelliSenseOptions.ShowAfterCharIsTyped && CompletionUtils.isStartingNewWord( sourceText, triggerPosition)))
94106
95107
96- static member ProvideCompletionsAsyncAux ( completionSource : Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.IAsyncCompletionSource , checker : FSharpChecker , sourceText : SourceText , caretPosition : int , options : FSharpProjectOptions , filePath : string ,
108+ static member ProvideCompletionsAsyncAux ( checker : FSharpChecker , sourceText : SourceText , caretPosition : int , options : FSharpProjectOptions , filePath : string ,
97109 textVersionHash : int , getAllSymbols : FSharpCheckFileResults -> AssemblySymbol list , languageServicePerformanceOptions : LanguageServicePerformanceOptions , intellisenseOptions : IntelliSenseOptions ) =
98110 asyncMaybe {
99111 let! parseResults , _ , checkFileResults = checker.ParseAndCheckDocument( filePath, textVersionHash, sourceText, options, languageServicePerformanceOptions, userOpName = userOpName)
@@ -112,7 +124,7 @@ type internal FSharpCompletionProvider
112124
113125 let! declarations = checkFileResults.GetDeclarationListInfo( Some( parseResults), fcsCaretLineNumber, caretLine.ToString(),
114126 partialName, getAllSymbols, userOpName= userOpName) |> liftAsync
115- let results = List< Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data .CompletionItem>()
127+ let results = List< Completion .CompletionItem>()
116128
117129 declarationItems <-
118130 declarations.Items
@@ -131,7 +143,6 @@ type internal FSharpCompletionProvider
131143
132144 declarationItems |> Array.iteri ( fun number declarationItem ->
133145 let glyph = Tokenizer.FSharpGlyphToRoslynGlyph ( declarationItem.Glyph, declarationItem.Accessibility)
134- let image = GlyphHelper.getImageId glyph |> ImageElement
135146 let name =
136147 match declarationItem.NamespaceToOpen with
137148 | Some namespaceToOpen -> sprintf " %s (open %s )" declarationItem.Name namespaceToOpen
@@ -145,38 +156,38 @@ type internal FSharpCompletionProvider
145156 // We are passing last part of long ident as FilterText.
146157 | _, idents -> Array.last idents
147158
148- let completionItem =
149- let item = new Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionItem( name, completionSource, icon = image)
150- item.Properties.AddProperty( IndexPropName, declarationItem)
151- item
152-
159+ let completionItem =
160+ FSharpCommonCompletionItem.Create( name, null , rules = getRules intellisenseOptions.ShowAfterCharIsTyped, glyph = Nullable glyph, filterText = filterText)
161+ .AddProperty( FullNamePropName, declarationItem.FullName)
162+
153163 let completionItem =
154164 match declarationItem.Kind with
155165 | CompletionItemKind.Method ( isExtension = true ) ->
156- completionItem// .AddProperty(IsExtensionMemberPropName, "")
166+ completionItem.AddProperty( IsExtensionMemberPropName, " " )
157167 | _ -> completionItem
158168
159169 let completionItem =
160170 if name <> declarationItem.NameInCode then
161- completionItem// .AddProperty(NameInCodePropName, declarationItem.NameInCode)
171+ completionItem.AddProperty( NameInCodePropName, declarationItem.NameInCode)
162172 else completionItem
163173
164174 let completionItem =
165175 match declarationItem.NamespaceToOpen with
166- | Some ns -> completionItem// .AddProperty(NamespaceToOpenPropName, ns)
176+ | Some ns -> completionItem.AddProperty( NamespaceToOpenPropName, ns)
167177 | None -> completionItem
168178
169- let completionItem = completionItem// .AddProperty(IndexPropName, string number)
179+ let completionItem = completionItem.AddProperty( IndexPropName, string number)
170180
171181 let priority =
172182 match mruItems.TryGetValue declarationItem.FullName with
173183 | true , hints -> maxHints - hints
174184 | _ -> number + maxHints + 1
175185
176186 let sortText = priority.ToString( " D6" )
177- let completionItem = completionItem// .WithSortText(sortText)
187+ let completionItem = completionItem.WithSortText( sortText)
178188 results.Add( completionItem))
179189
190+
180191 if results.Count > 0 && not declarations.IsForType && not declarations.IsError && List.isEmpty partialName.QualifyingIdents then
181192 let lineStr = textLines.[ caretLinePos.Line]. ToString()
182193
@@ -185,28 +196,46 @@ type internal FSharpCompletionProvider
185196 |> Option.bind ( fun parseTree ->
186197 UntypedParseImpl.TryGetCompletionContext( Pos.fromZ caretLinePos.Line caretLinePos.Character, parseTree, lineStr))
187198
188- let image = GlyphHelper.getImageId Glyph.Keyword |> ImageElement
189-
190199 match completionContext with
191- | None ->
192- let keywordItemsWithSource =
193- keywordCompletionItems
194- |> Seq.mapi ( fun n ( keyword , description ) ->
195- new Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionItem
196- ( keyword, completionSource, image, ImmutableArray< Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionFilter>. Empty, " " , keyword, sprintf " %06d " ( 1000000 + n), keyword, keyword, ImmutableArray< ImageElement>. Empty ))
197-
198- results.AddRange( keywordItemsWithSource)
200+ | None -> results.AddRange( keywordCompletionItems)
199201 | _ -> ()
200-
202+
201203 return results
202204 }
203205
204- override this.ProvideCompletionsAsync ( context : Completion.CompletionContext ) =
206+ override _.ShouldTriggerCompletion ( sourceText : SourceText , caretPosition : int , trigger : CompletionTrigger , _ : OptionSet ) =
207+ use _logBlock = Logger.LogBlock LogEditorFunctionId.Completion_ ShouldTrigger
208+
209+ let getInfo () =
210+ let documentId = workspace.GetDocumentIdInCurrentContext( sourceText.Container)
211+ let document = workspace.CurrentSolution.GetDocument( documentId)
212+ let defines = projectInfoManager.GetCompilationDefinesForEditingDocument( document)
213+ ( documentId, document.FilePath, defines)
214+
215+ FSharpCompletionProvider.ShouldTriggerCompletionAux( sourceText, caretPosition, trigger.Kind, getInfo, settings.IntelliSense)
216+
217+ override _.ProvideCompletionsAsync ( context : Completion.CompletionContext ) =
205218 asyncMaybe {
206- context.AddItems([]) //results)
219+ use _logBlock = Logger.LogBlockMessage context.Document.Name LogEditorFunctionId.Completion_ ProvideCompletionsAsync
220+
221+ let document = context.Document
222+ let! sourceText = context.Document.GetTextAsync( context.CancellationToken)
223+ let defines = projectInfoManager.GetCompilationDefinesForEditingDocument( document)
224+ do ! Option.guard ( CompletionUtils.shouldProvideCompletion( document.Id, document.FilePath, defines, sourceText, context.Position))
225+ let! _parsingOptions , projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject( document, context.CancellationToken)
226+ let! textVersion = context.Document.GetTextVersionAsync( context.CancellationToken)
227+ let getAllSymbols ( fileCheckResults : FSharpCheckFileResults ) =
228+ if settings.IntelliSense.IncludeSymbolsFromUnopenedNamespacesOrModules
229+ then assemblyContentProvider.GetAllEntitiesInProjectAndReferencedAssemblies( fileCheckResults)
230+ else []
231+ let! results =
232+ FSharpCompletionProvider.ProvideCompletionsAsyncAux( checker, sourceText, context.Position, projectOptions, document.FilePath,
233+ textVersion.GetHashCode(), getAllSymbols, settings.LanguageServicePerformance, settings.IntelliSense)
234+
235+ context.AddItems( results)
207236 } |> Async.Ignore |> RoslynHelpers.StartAsyncUnitAsTask context.CancellationToken
208237
209- override this .GetDescriptionAsync( document : Document , completionItem : Completion.CompletionItem , cancellationToken : CancellationToken ): Task < CompletionDescription > =
238+ override _ .GetDescriptionAsync( document : Document , completionItem : Completion.CompletionItem , cancellationToken : CancellationToken ): Task < CompletionDescription > =
210239 async {
211240 use _logBlock = Logger.LogBlockMessage document.Name LogEditorFunctionId.Completion_ GetDescriptionAsync
212241 match completionItem.Properties.TryGetValue IndexPropName with
@@ -220,27 +249,18 @@ type internal FSharpCompletionProvider
220249 // mix main description and xmldoc by using one collector
221250 XmlDocumentation.BuildDataTipText( documentationBuilder, collector, collector, collector, collector, collector, description)
222251 return CompletionDescription.Create( documentation.ToImmutableArray())
223- else
252+ else
253+ return CompletionDescription.Empty
254+ | _ ->
255+ // Try keyword descriptions if they exist
256+ match completionItem.Properties.TryGetValue KeywordDescription with
257+ | true , keywordDescription ->
258+ return CompletionDescription.FromText( keywordDescription)
259+ | false , _ ->
224260 return CompletionDescription.Empty
225- | _ ->
226- return CompletionDescription.Empty
227- } |> RoslynHelpers.StartAsyncAsTask cancellationToken
228-
229- member this.GetDescriptionAsync2 ( textView : ITextView , completionItem : Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionItem , cancellationToken : CancellationToken ): Task < CompletionDescription > =
230- async {
231- match completionItem.Properties.TryGetProperty IndexPropName with
232- | true , ( declarationItem: FSharpDeclarationListItem ) ->
233- let! description = declarationItem.StructuredDescriptionTextAsync
234- let documentation = List()
235- let collector = RoslynHelpers.CollectTaggedText documentation
236- // mix main description and xmldoc by using one collector
237- XmlDocumentation.BuildDataTipText( documentationBuilder, collector, collector, collector, collector, collector, description)
238- return CompletionDescription.Create( documentation.ToImmutableArray())
239- | _ ->
240- return CompletionDescription.Empty
241261 } |> RoslynHelpers.StartAsyncAsTask cancellationToken
242262
243- override this .GetChangeAsync( document , item , _ , cancellationToken ) : Task < CompletionChange > =
263+ override _ .GetChangeAsync( document , item , _ , cancellationToken ) : Task < CompletionChange > =
244264 async {
245265 use _logBlock = Logger.LogBlockMessage document.Name LogEditorFunctionId.Completion_ GetChangeAsync
246266
@@ -249,9 +269,8 @@ type internal FSharpCompletionProvider
249269 | true , x -> Some x
250270 | _ -> None
251271
252- // do not add extension members, keywords and not yet resolved symbols to the MRU list
253- if not ( item.Properties.ContainsKey NamespaceToOpenPropName) && not ( item.Properties.ContainsKey IsExtensionMemberPropName) &&
254- not ( item.Properties.ContainsKey IsKeywordPropName) then
272+ // do not add extension members and unresolved symbols to the MRU list
273+ if not ( item.Properties.ContainsKey NamespaceToOpenPropName) && not ( item.Properties.ContainsKey IsExtensionMemberPropName) then
255274 match fullName with
256275 | Some fullName ->
257276 match mruItems.TryGetValue fullName with
0 commit comments