diff --git a/RoslynPackageVersion.txt b/RoslynPackageVersion.txt index fd1d6f6d9bc..cc99af67911 100644 --- a/RoslynPackageVersion.txt +++ b/RoslynPackageVersion.txt @@ -1 +1 @@ -3.1.0-beta3-19222-02 +3.2.0-beta4-19312-15 diff --git a/eng/Versions.props b/eng/Versions.props index 3cd88abc9bf..258d4255ad6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -55,6 +55,7 @@ https://dotnet.myget.org/F/roslyn-tools/api/v3/index.json; https://api.nuget.org/v3/index.json; https://dotnet.myget.org/F/roslyn/api/v3/index.json; + https://dotnet.myget.org/F/roslyn-analyzers/api/v3/index.json; https://dotnet.myget.org/F/symreader-converter/api/v3/index.json; https://dotnet.myget.org/F/interactive-window/api/v3/index.json; https://myget.org/F/vs-devcore/api/v3/index.json; diff --git a/vsintegration/src/FSharp.Editor/BlockComment/CommentSelectionService.fs b/vsintegration/src/FSharp.Editor/BlockComment/CommentSelectionService.fs deleted file mode 100644 index 4c54a45a519..00000000000 --- a/vsintegration/src/FSharp.Editor/BlockComment/CommentSelectionService.fs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Microsoft.VisualStudio.FSharp.Editor - -open Microsoft.CodeAnalysis.CommentSelection -open Microsoft.CodeAnalysis.Host.Mef -open System.Composition -open System.Threading.Tasks - -[] -[, FSharpConstants.FSharpLanguageName)>] -type CommentSelectionService() = - interface ICommentSelectionService with - member this.GetInfoAsync(_document, _textSpan, _cancellationToken) = - Task.FromResult(CommentSelectionInfo(supportsSingleLineComment=true, - supportsBlockComment=true, - singleLineCommentString="//", - blockCommentStartString="(*", - blockCommentEndString="*)")) - - member this.FormatAsync(document, _changes, _cancellationToken) = Task.FromResult(document) diff --git a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs index e2ccc7e22c8..8b659aafb32 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/RemoveUnusedOpens.fs @@ -10,6 +10,7 @@ open Microsoft.CodeAnalysis.Diagnostics open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes open Microsoft.CodeAnalysis.CodeActions +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics open FSharp.Compiler.Range @@ -21,7 +22,7 @@ type internal FSharpRemoveUnusedOpensCodeFixProvider projectInfoManager: FSharpProjectOptionsManager ) = inherit CodeFixProvider() - let fixableDiagnosticIds = [IDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId] + let fixableDiagnosticIds = [FSharpIDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId] let createCodeFix (title: string, context: CodeFixContext) = CodeAction.Create( diff --git a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs b/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs index aeeae1773ef..a2768640387 100644 --- a/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs +++ b/vsintegration/src/FSharp.Editor/CodeFix/SimplifyName.fs @@ -10,12 +10,13 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Diagnostics open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.CodeFixes +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics open SymbolHelpers -[] +[] type internal FSharpSimplifyNameCodeFixProvider() = inherit CodeFixProvider() - let fixableDiagnosticId = IDEDiagnosticIds.SimplifyNamesDiagnosticId + let fixableDiagnosticId = FSharpIDEDiagnosticIds.SimplifyNamesDiagnosticId override __.FixableDiagnosticIds = ImmutableArray.Create(fixableDiagnosticId) diff --git a/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs b/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs index add51361f19..21a70f89e2e 100644 --- a/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs +++ b/vsintegration/src/FSharp.Editor/CodeLens/FSharpCodeLensService.fs @@ -14,6 +14,7 @@ open System.Windows.Media.Animation open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Editor.Shared.Extensions open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Classification +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor.Shared.Extensions open FSharp.Compiler open FSharp.Compiler.SourceCodeServices @@ -91,7 +92,7 @@ type internal FSharpCodeLensService logInfof "Tagged text %A" taggedText #endif let textBlock = new TextBlock(Background = Brushes.AliceBlue, Opacity = 0.0, TextTrimming = TextTrimming.None) - DependencyObjectExtensions.SetDefaultTextProperties(textBlock, formatMap.Value) + FSharpDependencyObjectExtensions.SetDefaultTextProperties(textBlock, formatMap.Value) let prefix = Documents.Run settings.CodeLens.Prefix prefix.Foreground <- SolidColorBrush(Color.FromRgb(153uy, 153uy, 153uy)) @@ -116,7 +117,7 @@ type internal FSharpCodeLensService coloredProperties.SetForeground(Color.FromRgb(153uy, 153uy, 153uy)) let run = Documents.Run text.Text - DependencyObjectExtensions.SetTextProperties (run, actualProperties) + FSharpDependencyObjectExtensions.SetTextProperties (run, actualProperties) let inl = match text with @@ -126,7 +127,7 @@ type internal FSharpCodeLensService navigation.NavigateTo nav.Range) h :> Documents.Inline | _ -> run :> _ - DependencyObjectExtensions.SetTextProperties (inl, actualProperties) + FSharpDependencyObjectExtensions.SetTextProperties (inl, actualProperties) textBlock.Inlines.Add inl diff --git a/vsintegration/src/FSharp.Editor/Common/ContentType.fs b/vsintegration/src/FSharp.Editor/Common/ContentType.fs deleted file mode 100644 index 2e92332ccbe..00000000000 --- a/vsintegration/src/FSharp.Editor/Common/ContentType.fs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -namespace Microsoft.VisualStudio.FSharp.Editor - -open System.ComponentModel.Composition - -open Microsoft.CodeAnalysis.Editor -open Microsoft.VisualStudio.Utilities -open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor - -module FSharpStaticTypeDefinitions = - [] - [] - [] - let FSharpContentTypeDefinition = ContentTypeDefinition() - - [] - [] - [] - let FSharpSignatureHelpContentTypeDefinition = ContentTypeDefinition() diff --git a/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs index 4d66d133b78..ae8c5fe4fb6 100644 --- a/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs +++ b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs @@ -130,14 +130,6 @@ module internal RoslynHelpers = let StartAsyncUnitAsTask cancellationToken (computation:Async) = StartAsyncAsTask cancellationToken computation :> Task - let private TheSupportedDiagnostics = - // We are constructing our own descriptors at run-time. Compiler service is already doing error formatting and localization. - let dummyDescriptors = - [| for i in 0 .. 10000 -> DiagnosticDescriptor(sprintf "FS%04d" i, String.Empty, String.Empty, String.Empty, DiagnosticSeverity.Error, true, null, null) |] - ImmutableArray.Create(dummyDescriptors) - - let SupportedDiagnostics() = TheSupportedDiagnostics - let ConvertError(error: FSharpErrorInfo, location: Location) = // Normalize the error message into the same format that we will receive it from the compiler. // This ensures that IntelliSense and Compiler errors in the 'Error List' are de-duplicated. diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs index 989f210a19a..9ec89f069dd 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionProvider.fs @@ -12,6 +12,7 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Completion open Microsoft.CodeAnalysis.Options open Microsoft.CodeAnalysis.Text +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Completion open Microsoft.VisualStudio.Shell @@ -48,7 +49,7 @@ type internal FSharpCompletionProvider |> List.filter (fun (keyword, _) -> not (PrettyNaming.IsOperatorName keyword)) |> List.sortBy (fun (keyword, _) -> keyword) |> List.mapi (fun n (keyword, description) -> - CommonCompletionItem.Create(keyword, null, CompletionItemRules.Default, Nullable Glyph.Keyword, sortText = sprintf "%06d" (1000000 + n)) + FSharpCommonCompletionItem.Create(keyword, null, CompletionItemRules.Default, Nullable Glyph.Keyword, sortText = sprintf "%06d" (1000000 + n)) .AddProperty("description", description) .AddProperty(IsKeywordPropName, "")) @@ -73,7 +74,7 @@ type internal FSharpCompletionProvider // * let xs = [1..10] <<---- Don't commit autocomplete! (same for arrays) let noCommitChars = [|' '; '='; ','; '.'; '<'; '>'; '('; ')'; '!'; ':'; '['; ']'; '|'|].ToImmutableArray() - CompletionItemRules.Default.WithCommitCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, noCommitChars)) + CompletionItemRules.Default.WithCommitCharacterRules(ImmutableArray.Create (CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, noCommitChars))) static let getRules showAfterCharIsTyped = if showAfterCharIsTyped then noCommitOnSpaceRules else CompletionItemRules.Default @@ -152,7 +153,7 @@ type internal FSharpCompletionProvider | _, idents -> Array.last idents let completionItem = - CommonCompletionItem.Create(name, null, rules = getRules intellisenseOptions.ShowAfterCharIsTyped, glyph = Nullable (Microsoft.CodeAnalysis.ExternalAccess.FSharp.FSharpGlyphHelpersObsolete.Convert(glyph)), filterText = filterText) + FSharpCommonCompletionItem.Create(name, null, rules = getRules intellisenseOptions.ShowAfterCharIsTyped, glyph = Nullable glyph, filterText = filterText) .AddProperty(FullNamePropName, declarationItem.FullName) let completionItem = @@ -198,7 +199,7 @@ type internal FSharpCompletionProvider } override this.ShouldTriggerCompletion(sourceText: SourceText, caretPosition: int, trigger: CompletionTrigger, _: OptionSet) = - use _logBlock = Logger.LogBlockMessage this.Name LogEditorFunctionId.Completion_ShouldTrigger + use _logBlock = Logger.LogBlock LogEditorFunctionId.Completion_ShouldTrigger let getInfo() = let documentId = workspace.GetDocumentIdInCurrentContext(sourceText.Container) diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs index ef138b72c7d..6303e2498d2 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionService.fs @@ -9,6 +9,7 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Completion open Microsoft.CodeAnalysis.Host open Microsoft.CodeAnalysis.Host.Mef +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Completion open Microsoft.VisualStudio.Shell @@ -26,10 +27,11 @@ type internal FSharpCompletionService let builtInProviders = ImmutableArray.Create( FSharpCompletionProvider(workspace, serviceProvider, checkerProvider, projectInfoManager, assemblyContentProvider), - HashDirectiveCompletionProvider(workspace, projectInfoManager, - [ Completion.Create("""\s*#load\s+(@?"*(?"[^"]*"?))""", [".fs"; ".fsx"], useIncludeDirectives = true) - Completion.Create("""\s*#r\s+(@?"*(?"[^"]*"?))""", [".dll"; ".exe"], useIncludeDirectives = true) - Completion.Create("""\s*#I\s+(@?"*(?"[^"]*"?))""", ["\x00"], useIncludeDirectives = false) ])) + FSharpCommonCompletionProvider.Create( + HashDirectiveCompletionProvider(workspace, projectInfoManager, + [ Completion.Create("""\s*#load\s+(@?"*(?"[^"]*"?))""", [".fs"; ".fsx"], useIncludeDirectives = true) + Completion.Create("""\s*#r\s+(@?"*(?"[^"]*"?))""", [".dll"; ".exe"], useIncludeDirectives = true) + Completion.Create("""\s*#I\s+(@?"*(?"[^"]*"?))""", ["\x00"], useIncludeDirectives = false) ]))) override this.Language = FSharpConstants.FSharpLanguageName override this.GetBuiltInProviders() = builtInProviders diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs index 6691640c4fd..5d210d5f363 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs @@ -8,6 +8,7 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Classification open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.Completion +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Completion open System.Globalization open FSharp.Compiler.SourceCodeServices @@ -81,7 +82,7 @@ module internal CompletionUtils = | _ -> false let isStartingNewWord (sourceText, position) = - CommonCompletionUtilities.IsStartingNewWord(sourceText, position, (fun ch -> isIdentifierStartCharacter ch), (fun ch -> isIdentifierPartCharacter ch)) + FSharpCommonCompletionUtilities.IsStartingNewWord(sourceText, position, (fun ch -> isIdentifierStartCharacter ch), (fun ch -> isIdentifierPartCharacter ch)) let shouldProvideCompletion (documentId: DocumentId, filePath: string, defines: string list, sourceText: SourceText, triggerPosition: int) : bool = let textLines = sourceText.Lines diff --git a/vsintegration/src/FSharp.Editor/Completion/FileSystemCompletion.fs b/vsintegration/src/FSharp.Editor/Completion/FileSystemCompletion.fs index e92caa22d4d..d8028425009 100644 --- a/vsintegration/src/FSharp.Editor/Completion/FileSystemCompletion.fs +++ b/vsintegration/src/FSharp.Editor/Completion/FileSystemCompletion.fs @@ -12,6 +12,7 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Completion open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.Classification +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Completion type internal Completion = { DirectiveRegex: Regex @@ -23,7 +24,6 @@ type internal Completion = UseIncludeDirectives = useIncludeDirectives } type internal HashDirectiveCompletionProvider(workspace: Workspace, projectInfoManager: FSharpProjectOptionsManager, completions: Completion list) = - inherit CommonCompletionProvider() let [] NetworkPath = "\\\\" let commitRules = ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, '"', '\\', ',', '/')) @@ -82,75 +82,77 @@ type internal HashDirectiveCompletionProvider(workspace: Workspace, projectInfoM ) |> Seq.toList - override this.ProvideCompletionsAsync(context) = - asyncMaybe { - let document = context.Document - let position = context.Position - do! let extension = Path.GetExtension document.FilePath - Option.guard (extension = ".fsx" || extension = ".fsscript") - - let! ct = liftAsync Async.CancellationToken - let! text = document.GetTextAsync(ct) - do! Option.guard (isInStringLiteral(text, position)) - let line = text.Lines.GetLineFromPosition(position) - let lineText = text.ToString(TextSpan.FromBounds(line.Start, position)) + interface IFSharpCommonCompletionProvider with + + member this.ProvideCompletionsAsync(context) = + asyncMaybe { + let document = context.Document + let position = context.Position + do! let extension = Path.GetExtension document.FilePath + Option.guard (extension = ".fsx" || extension = ".fsscript") + + let! ct = liftAsync Async.CancellationToken + let! text = document.GetTextAsync(ct) + do! Option.guard (isInStringLiteral(text, position)) + let line = text.Lines.GetLineFromPosition(position) + let lineText = text.ToString(TextSpan.FromBounds(line.Start, position)) - let! completion, quotedPathGroup = - completions |> List.tryPick (fun completion -> - match completion.DirectiveRegex.Match lineText with - | m when m.Success -> - let quotedPathGroup = m.Groups.["literal"] - let endsWithQuote = PathCompletionUtilities.EndsWithQuote(quotedPathGroup.Value) - if endsWithQuote && (position >= line.Start + m.Length) then - None - else - Some (completion, quotedPathGroup) - | _ -> None) - - let snapshot = text.FindCorrespondingEditorTextSnapshot() + let! completion, quotedPathGroup = + completions |> List.tryPick (fun completion -> + match completion.DirectiveRegex.Match lineText with + | m when m.Success -> + let quotedPathGroup = m.Groups.["literal"] + let endsWithQuote = PathCompletionUtilities.EndsWithQuote(quotedPathGroup.Value) + if endsWithQuote && (position >= line.Start + m.Length) then + None + else + Some (completion, quotedPathGroup) + | _ -> None) + + let snapshot = text.FindCorrespondingEditorTextSnapshot() - do! Option.guard (not (isNull snapshot)) - - let extraSearchPaths = - if completion.UseIncludeDirectives then - getIncludeDirectives (text, position) - else [] - - let defaultSearchPath = Path.GetDirectoryName document.FilePath - let searchPaths = defaultSearchPath :: extraSearchPaths - - let helper = - FileSystemCompletionHelper( - Glyph.OpenFolder, - completion.AllowableExtensions |> List.tryPick getFileGlyph |> Option.defaultValue Glyph.None, - Seq.toImmutableArray searchPaths, - null, - completion.AllowableExtensions |> Seq.toImmutableArray, - rules) + do! Option.guard (not (isNull snapshot)) + + let extraSearchPaths = + if completion.UseIncludeDirectives then + getIncludeDirectives (text, position) + else [] + + let defaultSearchPath = Path.GetDirectoryName document.FilePath + let searchPaths = defaultSearchPath :: extraSearchPaths + + let helper = + FSharpFileSystemCompletionHelper( + Glyph.OpenFolder, + completion.AllowableExtensions |> List.tryPick getFileGlyph |> Option.defaultValue Glyph.None, + Seq.toImmutableArray searchPaths, + null, + completion.AllowableExtensions |> Seq.toImmutableArray, + rules) - let pathThroughLastSlash = getPathThroughLastSlash(text, position, quotedPathGroup) - let! items = helper.GetItemsAsync(pathThroughLastSlash, ct) |> Async.AwaitTask |> liftAsync - context.AddItems(items) - } - |> Async.Ignore - |> RoslynHelpers.StartAsyncUnitAsTask context.CancellationToken + let pathThroughLastSlash = getPathThroughLastSlash(text, position, quotedPathGroup) + let! items = helper.GetItemsAsync(pathThroughLastSlash, ct) |> Async.AwaitTask |> liftAsync + context.AddItems(items) + } + |> Async.Ignore + |> RoslynHelpers.StartAsyncUnitAsTask context.CancellationToken - override __.IsInsertionTrigger(text, position, _) = - // Bring up completion when the user types a quote (i.e.: #r "), or if they type a slash - // path separator character, or if they type a comma (#r "foo,version..."). - // Also, if they're starting a word. i.e. #r "c:\W - let ch = text.[position] - let isTriggerChar = - ch = '"' || ch = '\\' || ch = ',' || ch = '/' || - CommonCompletionUtilities.IsStartingNewWord(text, position, (fun x -> Char.IsLetter x), (fun x -> Char.IsLetterOrDigit x)) - isTriggerChar && isInStringLiteral(text, position) + member __.IsInsertionTrigger(text, position, _) = + // Bring up completion when the user types a quote (i.e.: #r "), or if they type a slash + // path separator character, or if they type a comma (#r "foo,version..."). + // Also, if they're starting a word. i.e. #r "c:\W + let ch = text.[position] + let isTriggerChar = + ch = '"' || ch = '\\' || ch = ',' || ch = '/' || + FSharpCommonCompletionUtilities.IsStartingNewWord(text, position, (fun x -> Char.IsLetter x), (fun x -> Char.IsLetterOrDigit x)) + isTriggerChar && isInStringLiteral(text, position) - override __.GetTextChangeAsync(selectedItem, ch, cancellationToken) = - // When we commit "\\" when the user types \ we have to adjust for the fact that the - // controller will automatically append \ after we commit. Because of that, we don't - // want to actually commit "\\" as we'll end up with "\\\". So instead we just commit - // "\" and know that controller will append "\" and give us "\\". - if selectedItem.DisplayText = NetworkPath && ch = Nullable '\\' then - Task.FromResult(Nullable(TextChange(selectedItem.Span, "\\"))) - else - base.GetTextChangeAsync(selectedItem, ch, cancellationToken) \ No newline at end of file + member __.GetTextChangeAsync(baseGetTextChangeAsync, selectedItem, ch, cancellationToken) = + // When we commit "\\" when the user types \ we have to adjust for the fact that the + // controller will automatically append \ after we commit. Because of that, we don't + // want to actually commit "\\" as we'll end up with "\\\". So instead we just commit + // "\" and know that controller will append "\" and give us "\\". + if selectedItem.DisplayText = NetworkPath && ch = Nullable '\\' then + Task.FromResult(Nullable(TextChange(selectedItem.Span, "\\"))) + else + baseGetTextChangeAsync.Invoke(selectedItem, ch, cancellationToken) \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs index 288feb97870..cde4f1d22dd 100644 --- a/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs +++ b/vsintegration/src/FSharp.Editor/Completion/SignatureHelp.fs @@ -9,6 +9,7 @@ open System.Collections.Generic open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.SignatureHelp open Microsoft.CodeAnalysis.Text +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.SignatureHelp open Microsoft.VisualStudio.Text open Microsoft.VisualStudio.Shell @@ -19,7 +20,7 @@ open FSharp.Compiler.Range open FSharp.Compiler.SourceCodeServices [] -[] +[)>] type internal FSharpSignatureHelpProvider [] ( @@ -188,7 +189,7 @@ type internal FSharpSignatureHelpProvider return Some items } - interface ISignatureHelpProvider with + interface IFSharpSignatureHelpProvider with member this.IsTriggerCharacter(c) = c ='(' || c = '<' || c = ',' member this.IsRetriggerCharacter(c) = c = ')' || c = '>' || c = '=' @@ -200,7 +201,7 @@ type internal FSharpSignatureHelpProvider let! textVersion = document.GetTextVersionAsync(cancellationToken) let triggerTypedChar = - if triggerInfo.TriggerCharacter.HasValue && triggerInfo.TriggerReason = SignatureHelpTriggerReason.TypeCharCommand then + if triggerInfo.TriggerCharacter.HasValue && triggerInfo.TriggerReason = FSharpSignatureHelpTriggerReason.TypeCharCommand then Some triggerInfo.TriggerCharacter.Value else None @@ -211,27 +212,13 @@ type internal FSharpSignatureHelpProvider |> Array.map (fun (hasParamArrayArg, doc, prefixParts, separatorParts, suffixParts, parameters, descriptionParts) -> let parameters = parameters |> Array.map (fun (paramName, isOptional, _typeText, paramDoc, displayParts) -> - SignatureHelpParameter(paramName,isOptional,documentationFactory=(fun _ -> paramDoc :> seq<_>),displayParts=displayParts)) - SignatureHelpItem(isVariadic=hasParamArrayArg, documentationFactory=(fun _ -> doc :> seq<_>),prefixParts=prefixParts,separatorParts=separatorParts,suffixParts=suffixParts,parameters=parameters,descriptionParts=descriptionParts)) + FSharpSignatureHelpParameter(paramName,isOptional,documentationFactory=(fun _ -> paramDoc :> seq<_>),displayParts=displayParts)) + FSharpSignatureHelpItem(isVariadic=hasParamArrayArg, documentationFactory=(fun _ -> doc :> seq<_>),prefixParts=prefixParts,separatorParts=separatorParts,suffixParts=suffixParts,parameters=parameters,descriptionParts=descriptionParts)) - return SignatureHelpItems(items,applicableSpan,argumentIndex,argumentCount,Option.toObj argumentName) + return FSharpSignatureHelpItems(items,applicableSpan,argumentIndex,argumentCount,Option.toObj argumentName) with ex -> Assert.Exception(ex) return! None } |> Async.map Option.toObj |> RoslynHelpers.StartAsyncAsTask cancellationToken - -open System.ComponentModel.Composition -open Microsoft.VisualStudio.Utilities -open Microsoft.VisualStudio.Text.Classification -open Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp.Presentation - -// Enable colorized signature help for F# buffers - -[)>] -[] -type internal FSharpSignatureHelpClassifierProvider [] (typeMap) = - interface IClassifierProvider with - override __.GetClassifier (buffer: ITextBuffer) = - buffer.Properties.GetOrCreateSingletonProperty(fun _ -> SignatureHelpClassifier(buffer, typeMap) :> _) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index 44b94805950..273e0bad4b6 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -3,6 +3,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System +open System.Composition open System.Collections.Immutable open System.Collections.Generic open System.Threading @@ -11,6 +12,7 @@ open System.Threading.Tasks open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Diagnostics open Microsoft.CodeAnalysis.Text +open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics open FSharp.Compiler @@ -22,9 +24,8 @@ type internal DiagnosticsType = | Syntax | Semantic -[] -type internal FSharpDocumentDiagnosticAnalyzer() = - inherit DocumentDiagnosticAnalyzer() +[)>] +type internal FSharpDocumentDiagnosticAnalyzer [] () = static let userOpName = "DocumentDiagnosticAnalyzer" let getChecker(document: Document) = @@ -106,40 +107,33 @@ type internal FSharpDocumentDiagnosticAnalyzer() = return results } - override __.Priority = 10 // Default = 50 + interface IFSharpDocumentDiagnosticAnalyzer with - override this.SupportedDiagnostics = RoslynHelpers.SupportedDiagnostics() - - override this.AnalyzeSyntaxAsync(document: Document, cancellationToken: CancellationToken): Task> = - let projectInfoManager = getProjectInfoManager document - asyncMaybe { - let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken) - let! sourceText = document.GetTextAsync(cancellationToken) - let! textVersion = document.GetTextVersionAsync(cancellationToken) - return! - FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Syntax) - |> liftAsync - } - |> Async.map (Option.defaultValue ImmutableArray.Empty) - |> RoslynHelpers.StartAsyncAsTask cancellationToken - - override this.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken): Task> = - let projectInfoManager = getProjectInfoManager document - asyncMaybe { - let! parsingOptions, _, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document, cancellationToken) - let! sourceText = document.GetTextAsync(cancellationToken) - let! textVersion = document.GetTextVersionAsync(cancellationToken) - if document.Project.Name <> FSharpConstants.FSharpMiscellaneousFilesName || isScriptFile document.FilePath then + member this.AnalyzeSyntaxAsync(document: Document, cancellationToken: CancellationToken): Task> = + let projectInfoManager = getProjectInfoManager document + asyncMaybe { + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken) + let! sourceText = document.GetTextAsync(cancellationToken) + let! textVersion = document.GetTextVersionAsync(cancellationToken) return! - FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Semantic) + FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Syntax) |> liftAsync - else - return ImmutableArray.Empty - } - |> Async.map (Option.defaultValue ImmutableArray.Empty) - |> RoslynHelpers.StartAsyncAsTask cancellationToken - - interface IBuiltInAnalyzer with - member __.GetAnalyzerCategory() : DiagnosticAnalyzerCategory = DiagnosticAnalyzerCategory.SemanticDocumentAnalysis - member __.OpenFileOnly _ = true - + } + |> Async.map (Option.defaultValue ImmutableArray.Empty) + |> RoslynHelpers.StartAsyncAsTask cancellationToken + + member this.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken): Task> = + let projectInfoManager = getProjectInfoManager document + asyncMaybe { + let! parsingOptions, _, projectOptions = projectInfoManager.TryGetOptionsForDocumentOrProject(document, cancellationToken) + let! sourceText = document.GetTextAsync(cancellationToken) + let! textVersion = document.GetTextVersionAsync(cancellationToken) + if document.Project.Name <> FSharpConstants.FSharpMiscellaneousFilesName || isScriptFile document.FilePath then + return! + FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(getChecker document, document.FilePath, sourceText, textVersion.GetHashCode(), parsingOptions, projectOptions, DiagnosticsType.Semantic) + |> liftAsync + else + return ImmutableArray.Empty + } + |> Async.map (Option.defaultValue ImmutableArray.Empty) + |> RoslynHelpers.StartAsyncAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/ProjectDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/ProjectDiagnosticAnalyzer.fs index 9b67e11819f..544039aa36a 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/ProjectDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/ProjectDiagnosticAnalyzer.fs @@ -14,20 +14,20 @@ open Microsoft.CodeAnalysis.Diagnostics open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.SolutionCrawler +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics open FSharp.Compiler open FSharp.Compiler.SourceCodeServices open FSharp.Compiler.Range -#if PROJECT_ANALYSIS // Project-wide error analysis. We don't enable this because ParseAndCheckProject checks projects against the versions of the files // saves to the file system. This is different to the versions of the files active in the editor. This results in out-of-sync error // messages while files are being edited -[] -type internal FSharpProjectDiagnosticAnalyzer() = - inherit ProjectDiagnosticAnalyzer() +[)>] +type internal FSharpProjectDiagnosticAnalyzer [] () = +#if PROJECT_ANALYSIS static member GetDiagnostics(options: FSharpProjectOptions) = async { let! checkProjectResults = FSharpLanguageService.Checker.ParseAndCheckProject(options) let results = @@ -42,13 +42,17 @@ type internal FSharpProjectDiagnosticAnalyzer() = |> Seq.toImmutableArray return results } - - override this.SupportedDiagnostics = CommonRoslynHelpers.SupportedDiagnostics() - - override this.AnalyzeProjectAsync(project: Project, cancellationToken: CancellationToken): Task> = - async { - match FSharpLanguageService.GetOptionsForProject(project.Id) with - | Some options -> return! FSharpProjectDiagnosticAnalyzer.GetDiagnostics(options) - | None -> return ImmutableArray.Empty - } |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken +#endif + + interface IFSharpProjectDiagnosticAnalyzer with + + member this.AnalyzeProjectAsync(_project: Project, _cancellationToken: CancellationToken): Task> = +#if PROJECT_ANALYSIS + async { + match FSharpLanguageService.GetOptionsForProject(project.Id) with + | Some options -> return! FSharpProjectDiagnosticAnalyzer.GetDiagnostics(options) + | None -> return ImmutableArray.Empty + } |> CommonRoslynHelpers.StartAsyncAsTask cancellationToken +#else + Task.FromResult(ImmutableArray.Empty) #endif diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs index 97235c0f1e0..f90717522a5 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/SimplifyNameDiagnosticAnalyzer.fs @@ -3,6 +3,7 @@ namespace rec Microsoft.VisualStudio.FSharp.Editor open System +open System.Composition open System.Collections.Immutable open System.Diagnostics open System.Threading @@ -13,15 +14,15 @@ open Microsoft.CodeAnalysis.Diagnostics open FSharp.Compiler open FSharp.Compiler.Range open System.Runtime.Caching +open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics type private TextVersionHash = int type private PerDocumentSavedData = { Hash: int; Diagnostics: ImmutableArray } -[] -type internal SimplifyNameDiagnosticAnalyzer() = - inherit DocumentDiagnosticAnalyzer() - +[)>] +type internal SimplifyNameDiagnosticAnalyzer [] () = + static let userOpName = "SimplifyNameDiagnosticAnalyzer" let getProjectInfoManager (document: Document) = document.Project.Solution.Workspace.Services.GetService().FSharpProjectOptionsManager let getChecker (document: Document) = document.Project.Solution.Workspace.Services.GetService().Checker @@ -30,105 +31,90 @@ type internal SimplifyNameDiagnosticAnalyzer() = // Make sure only one document is being analyzed at a time, to be nice static let guard = new SemaphoreSlim(1) - static let Descriptor = - DiagnosticDescriptor( - id = IDEDiagnosticIds.SimplifyNamesDiagnosticId, - title = SR.SimplifyName(), - messageFormat = SR.NameCanBeSimplified(), - category = DiagnosticCategory.Style, - defaultSeverity = DiagnosticSeverity.Hidden, - isEnabledByDefault = true, - customTags = FSharpDiagnosticCustomTags.Unnecessary) - static member LongIdentPropertyKey = "FullName" - override __.Priority = 100 // Default = 50 - override __.SupportedDiagnostics = ImmutableArray.Create Descriptor - override this.AnalyzeSyntaxAsync(_, _) = Task.FromResult ImmutableArray.Empty - override this.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken) = - asyncMaybe { - do! Option.guard document.FSharpOptions.CodeFixes.SimplifyName - do Trace.TraceInformation("{0:n3} (start) SimplifyName", DateTime.Now.TimeOfDay.TotalSeconds) - do! Async.Sleep DefaultTuning.SimplifyNameInitialDelay |> liftAsync - let! _parsingOptions, projectOptions = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document, cancellationToken) - let! textVersion = document.GetTextVersionAsync(cancellationToken) - let textVersionHash = textVersion.GetHashCode() - let! _ = guard.WaitAsync(cancellationToken) |> Async.AwaitTask |> liftAsync - try - let key = document.Id.ToString() - match cache.Get(key) with - | :? PerDocumentSavedData as data when data.Hash = textVersionHash -> return data.Diagnostics - | _ -> - let! sourceText = document.GetTextAsync() - let checker = getChecker document - let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName=userOpName) - let! symbolUses = checkResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync - let mutable result = ResizeArray() - let symbolUses = - symbolUses - |> Array.filter (fun symbolUse -> not symbolUse.IsFromOpenStatement) - |> Array.Parallel.map (fun symbolUse -> - let lineStr = sourceText.Lines.[Line.toZ symbolUse.RangeAlternate.StartLine].ToString() - // for `System.DateTime.Now` it returns ([|"System"; "DateTime"|], "Now") - let partialName = QuickParse.GetPartialLongNameEx(lineStr, symbolUse.RangeAlternate.EndColumn - 1) - // `symbolUse.RangeAlternate.Start` does not point to the start of plid, it points to start of `name`, - // so we have to calculate plid's start ourselves. - let plidStartCol = symbolUse.RangeAlternate.EndColumn - partialName.PartialIdent.Length - (getPlidLength partialName.QualifyingIdents) - symbolUse, partialName.QualifyingIdents, plidStartCol, partialName.PartialIdent) - |> Array.filter (fun (_, plid, _, name) -> name <> "" && not (List.isEmpty plid)) - |> Array.groupBy (fun (symbolUse, _, plidStartCol, _) -> symbolUse.RangeAlternate.StartLine, plidStartCol) - |> Array.map (fun (_, xs) -> xs |> Array.maxBy (fun (symbolUse, _, _, _) -> symbolUse.RangeAlternate.EndColumn)) + interface IFSharpSimplifyNameDiagnosticAnalyzer with + + member this.AnalyzeSemanticsAsync(descriptor, document: Document, cancellationToken: CancellationToken) = + asyncMaybe { + do! Option.guard document.FSharpOptions.CodeFixes.SimplifyName + do Trace.TraceInformation("{0:n3} (start) SimplifyName", DateTime.Now.TimeOfDay.TotalSeconds) + do! Async.Sleep DefaultTuning.SimplifyNameInitialDelay |> liftAsync + let! _parsingOptions, projectOptions = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document, cancellationToken) + let! textVersion = document.GetTextVersionAsync(cancellationToken) + let textVersionHash = textVersion.GetHashCode() + let! _ = guard.WaitAsync(cancellationToken) |> Async.AwaitTask |> liftAsync + try + let key = document.Id.ToString() + match cache.Get(key) with + | :? PerDocumentSavedData as data when data.Hash = textVersionHash -> return data.Diagnostics + | _ -> + let! sourceText = document.GetTextAsync() + let checker = getChecker document + let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName=userOpName) + let! symbolUses = checkResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync + let mutable result = ResizeArray() + let symbolUses = + symbolUses + |> Array.filter (fun symbolUse -> not symbolUse.IsFromOpenStatement) + |> Array.Parallel.map (fun symbolUse -> + let lineStr = sourceText.Lines.[Line.toZ symbolUse.RangeAlternate.StartLine].ToString() + // for `System.DateTime.Now` it returns ([|"System"; "DateTime"|], "Now") + let partialName = QuickParse.GetPartialLongNameEx(lineStr, symbolUse.RangeAlternate.EndColumn - 1) + // `symbolUse.RangeAlternate.Start` does not point to the start of plid, it points to start of `name`, + // so we have to calculate plid's start ourselves. + let plidStartCol = symbolUse.RangeAlternate.EndColumn - partialName.PartialIdent.Length - (getPlidLength partialName.QualifyingIdents) + symbolUse, partialName.QualifyingIdents, plidStartCol, partialName.PartialIdent) + |> Array.filter (fun (_, plid, _, name) -> name <> "" && not (List.isEmpty plid)) + |> Array.groupBy (fun (symbolUse, _, plidStartCol, _) -> symbolUse.RangeAlternate.StartLine, plidStartCol) + |> Array.map (fun (_, xs) -> xs |> Array.maxBy (fun (symbolUse, _, _, _) -> symbolUse.RangeAlternate.EndColumn)) - for symbolUse, plid, plidStartCol, name in symbolUses do - if not symbolUse.IsFromDefinition then - let posAtStartOfName = - let r = symbolUse.RangeAlternate - if r.StartLine = r.EndLine then Range.mkPos r.StartLine (r.EndColumn - name.Length) - else r.Start + for symbolUse, plid, plidStartCol, name in symbolUses do + if not symbolUse.IsFromDefinition then + let posAtStartOfName = + let r = symbolUse.RangeAlternate + if r.StartLine = r.EndLine then Range.mkPos r.StartLine (r.EndColumn - name.Length) + else r.Start - let getNecessaryPlid (plid: string list) : Async = - let rec loop (rest: string list) (current: string list) = - async { - match rest with - | [] -> return current - | headIdent :: restPlid -> - let! res = checkResults.IsRelativeNameResolvableFromSymbol(posAtStartOfName, current, symbolUse.Symbol, userOpName=userOpName) - if res then return current - else return! loop restPlid (headIdent :: current) - } - loop (List.rev plid) [] + let getNecessaryPlid (plid: string list) : Async = + let rec loop (rest: string list) (current: string list) = + async { + match rest with + | [] -> return current + | headIdent :: restPlid -> + let! res = checkResults.IsRelativeNameResolvableFromSymbol(posAtStartOfName, current, symbolUse.Symbol, userOpName=userOpName) + if res then return current + else return! loop restPlid (headIdent :: current) + } + loop (List.rev plid) [] - do! Async.Sleep DefaultTuning.SimplifyNameEachItemDelay |> liftAsync // be less intrusive, give other work priority most of the time - let! necessaryPlid = getNecessaryPlid plid |> liftAsync + do! Async.Sleep DefaultTuning.SimplifyNameEachItemDelay |> liftAsync // be less intrusive, give other work priority most of the time + let! necessaryPlid = getNecessaryPlid plid |> liftAsync - match necessaryPlid with - | necessaryPlid when necessaryPlid = plid -> () - | necessaryPlid -> - let r = symbolUse.RangeAlternate - let necessaryPlidStartCol = r.EndColumn - name.Length - (getPlidLength necessaryPlid) + match necessaryPlid with + | necessaryPlid when necessaryPlid = plid -> () + | necessaryPlid -> + let r = symbolUse.RangeAlternate + let necessaryPlidStartCol = r.EndColumn - name.Length - (getPlidLength necessaryPlid) - let unnecessaryRange = - Range.mkRange r.FileName (Range.mkPos r.StartLine plidStartCol) (Range.mkPos r.EndLine necessaryPlidStartCol) + let unnecessaryRange = + Range.mkRange r.FileName (Range.mkPos r.StartLine plidStartCol) (Range.mkPos r.EndLine necessaryPlidStartCol) - let relativeName = (String.concat "." plid) + "." + name - result.Add( - Diagnostic.Create( - Descriptor, - RoslynHelpers.RangeToLocation(unnecessaryRange, sourceText, document.FilePath), - properties = (dict [SimplifyNameDiagnosticAnalyzer.LongIdentPropertyKey, relativeName]).ToImmutableDictionary())) + let relativeName = (String.concat "." plid) + "." + name + result.Add( + Diagnostic.Create( + descriptor, + RoslynHelpers.RangeToLocation(unnecessaryRange, sourceText, document.FilePath), + properties = (dict [SimplifyNameDiagnosticAnalyzer.LongIdentPropertyKey, relativeName]).ToImmutableDictionary())) - let diagnostics = result.ToImmutableArray() - cache.Remove(key) |> ignore - let data = { Hash = textVersionHash; Diagnostics=diagnostics } - let cacheItem = CacheItem(key, data) - let policy = CacheItemPolicy(SlidingExpiration=DefaultTuning.PerDocumentSavedDataSlidingWindow) - cache.Set(cacheItem, policy) - return diagnostics - finally guard.Release() |> ignore - } - |> Async.map (Option.defaultValue ImmutableArray.Empty) - |> RoslynHelpers.StartAsyncAsTask cancellationToken - - interface IBuiltInAnalyzer with - member __.OpenFileOnly _ = true - member __.GetAnalyzerCategory() = DiagnosticAnalyzerCategory.SemanticDocumentAnalysis \ No newline at end of file + let diagnostics = result.ToImmutableArray() + cache.Remove(key) |> ignore + let data = { Hash = textVersionHash; Diagnostics=diagnostics } + let cacheItem = CacheItem(key, data) + let policy = CacheItemPolicy(SlidingExpiration=DefaultTuning.PerDocumentSavedDataSlidingWindow) + cache.Set(cacheItem, policy) + return diagnostics + finally guard.Release() |> ignore + } + |> Async.map (Option.defaultValue ImmutableArray.Empty) + |> RoslynHelpers.StartAsyncAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs index 58b1b139ff0..ff7c43839d0 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedDeclarationsAnalyzer.fs @@ -3,6 +3,7 @@ namespace rec Microsoft.VisualStudio.FSharp.Editor open System +open System.Composition open System.Collections.Generic open System.Collections.Immutable open System.Diagnostics @@ -10,27 +11,16 @@ open System.Threading.Tasks open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Diagnostics +open Microsoft.CodeAnalysis.Host.Mef open FSharp.Compiler.SourceCodeServices open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics -[] -type internal UnusedDeclarationsAnalyzer() = - inherit DocumentDiagnosticAnalyzer() +[)>] +type internal UnusedDeclarationsAnalyzer [] () = static let userOpName = "UnusedDeclarationsAnalyzer" let getProjectInfoManager (document: Document) = document.Project.Solution.Workspace.Services.GetService().FSharpProjectOptionsManager let getChecker (document: Document) = document.Project.Solution.Workspace.Services.GetService().Checker - let [] DescriptorId = "FS1182" - - let Descriptor = - DiagnosticDescriptor( - id = DescriptorId, - title = SR.TheValueIsUnused(), - messageFormat = SR.TheValueIsUnused(), - category = DiagnosticCategory.Style, - defaultSeverity = DiagnosticSeverity.Hidden, - isEnabledByDefault = true, - customTags = FSharpDiagnosticCustomTags.Unnecessary) let isPotentiallyUnusedDeclaration (symbol: FSharpSymbol) : bool = match symbol with @@ -95,33 +85,25 @@ type internal UnusedDeclarationsAnalyzer() = //#endif unusedRanges - override __.Priority = 80 // Default = 50 - - override __.SupportedDiagnostics = ImmutableArray.Create Descriptor - - override __.AnalyzeSyntaxAsync(_, _) = Task.FromResult ImmutableArray.Empty - - override __.AnalyzeSemanticsAsync(document, cancellationToken) = - asyncMaybe { - do! Option.guard document.FSharpOptions.CodeFixes.UnusedDeclarations + interface IFSharpUnusedDeclarationsDiagnosticAnalyzer with - do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) - do! Async.Sleep DefaultTuning.UnusedDeclarationsAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time - match! getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document, cancellationToken) with - | (_parsingOptions, projectOptions) -> - let! sourceText = document.GetTextAsync() - let checker = getChecker document - let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName = userOpName) - let! allSymbolUsesInFile = checkResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync - let unusedRanges = getUnusedDeclarationRanges allSymbolUsesInFile (isScriptFile document.FilePath) - return - unusedRanges - |> Seq.map (fun m -> Diagnostic.Create(Descriptor, RoslynHelpers.RangeToLocation(m, sourceText, document.FilePath))) - |> Seq.toImmutableArray - } - |> Async.map (Option.defaultValue ImmutableArray.Empty) - |> RoslynHelpers.StartAsyncAsTask cancellationToken + member __.AnalyzeSemanticsAsync(descriptor, document, cancellationToken) = + asyncMaybe { + do! Option.guard document.FSharpOptions.CodeFixes.UnusedDeclarations - interface IBuiltInAnalyzer with - member __.OpenFileOnly _ = true - member __.GetAnalyzerCategory() = DiagnosticAnalyzerCategory.SemanticDocumentAnalysis \ No newline at end of file + do Trace.TraceInformation("{0:n3} (start) UnusedDeclarationsAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) + do! Async.Sleep DefaultTuning.UnusedDeclarationsAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time + match! getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document, cancellationToken) with + | (_parsingOptions, projectOptions) -> + let! sourceText = document.GetTextAsync() + let checker = getChecker document + let! _, _, checkResults = checker.ParseAndCheckDocument(document, projectOptions, sourceText = sourceText, userOpName = userOpName) + let! allSymbolUsesInFile = checkResults.GetAllUsesOfAllSymbolsInFile() |> liftAsync + let unusedRanges = getUnusedDeclarationRanges allSymbolUsesInFile (isScriptFile document.FilePath) + return + unusedRanges + |> Seq.map (fun m -> Diagnostic.Create(descriptor, RoslynHelpers.RangeToLocation(m, sourceText, document.FilePath))) + |> Seq.toImmutableArray + } + |> Async.map (Option.defaultValue ImmutableArray.Empty) + |> RoslynHelpers.StartAsyncAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs index b94f0ca09a9..c79027b0dff 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/UnusedOpensDiagnosticAnalyzer.fs @@ -3,6 +3,7 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System +open System.Composition open System.Collections.Immutable open System.Diagnostics open System.Threading @@ -16,29 +17,16 @@ open FSharp.Compiler.Ast open FSharp.Compiler.Range open FSharp.Compiler.SourceCodeServices open Microsoft.VisualStudio.FSharp.Editor.Symbols +open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics -[] -type internal UnusedOpensDiagnosticAnalyzer() = - inherit DocumentDiagnosticAnalyzer() - +[)>] +type internal UnusedOpensDiagnosticAnalyzer [] () = + let getProjectInfoManager (document: Document) = document.Project.Solution.Workspace.Services.GetService().FSharpProjectOptionsManager let getChecker (document: Document) = document.Project.Solution.Workspace.Services.GetService().Checker static let userOpName = "UnusedOpensAnalyzer" - static let Descriptor = - DiagnosticDescriptor( - id = IDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId, - title = SR.RemoveUnusedOpens(), - messageFormat = SR.UnusedOpens(), - category = DiagnosticCategory.Style, - defaultSeverity = DiagnosticSeverity.Hidden, - isEnabledByDefault = true, - customTags = FSharpDiagnosticCustomTags.Unnecessary) - - override __.Priority = 90 // Default = 50 - override __.SupportedDiagnostics = ImmutableArray.Create Descriptor - override this.AnalyzeSyntaxAsync(_, _) = Task.FromResult ImmutableArray.Empty static member GetUnusedOpenRanges(document: Document, options, checker: FSharpChecker) : Async> = asyncMaybe { @@ -55,26 +43,24 @@ type internal UnusedOpensDiagnosticAnalyzer() = return unusedOpens } - override this.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken) = - asyncMaybe { - do Trace.TraceInformation("{0:n3} (start) UnusedOpensAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) - do! Async.Sleep DefaultTuning.UnusedOpensAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time - let! _parsingOptions, projectOptions = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document, cancellationToken) - let! sourceText = document.GetTextAsync() - let checker = getChecker document - let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document, projectOptions, checker) - - return - unusedOpens - |> List.map (fun range -> - Diagnostic.Create( - Descriptor, - RoslynHelpers.RangeToLocation(range, sourceText, document.FilePath))) - |> Seq.toImmutableArray - } - |> Async.map (Option.defaultValue ImmutableArray.Empty) - |> RoslynHelpers.StartAsyncAsTask cancellationToken + interface IFSharpUnusedOpensDiagnosticAnalyzer with - interface IBuiltInAnalyzer with - member __.OpenFileOnly _ = true - member __.GetAnalyzerCategory() = DiagnosticAnalyzerCategory.SemanticDocumentAnalysis \ No newline at end of file + member this.AnalyzeSemanticsAsync(descriptor, document: Document, cancellationToken: CancellationToken) = + asyncMaybe { + do Trace.TraceInformation("{0:n3} (start) UnusedOpensAnalyzer", DateTime.Now.TimeOfDay.TotalSeconds) + do! Async.Sleep DefaultTuning.UnusedOpensAnalyzerInitialDelay |> liftAsync // be less intrusive, give other work priority most of the time + let! _parsingOptions, projectOptions = getProjectInfoManager(document).TryGetOptionsForEditingDocumentOrProject(document, cancellationToken) + let! sourceText = document.GetTextAsync() + let checker = getChecker document + let! unusedOpens = UnusedOpensDiagnosticAnalyzer.GetUnusedOpenRanges(document, projectOptions, checker) + + return + unusedOpens + |> List.map (fun range -> + Diagnostic.Create( + descriptor, + RoslynHelpers.RangeToLocation(range, sourceText, document.FilePath))) + |> Seq.toImmutableArray + } + |> Async.map (Option.defaultValue ImmutableArray.Empty) + |> RoslynHelpers.StartAsyncAsTask cancellationToken diff --git a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs index 923b5372c67..10ee6533790 100644 --- a/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs +++ b/vsintegration/src/FSharp.Editor/DocumentHighlights/DocumentHighlightsService.fs @@ -11,6 +11,7 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.DocumentHighlighting open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.Text +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.DocumentHighlighting open FSharp.Compiler.SourceCodeServices open FSharp.Compiler.Range @@ -20,8 +21,7 @@ type internal FSharpHighlightSpan = TextSpan: TextSpan } override this.ToString() = sprintf "%+A" this -[] -[, FSharpConstants.FSharpLanguageName)>] +[)>] type internal FSharpDocumentHighlightsService [] (checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager) = static let userOpName = "DocumentHighlights" @@ -72,8 +72,8 @@ type internal FSharpDocumentHighlightsService [] (checkerP |> fixInvalidSymbolSpans sourceText symbol.Ident.idText } - interface IDocumentHighlightsService with - member __.GetDocumentHighlightsAsync(document, position, _documentsToSearch, cancellationToken) : Task> = + interface IFSharpDocumentHighlightsService with + member __.GetDocumentHighlightsAsync(document, position, _documentsToSearch, cancellationToken) : Task> = asyncMaybe { let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken) let! sourceText = document.GetTextAsync(cancellationToken) @@ -85,11 +85,11 @@ type internal FSharpDocumentHighlightsService [] (checkerP let highlightSpans = spans |> Array.map (fun span -> - let kind = if span.IsDefinition then HighlightSpanKind.Definition else HighlightSpanKind.Reference - HighlightSpan(span.TextSpan, kind)) + let kind = if span.IsDefinition then FSharpHighlightSpanKind.Definition else FSharpHighlightSpanKind.Reference + FSharpHighlightSpan(span.TextSpan, kind)) |> Seq.toImmutableArray - return ImmutableArray.Create(DocumentHighlights(document, highlightSpans)) + return ImmutableArray.Create(FSharpDocumentHighlights(document, highlightSpans)) } - |> Async.map (Option.defaultValue ImmutableArray.Empty) + |> Async.map (Option.defaultValue ImmutableArray.Empty) |> RoslynHelpers.StartAsyncAsTask(cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 8b99167f24e..77add187654 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -34,7 +34,6 @@ - Common\LspExternalAccess.fs @@ -94,7 +93,6 @@ - @@ -173,6 +171,7 @@ + diff --git a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs index 82cccffbf1a..8791c558896 100644 --- a/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs +++ b/vsintegration/src/FSharp.Editor/InlineRename/InlineRenameService.fs @@ -12,6 +12,8 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Editor open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.Text +open Microsoft.CodeAnalysis.ExternalAccess.FSharp +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor open FSharp.Compiler open FSharp.Compiler.Range @@ -19,9 +21,9 @@ open FSharp.Compiler.SourceCodeServices open Symbols type internal FailureInlineRenameInfo private () = - interface IInlineRenameInfo with + interface IFSharpInlineRenameInfo with member __.CanRename = false - member __.LocalizedErrorMessage = EditorFeaturesResources.You_cannot_rename_this_element + member __.LocalizedErrorMessage = FSharpEditorFeaturesResources.You_cannot_rename_this_element member __.TriggerSpan = Unchecked.defaultof<_> member __.HasOverloads = false member __.ForceRenameOverloads = true @@ -31,17 +33,17 @@ type internal FailureInlineRenameInfo private () = member __.GetFinalSymbolName _ = "" member __.GetReferenceEditSpan(_, _) = Unchecked.defaultof<_> member __.GetConflictEditSpan(_, _, _) = Nullable() - member __.FindRenameLocationsAsync(_, _) = Task.FromResult null + member __.FindRenameLocationsAsync(_, _) = Task.FromResult null member __.TryOnBeforeGlobalSymbolRenamed(_, _, _) = false member __.TryOnAfterGlobalSymbolRenamed(_, _, _) = false - static member Instance = FailureInlineRenameInfo() :> IInlineRenameInfo + static member Instance = FailureInlineRenameInfo() :> IFSharpInlineRenameInfo -type internal InlineRenameLocationSet(locations: InlineRenameLocation [], originalSolution: Solution, symbolKind: LexerSymbolKind, symbol: FSharpSymbol) = - interface IInlineRenameLocationSet with +type internal InlineRenameLocationSet(locations: FSharpInlineRenameLocation [], originalSolution: Solution, symbolKind: LexerSymbolKind, symbol: FSharpSymbol) = + interface IFSharpInlineRenameLocationSet with member __.Locations = upcast locations.ToList() - member __.GetReplacementsAsync(replacementText, _optionSet, cancellationToken) : Task = - let rec applyChanges (solution: Solution) (locationsByDocument: (Document * InlineRenameLocation list) list) = + member __.GetReplacementsAsync(replacementText, _optionSet, cancellationToken) : Task = + let rec applyChanges (solution: Solution) (locationsByDocument: (Document * FSharpInlineRenameLocation list) list) = async { match locationsByDocument with | [] -> return solution @@ -59,7 +61,7 @@ type internal InlineRenameLocationSet(locations: InlineRenameLocation [], origin | LexerSymbolKind.StaticallyResolvedTypeParameter -> replacementText | _ -> Keywords.NormalizeIdentifierBackticks replacementText return - { new IInlineRenameReplacementInfo with + { new IFSharpInlineRenameReplacementInfo with member __.NewSolution = newSolution member __.ReplacementTextValid = Tokenizer.isValidNameForSymbol(symbolKind, symbol, replacementText) member __.DocumentIds = locations |> Seq.map (fun doc -> doc.Document.Id) |> Seq.distinct @@ -90,7 +92,7 @@ type internal InlineRenameInfo SymbolHelpers.getSymbolUsesInSolution(symbolUse.Symbol, declLoc, checkFileResults, projectInfoManager, checker, document.Project.Solution, userOpName) |> Async.cache - interface IInlineRenameInfo with + interface IFSharpInlineRenameInfo with member __.CanRename = true member __.LocalizedErrorMessage = null member __.TriggerSpan = triggerSpan @@ -126,19 +128,19 @@ type internal InlineRenameInfo match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) with | Some span -> let textSpan = Tokenizer.fixupSpan(sourceText, span) - yield InlineRenameLocation(document, textSpan) + yield FSharpInlineRenameLocation(document, textSpan) | None -> () |] }) |> Async.Parallel |> Async.map Array.concat - return InlineRenameLocationSet(locations, document.Project.Solution, lexerSymbol.Kind, symbolUse.Symbol) :> IInlineRenameLocationSet + return InlineRenameLocationSet(locations, document.Project.Solution, lexerSymbol.Kind, symbolUse.Symbol) :> IFSharpInlineRenameLocationSet } |> RoslynHelpers.StartAsyncAsTask(cancellationToken) member __.TryOnBeforeGlobalSymbolRenamed(_workspace, _changedDocumentIDs, _replacementText) = true member __.TryOnAfterGlobalSymbolRenamed(_workspace, _changedDocumentIDs, _replacementText) = true -[, FSharpConstants.FSharpLanguageName); Shared>] +[); Shared>] type internal InlineRenameService [] ( @@ -148,7 +150,7 @@ type internal InlineRenameService static let userOpName = "InlineRename" static member GetInlineRenameInfo(checker: FSharpChecker, projectInfoManager: FSharpProjectOptionsManager, document: Document, sourceText: SourceText, position: int, - defines: string list, options: FSharpProjectOptions) : Async = + defines: string list, options: FSharpProjectOptions) : Async = asyncMaybe { let textLine = sourceText.Lines.GetLineFromPosition(position) let textLinePos = sourceText.Lines.GetLinePosition(position) @@ -161,11 +163,11 @@ type internal InlineRenameService let! span = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.RangeAlternate) let triggerSpan = Tokenizer.fixupSpan(sourceText, span) - return InlineRenameInfo(checker, projectInfoManager, document, triggerSpan, symbol, symbolUse, declLoc, checkFileResults) :> IInlineRenameInfo + return InlineRenameInfo(checker, projectInfoManager, document, triggerSpan, symbol, symbolUse, declLoc, checkFileResults) :> IFSharpInlineRenameInfo } - interface IEditorInlineRenameService with - member __.GetRenameInfoAsync(document: Document, position: int, cancellationToken: CancellationToken) : Task = + interface IFSharpEditorInlineRenameService with + member __.GetRenameInfoAsync(document: Document, position: int, cancellationToken: CancellationToken) : Task = asyncMaybe { let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken) let! sourceText = document.GetTextAsync(cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index a3b7b6f7586..f02f4e344b4 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -221,7 +221,7 @@ type internal FSharpLanguageService(package : FSharpPackage) = base.Initialize() this.Workspace.Options <- this.Workspace.Options.WithChangedOption(Completion.FSharpCompletionOptions.BlockForCompletionItems, FSharpConstants.FSharpLanguageName, false) - this.Workspace.Options <- this.Workspace.Options.WithChangedOption(Shared.Options.ServiceFeatureOnOffOptions.ClosedFileDiagnostic, FSharpConstants.FSharpLanguageName, Nullable false) + this.Workspace.Options <- this.Workspace.Options.WithChangedOption(Shared.Options.FSharpServiceFeatureOnOffOptions.ClosedFileDiagnostic, FSharpConstants.FSharpLanguageName, Nullable false) let theme = package.ComponentModel.DefaultExportProvider.GetExport().Value theme.SetColors() diff --git a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs index 2286cd06445..f33d23b3291 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/FindUsagesService.fs @@ -59,7 +59,7 @@ type internal FSharpFindUsagesService let! symbol = Tokenizer.getSymbolAtPosition(document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false) let! symbolUse = checkFileResults.GetSymbolUseAtLocation(lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, userOpName=userOpName) let! declaration = checkFileResults.GetDeclarationLocation (lineNumber, symbol.Ident.idRange.EndColumn, textLine, symbol.FullIsland, false, userOpName=userOpName) |> liftAsync - let tags = GlyphTags.GetTags(Microsoft.CodeAnalysis.ExternalAccess.FSharp.FSharpGlyphHelpersObsolete.Convert(Tokenizer.GetGlyphForSymbol (symbolUse.Symbol, symbol.Kind))) + let tags = FSharpGlyphTags.GetTags(Tokenizer.GetGlyphForSymbol (symbolUse.Symbol, symbol.Kind)) let declarationRange = match declaration with diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs index 5ba172edc4b..a607442e462 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinition.fs @@ -14,6 +14,7 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.FindSymbols open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.Navigation +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Navigation open Microsoft.VisualStudio.Shell.Interop @@ -105,16 +106,6 @@ module private ExternalSymbol = | _ -> [] -type internal FSharpNavigableItem(document: Document, textSpan: TextSpan) = - interface INavigableItem with - member __.Glyph = Glyph.BasicFile - member __.DisplayFileLocation = true - member __.IsImplicitlyDeclared = false - member __.Document = document - member __.SourceSpan = textSpan - member __.DisplayTaggedParts = ImmutableArray.Empty - member __.ChildItems = ImmutableArray.Empty - // TODO: Uncomment code when VS has a fix for updating the status bar. type internal StatusBar(statusBar: IVsStatusbar) = let mutable _searchIcon = int16 Microsoft.VisualStudio.Shell.Interop.Constants.SBAI_Find :> obj @@ -151,6 +142,9 @@ type internal StatusBar(statusBar: IVsStatusbar) = { new IDisposable with member __.Dispose() = () } //statusBar.Animation(0, &searchIcon) |> ignore } +type internal FSharpGoToDefinitionNavigableItem(document, sourceSpan) = + inherit FSharpNavigableItem(Glyph.BasicFile, ImmutableArray.Empty, document, sourceSpan) + type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpProjectOptionsManager) = let userOpName = "GoToDefinition" @@ -167,7 +161,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP let! refSourceText = refDocument.GetTextAsync(cancellationToken) |> Async.AwaitTask match RoslynHelpers.TryFSharpRangeToTextSpan (refSourceText, range) with | None -> return None - | Some refTextSpan -> return Some (FSharpNavigableItem (refDocument, refTextSpan)) + | Some refTextSpan -> return Some (FSharpGoToDefinitionNavigableItem (refDocument, refTextSpan)) else return None } @@ -201,7 +195,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP let! symbolUses = checkFileResults.GetUsesOfSymbolInFile symbol |> liftAsync let! implSymbol = symbolUses |> Array.tryHead let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, implSymbol.RangeAlternate) - return FSharpNavigableItem (implDoc, implTextSpan) + return FSharpGoToDefinitionNavigableItem (implDoc, implTextSpan) else let! targetDocument = originDocument.Project.Solution.TryGetDocumentFromFSharpRange fsSymbolUse.RangeAlternate return! rangeToNavigableItem (fsSymbolUse.RangeAlternate, targetDocument) @@ -262,7 +256,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP ) let! location = symbol.Locations |> Seq.tryHead - return (FSharpNavigableItem(project.GetDocument(location.SourceTree), location.SourceSpan), idRange) + return (FSharpGoToDefinitionNavigableItem(project.GetDocument(location.SourceTree), location.SourceSpan), idRange) | FSharpFindDeclResult.DeclFound targetRange -> // if goto definition is called at we are alread at the declaration location of a symbol in @@ -279,7 +273,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP let! targetRange = this.FindSymbolDeclarationInFile(targetSymbolUse, implFilePath, implSourceText, projectOptions, implVersion.GetHashCode()) let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, targetRange) - let navItem = FSharpNavigableItem (implDocument, implTextSpan) + let navItem = FSharpGoToDefinitionNavigableItem (implDocument, implTextSpan) return (navItem, idRange) else // jump from implementation to the corresponding signature let! declarations = checkFileResults.GetDeclarationLocation (fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland, true, userOpName=userOpName) |> liftAsync @@ -288,7 +282,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP let! sigDocument = originDocument.Project.Solution.TryGetDocumentFromPath targetRange.FileName let! sigSourceText = sigDocument.GetTextAsync () |> liftTaskAsync let! sigTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (sigSourceText, targetRange) - let navItem = FSharpNavigableItem (sigDocument, sigTextSpan) + let navItem = FSharpGoToDefinitionNavigableItem (sigDocument, sigTextSpan) return (navItem, idRange) | _ -> return! None @@ -301,7 +295,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP let! sigTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (sigSourceText, targetRange) // if the gotodef call originated from a signature and the returned target is a signature, navigate there if isSignatureFile targetRange.FileName && preferSignature then - let navItem = FSharpNavigableItem (sigDocument, sigTextSpan) + let navItem = FSharpGoToDefinitionNavigableItem (sigDocument, sigTextSpan) return (navItem, idRange) else // we need to get an FSharpSymbol from the targetRange found in the signature // that symbol will be used to find the destination in the corresponding implementation file @@ -318,7 +312,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP let! targetRange = this.FindSymbolDeclarationInFile(targetSymbolUse, implFilePath, implSourceText, projectOptions, implVersion.GetHashCode()) let! implTextSpan = RoslynHelpers.TryFSharpRangeToTextSpan (implSourceText, targetRange) - let navItem = FSharpNavigableItem (implDocument, implTextSpan) + let navItem = FSharpGoToDefinitionNavigableItem (implDocument, implTextSpan) return (navItem, idRange) | _ -> return! None @@ -335,7 +329,7 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP member this.FindDefinitionsForPeekTask(originDocument: Document, position: int, cancellationToken: CancellationToken) = this.FindDefinitionAtPosition(originDocument, position) |> Async.map ( - Option.map (fun (navItem, _) -> (navItem :> INavigableItem)) + Option.map (fun (navItem, _) -> navItem :> FSharpNavigableItem) >> Option.toArray >> Array.toSeq) |> RoslynHelpers.StartAsyncAsTask cancellationToken @@ -344,31 +338,30 @@ type internal GoToDefinition(checker: FSharpChecker, projectInfoManager: FSharpP /// at the provided position in the document. member this.FindDefinitionTask(originDocument: Document, position: int, cancellationToken: CancellationToken) = this.FindDefinitionAtPosition(originDocument, position) - |> Async.map (Option.map (fun (navItem, range) -> (navItem :> INavigableItem, range))) |> RoslynHelpers.StartAsyncAsTask cancellationToken /// Navigate to the positon of the textSpan in the provided document /// used by quickinfo link navigation when the tooltip contains the correct destination range. member __.TryNavigateToTextSpan(document: Document, textSpan: TextSpan, statusBar: StatusBar) = - let navigableItem = FSharpNavigableItem(document, textSpan) :> INavigableItem + let navigableItem = FSharpGoToDefinitionNavigableItem(document, textSpan) let workspace = document.Project.Solution.Workspace - let navigationService = workspace.Services.GetService() - let options = workspace.Options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true) + let navigationService = workspace.Services.GetService() + let options = workspace.Options.WithChangedOption(FSharpNavigationOptions.PreferProvisionalTab, true) let navigationSucceeded = navigationService.TryNavigateToSpan(workspace, navigableItem.Document.Id, navigableItem.SourceSpan, options) if not navigationSucceeded then statusBar.TempMessage (SR.CannotNavigateUnknown()) - member __.NavigateToItem(navigableItem: #INavigableItem, statusBar: StatusBar) = + member __.NavigateToItem(navigableItem: FSharpNavigableItem, statusBar: StatusBar) = use __ = statusBar.Animate() statusBar.Message (SR.NavigatingTo()) let workspace = navigableItem.Document.Project.Solution.Workspace - let navigationService = workspace.Services.GetService() + let navigationService = workspace.Services.GetService() // Prefer open documents in the preview tab. - let options = workspace.Options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true) + let options = workspace.Options.WithChangedOption(FSharpNavigationOptions.PreferProvisionalTab, true) let result = navigationService.TryNavigateToSpan(workspace, navigableItem.Document.Id, navigableItem.SourceSpan, options) if result then diff --git a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs index d2bcd9dd4f5..1a5b1e611ec 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/GoToDefinitionService.fs @@ -9,12 +9,13 @@ open System.Threading.Tasks open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Editor open Microsoft.CodeAnalysis.Host.Mef +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor open Microsoft.VisualStudio.Shell open Microsoft.VisualStudio.Shell.Interop open System -[, FSharpConstants.FSharpLanguageName)>] +[)>] [)>] type internal FSharpGoToDefinitionService [] @@ -26,7 +27,7 @@ type internal FSharpGoToDefinitionService let gtd = GoToDefinition(checkerProvider.Checker, projectInfoManager) let statusBar = StatusBar(ServiceProvider.GlobalProvider.GetService()) - interface IGoToDefinitionService with + interface IFSharpGoToDefinitionService with /// Invoked with Peek Definition. member __.FindDefinitionsAsync (document: Document, position: int, cancellationToken: CancellationToken) = gtd.FindDefinitionsForPeekTask(document, position, cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs index e433733f4d0..a1022788ab6 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigableSymbolsService.fs @@ -9,6 +9,7 @@ open System.ComponentModel.Composition open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.Navigation +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Navigation open Microsoft.VisualStudio.Language.Intellisense open Microsoft.VisualStudio.Text @@ -18,7 +19,7 @@ open Microsoft.VisualStudio.Utilities open Microsoft.VisualStudio.Shell [] -type internal FSharpNavigableSymbol(item: INavigableItem, span: SnapshotSpan, gtd: GoToDefinition, statusBar: StatusBar) = +type internal FSharpNavigableSymbol(item: FSharpNavigableItem, span: SnapshotSpan, gtd: GoToDefinition, statusBar: StatusBar) = interface INavigableSymbol with member __.Navigate(_: INavigableRelationship) = gtd.NavigateToItem(item, statusBar) diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs index 25f33dbb75c..428ca48ef62 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigateToSearchService.fs @@ -19,39 +19,21 @@ open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.NavigateTo open Microsoft.CodeAnalysis.Navigation open Microsoft.CodeAnalysis.PatternMatching +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Navigation +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.NavigateTo open FSharp.Compiler open FSharp.Compiler.SourceCodeServices type internal NavigableItem(document: Document, sourceSpan: TextSpan, glyph: Glyph, name: string, kind: string, additionalInfo: string) = - interface INavigableItem with - member __.Glyph = glyph - /// The tagged parts to display for this item. If default, the line of text from is used. - member __.DisplayTaggedParts = ImmutableArray.Create (TaggedText(TextTags.Text, name)) - /// Return true to display the file path of and the span of when displaying this item. - member __.DisplayFileLocation = true - /// This is intended for symbols that are ordinary symbols in the language sense, and may be used by code, but that are simply declared - /// implicitly rather than with explicit language syntax. For example, a default synthesized constructor in C# when the class contains no - /// explicit constructors. - member __.IsImplicitlyDeclared = false - member __.Document = document - member __.SourceSpan = sourceSpan - member __.ChildItems = ImmutableArray.Empty + inherit FSharpNavigableItem(glyph, ImmutableArray.Create (TaggedText(TextTags.Text, name)), document, sourceSpan) + member __.Name = name member __.Kind = kind member __.AdditionalInfo = additionalInfo -type internal NavigateToSearchResult(item: NavigableItem, matchKind: NavigateToMatchKind) = - interface INavigateToSearchResult with - member __.AdditionalInformation = item.AdditionalInfo - member __.Kind = item.Kind - member __.MatchKind = matchKind - member __.IsCaseSensitive = false - member __.Name = item.Name - member __.NameMatchSpans = ImmutableArray<_>.Empty - member __.SecondarySort = null - member __.Summary = null - member __.NavigableItem = upcast item +type internal NavigateToSearchResult(item: NavigableItem, matchKind: FSharpNavigateToMatchKind) = + inherit FSharpNavigateToSearchResult(item.AdditionalInfo, item.Kind, matchKind, item.Name, item) module private Index = [] @@ -73,18 +55,18 @@ module private Index = if res = 0 then a.Offset.CompareTo(b.Offset) else res } type IIndexedNavigableItems = - abstract Find: searchValue: string -> INavigateToSearchResult [] + abstract Find: searchValue: string -> FSharpNavigateToSearchResult [] abstract AllItems: NavigableItem [] let private navigateToSearchResultComparer = - { new IEqualityComparer with - member __.Equals(x: INavigateToSearchResult, y: INavigateToSearchResult) = + { new IEqualityComparer with + member __.Equals(x: FSharpNavigateToSearchResult, y: FSharpNavigateToSearchResult) = match x, y with | null, _ | _, null -> false | _ -> x.NavigableItem.Document.Id = y.NavigableItem.Document.Id && x.NavigableItem.SourceSpan = y.NavigableItem.SourceSpan - member __.GetHashCode(x: INavigateToSearchResult) = + member __.GetHashCode(x: FSharpNavigateToSearchResult) = if isNull x then 0 else 23 * (17 * 23 + x.NavigableItem.Document.Id.GetHashCode()) + x.NavigableItem.SourceSpan.GetHashCode() } @@ -115,11 +97,11 @@ module private Index = let entry = entries.[index] let matchKind = if entry.Offset = 0 then - if entry.Length = searchValue.Length then NavigateToMatchKind.Exact - else NavigateToMatchKind.Prefix - else NavigateToMatchKind.Substring + if entry.Length = searchValue.Length then FSharpNavigateToMatchKind.Exact + else FSharpNavigateToMatchKind.Prefix + else FSharpNavigateToMatchKind.Substring let item = entry.Item - result.Add (NavigateToSearchResult(item, matchKind) :> INavigateToSearchResult) |> ignore + result.Add (NavigateToSearchResult(item, matchKind) :> FSharpNavigateToSearchResult) |> ignore // in case if there are multiple matching items binary search might return not the first one. // in this case we'll walk backwards searching for the applicable answers @@ -140,17 +122,17 @@ module private Index = module private Utils = let navigateToItemKindToRoslynKind = function - | NavigateTo.NavigableItemKind.Module -> NavigateToItemKind.Module - | NavigateTo.NavigableItemKind.ModuleAbbreviation -> NavigateToItemKind.Module - | NavigateTo.NavigableItemKind.Exception -> NavigateToItemKind.Class - | NavigateTo.NavigableItemKind.Type -> NavigateToItemKind.Class - | NavigateTo.NavigableItemKind.ModuleValue -> NavigateToItemKind.Field - | NavigateTo.NavigableItemKind.Field -> NavigateToItemKind.Field - | NavigateTo.NavigableItemKind.Property -> NavigateToItemKind.Property - | NavigateTo.NavigableItemKind.Constructor -> NavigateToItemKind.Method - | NavigateTo.NavigableItemKind.Member -> NavigateToItemKind.Method - | NavigateTo.NavigableItemKind.EnumCase -> NavigateToItemKind.EnumItem - | NavigateTo.NavigableItemKind.UnionCase -> NavigateToItemKind.EnumItem + | NavigateTo.NavigableItemKind.Module -> FSharpNavigateToItemKind.Module + | NavigateTo.NavigableItemKind.ModuleAbbreviation -> FSharpNavigateToItemKind.Module + | NavigateTo.NavigableItemKind.Exception -> FSharpNavigateToItemKind.Class + | NavigateTo.NavigableItemKind.Type -> FSharpNavigateToItemKind.Class + | NavigateTo.NavigableItemKind.ModuleValue -> FSharpNavigateToItemKind.Field + | NavigateTo.NavigableItemKind.Field -> FSharpNavigateToItemKind.Field + | NavigateTo.NavigableItemKind.Property -> FSharpNavigateToItemKind.Property + | NavigateTo.NavigableItemKind.Constructor -> FSharpNavigateToItemKind.Method + | NavigateTo.NavigableItemKind.Member -> FSharpNavigateToItemKind.Method + | NavigateTo.NavigableItemKind.EnumCase -> FSharpNavigateToItemKind.EnumItem + | NavigateTo.NavigableItemKind.UnionCase -> FSharpNavigateToItemKind.EnumItem let navigateToItemKindToGlyph = function | NavigateTo.NavigableItemKind.Module -> Glyph.ModulePublic @@ -182,7 +164,7 @@ module private Utils = type PerDocumentSavedData = { Hash: int; Items: Index.IIndexedNavigableItems } -[, FSharpConstants.FSharpLanguageName); Shared>] +[)>] type internal FSharpNavigateToSearchService [] ( @@ -190,7 +172,7 @@ type internal FSharpNavigateToSearchService projectInfoManager: FSharpProjectOptionsManager ) = - let kindsProvided = ImmutableHashSet.Create(NavigateToItemKind.Module, NavigateToItemKind.Class, NavigateToItemKind.Field, NavigateToItemKind.Property, NavigateToItemKind.Method, NavigateToItemKind.Enum, NavigateToItemKind.EnumItem) :> IImmutableSet + let kindsProvided = ImmutableHashSet.Create(FSharpNavigateToItemKind.Module, FSharpNavigateToItemKind.Class, FSharpNavigateToItemKind.Field, FSharpNavigateToItemKind.Property, FSharpNavigateToItemKind.Method, FSharpNavigateToItemKind.Enum, FSharpNavigateToItemKind.EnumItem) :> IImmutableSet // Save the backing navigation data in a memory cache held in a sliding window let itemsByDocumentId = new MemoryCache("FSharp.Editor.FSharpNavigateToSearchService") @@ -237,15 +219,15 @@ type internal FSharpNavigateToSearchService return indexedItems } let patternMatchKindToNavigateToMatchKind = function - | PatternMatchKind.Exact -> NavigateToMatchKind.Exact - | PatternMatchKind.Prefix -> NavigateToMatchKind.Prefix - | PatternMatchKind.Substring -> NavigateToMatchKind.Substring - | PatternMatchKind.CamelCase -> NavigateToMatchKind.Regular - | PatternMatchKind.Fuzzy -> NavigateToMatchKind.Regular - | _ -> NavigateToMatchKind.Regular - - interface INavigateToSearchService_RemoveInterfaceAboveAndRenameThisAfterInternalsVisibleToUsersUpdate with - member __.SearchProjectAsync(project, _priorityDocuments, searchPattern, kinds, cancellationToken) : Task> = + | PatternMatchKind.Exact -> FSharpNavigateToMatchKind.Exact + | PatternMatchKind.Prefix -> FSharpNavigateToMatchKind.Prefix + | PatternMatchKind.Substring -> FSharpNavigateToMatchKind.Substring + | PatternMatchKind.CamelCase -> FSharpNavigateToMatchKind.Regular + | PatternMatchKind.Fuzzy -> FSharpNavigateToMatchKind.Regular + | _ -> FSharpNavigateToMatchKind.Regular + + interface IFSharpNavigateToSearchService with + member __.SearchProjectAsync(project, _priorityDocuments, searchPattern, kinds, cancellationToken) : Task> = asyncMaybe { let! parsingOptions, _options = projectInfoManager.TryGetOptionsByProject(project, cancellationToken) let! items = @@ -268,7 +250,7 @@ type internal FSharpNavigateToSearchService |> Array.Parallel.collect (fun x -> patternMatcher.GetMatches(x.Name) |> Seq.map (fun pm -> - NavigateToSearchResult(x, patternMatchKindToNavigateToMatchKind pm.Kind) :> INavigateToSearchResult) + NavigateToSearchResult(x, patternMatchKindToNavigateToMatchKind pm.Kind) :> FSharpNavigateToSearchResult) |> Seq.toArray) |] return items |> Array.distinctBy (fun x -> x.NavigableItem.Document.Id, x.NavigableItem.SourceSpan) @@ -277,7 +259,7 @@ type internal FSharpNavigateToSearchService |> Async.map Seq.toImmutableArray |> RoslynHelpers.StartAsyncAsTask(cancellationToken) - member __.SearchDocumentAsync(document, searchPattern, kinds, cancellationToken) : Task> = + member __.SearchDocumentAsync(document, searchPattern, kinds, cancellationToken) : Task> = asyncMaybe { let! parsingOptions, _, _ = projectInfoManager.TryGetOptionsForDocumentOrProject(document, cancellationToken) let! items = getCachedIndexedNavigableItems(document, parsingOptions, kinds) |> liftAsync diff --git a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs index 18115fa62fd..d8edebd3ac1 100644 --- a/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs +++ b/vsintegration/src/FSharp.Editor/Navigation/NavigationBarItemService.fs @@ -10,13 +10,15 @@ open Microsoft.CodeAnalysis.Editor open Microsoft.CodeAnalysis.Navigation open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.Notification +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Navigation open FSharp.Compiler.SourceCodeServices type internal NavigationBarSymbolItem(text, glyph, spans, childItems) = - inherit NavigationBarItem(text, glyph, spans, childItems) + inherit FSharpNavigationBarItem(text, glyph, spans, childItems) -[, FSharpConstants.FSharpLanguageName); Shared>] +[)>] type internal FSharpNavigationBarItemService [] ( @@ -25,10 +27,10 @@ type internal FSharpNavigationBarItemService ) = static let userOpName = "NavigationBarItem" - static let emptyResult: IList = upcast [||] + static let emptyResult: IList = upcast [||] - interface INavigationBarItemService with - member __.GetItemsAsync(document, cancellationToken) : Task> = + interface IFSharpNavigationBarItemService with + member __.GetItemsAsync(document, cancellationToken) : Task> = asyncMaybe { let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken) let! sourceText = document.GetTextAsync(cancellationToken) @@ -45,25 +47,10 @@ type internal FSharpNavigationBarItemService |> Array.choose (fun decl -> rangeToTextSpan(decl.Range) |> Option.map(fun textSpan -> - NavigationBarSymbolItem(decl.Name, Microsoft.CodeAnalysis.ExternalAccess.FSharp.FSharpGlyphHelpersObsolete.Convert(decl.RoslynGlyph), [| textSpan |], null) :> NavigationBarItem)) + NavigationBarSymbolItem(decl.Name, decl.RoslynGlyph, [| textSpan |], null) :> FSharpNavigationBarItem)) - NavigationBarSymbolItem(topLevelDecl.Declaration.Name, Microsoft.CodeAnalysis.ExternalAccess.FSharp.FSharpGlyphHelpersObsolete.Convert(topLevelDecl.Declaration.RoslynGlyph), [| topLevelTextSpan |], childItems) - :> NavigationBarItem)) :> IList<_> + NavigationBarSymbolItem(topLevelDecl.Declaration.Name, topLevelDecl.Declaration.RoslynGlyph, [| topLevelTextSpan |], childItems) + :> FSharpNavigationBarItem)) :> IList<_> } |> Async.map (Option.defaultValue emptyResult) |> RoslynHelpers.StartAsyncAsTask(cancellationToken) - - member __.ShowItemGrayedIfNear (_item) : bool = false - - member __.NavigateToItem(document, item, _view, _cancellationToken) = - match item.Spans |> Seq.tryHead with - | Some span -> - let workspace = document.Project.Solution.Workspace - let navigationService = workspace.Services.GetService() - - if navigationService.CanNavigateToPosition(workspace, document.Id, span.Start) then - navigationService.TryNavigateToPosition(workspace, document.Id, span.Start) |> ignore - else - let notificationService = workspace.Services.GetService() - notificationService.SendNotification(EditorFeaturesResources.The_definition_of_the_object_is_hidden, severity = NotificationSeverity.Error) - | None -> () \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs index ab2c04ddca2..996845ed516 100644 --- a/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs +++ b/vsintegration/src/FSharp.Editor/Structure/BlockStructureService.fs @@ -10,6 +10,7 @@ open Microsoft.CodeAnalysis open Microsoft.CodeAnalysis.Host.Mef open Microsoft.CodeAnalysis.Text open Microsoft.CodeAnalysis.Structure +open Microsoft.CodeAnalysis.ExternalAccess.FSharp.Structure open FSharp.Compiler open FSharp.Compiler.SourceCodeServices @@ -17,9 +18,9 @@ open FSharp.Compiler.SourceCodeServices.Structure module internal BlockStructure = let scopeToBlockType = function - | Scope.Open -> BlockTypes.Imports + | Scope.Open -> FSharpBlockTypes.Imports | Scope.Namespace - | Scope.Module -> BlockTypes.Namespace + | Scope.Module -> FSharpBlockTypes.Namespace | Scope.Record | Scope.Interface | Scope.TypeExtension @@ -28,10 +29,10 @@ module internal BlockStructure = | Scope.ObjExpr | Scope.UnionDefn | Scope.Attribute - | Scope.Type -> BlockTypes.Type + | Scope.Type -> FSharpBlockTypes.Type | Scope.New | Scope.RecordField - | Scope.Member -> BlockTypes.Member + | Scope.Member -> FSharpBlockTypes.Member | Scope.LetOrUse | Scope.Match | Scope.MatchBang @@ -47,7 +48,7 @@ module internal BlockStructure = | Scope.TryFinally | Scope.TryInTryFinally | Scope.FinallyInTryFinally - | Scope.IfThenElse-> BlockTypes.Conditional + | Scope.IfThenElse-> FSharpBlockTypes.Conditional | Scope.Tuple | Scope.ArrayOrList | Scope.CompExprInternal @@ -58,13 +59,13 @@ module internal BlockStructure = | Scope.Val | Scope.YieldOrReturn | Scope.YieldOrReturnBang - | Scope.TryWith -> BlockTypes.Expression - | Scope.Do -> BlockTypes.Statement + | Scope.TryWith -> FSharpBlockTypes.Expression + | Scope.Do -> FSharpBlockTypes.Statement | Scope.While - | Scope.For -> BlockTypes.Loop - | Scope.HashDirective -> BlockTypes.PreprocessorRegion + | Scope.For -> FSharpBlockTypes.Loop + | Scope.HashDirective -> FSharpBlockTypes.PreprocessorRegion | Scope.Comment - | Scope.XmlDocComment -> BlockTypes.Comment + | Scope.XmlDocComment -> FSharpBlockTypes.Comment let isAutoCollapsible = function | Scope.New @@ -134,33 +135,27 @@ module internal BlockStructure = match Option.ofNullable (line.Span.Intersection textSpan) with | Some span -> sourceText.GetSubText(span).ToString()+"..." | None -> "..." - let blockType = if isBlockStructureEnabled then scopeToBlockType scopeRange.Scope else BlockTypes.Nonstructural - Some (BlockSpan(blockType, true, textSpan, hintSpan, bannerText, autoCollapse = isAutoCollapsible scopeRange.Scope)) + let blockType = if isBlockStructureEnabled then scopeToBlockType scopeRange.Scope else FSharpBlockTypes.Nonstructural + Some (FSharpBlockSpan(blockType, true, textSpan, hintSpan, bannerText, autoCollapse = isAutoCollapsible scopeRange.Scope)) | _, _ -> None ) open BlockStructure -type internal FSharpBlockStructureService(checker: FSharpChecker, projectInfoManager: FSharpProjectOptionsManager) = - inherit BlockStructureService() +[)>] +type internal FSharpBlockStructureService [] (checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager) = - static let userOpName = "BlockStructure" + static let userOpName = "FSharpBlockStructure" - override __.Language = FSharpConstants.FSharpLanguageName + interface IFSharpBlockStructureService with - override __.GetBlockStructureAsync(document, cancellationToken) : Task = - asyncMaybe { - let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken) - let! sourceText = document.GetTextAsync(cancellationToken) - let! parsedInput = checker.ParseDocument(document, parsingOptions, sourceText, userOpName) - return createBlockSpans document.FSharpOptions.Advanced.IsBlockStructureEnabled sourceText parsedInput |> Seq.toImmutableArray - } - |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) - |> Async.map BlockStructure - |> RoslynHelpers.StartAsyncAsTask(cancellationToken) - -[, FSharpConstants.FSharpLanguageName); Shared>] -type internal FSharpBlockStructureServiceFactory [](checkerProvider: FSharpCheckerProvider, projectInfoManager: FSharpProjectOptionsManager) = - interface ILanguageServiceFactory with - member __.CreateLanguageService(_languageServices) = - upcast FSharpBlockStructureService(checkerProvider.Checker, projectInfoManager) \ No newline at end of file + member __.GetBlockStructureAsync(document, cancellationToken) : Task = + asyncMaybe { + let! parsingOptions, _options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, cancellationToken) + let! sourceText = document.GetTextAsync(cancellationToken) + let! parsedInput = checkerProvider.Checker.ParseDocument(document, parsingOptions, sourceText, userOpName) + return createBlockSpans document.FSharpOptions.Advanced.IsBlockStructureEnabled sourceText parsedInput |> Seq.toImmutableArray + } + |> Async.map (Option.defaultValue ImmutableArray<_>.Empty) + |> Async.map FSharpBlockStructure + |> RoslynHelpers.StartAsyncAsTask(cancellationToken) diff --git a/vsintegration/src/FSharp.LanguageService/FSharp.LanguageService.fsproj b/vsintegration/src/FSharp.LanguageService/FSharp.LanguageService.fsproj index cf72f552163..7692f24fcb1 100644 --- a/vsintegration/src/FSharp.LanguageService/FSharp.LanguageService.fsproj +++ b/vsintegration/src/FSharp.LanguageService/FSharp.LanguageService.fsproj @@ -81,6 +81,7 @@ + diff --git a/vsintegration/tests/GetTypesVS.UnitTests/GetTypesVS.UnitTests.fsproj b/vsintegration/tests/GetTypesVS.UnitTests/GetTypesVS.UnitTests.fsproj index d44b41fab37..8c70a8c5e0a 100644 --- a/vsintegration/tests/GetTypesVS.UnitTests/GetTypesVS.UnitTests.fsproj +++ b/vsintegration/tests/GetTypesVS.UnitTests/GetTypesVS.UnitTests.fsproj @@ -1,4 +1,4 @@ - + @@ -24,6 +24,7 @@ + diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index 7b11fb96c6c..7a0bea77017 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -253,6 +253,7 @@ +