1+ // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+ namespace rec Microsoft.VisualStudio.FSharp.Editor
4+
5+ open System
6+ open System.Composition
7+ open System.Threading
8+ open System.Threading .Tasks
9+
10+ open Microsoft.CodeAnalysis
11+ open Microsoft.CodeAnalysis .Text
12+ open Microsoft.CodeAnalysis .CodeFixes
13+ open Microsoft.CodeAnalysis .CodeActions
14+
15+ open Microsoft.FSharp .Compiler
16+ open Microsoft.FSharp .Compiler .SourceCodeServices
17+
18+ [<ExportCodeFixProvider( FSharpConstants.FSharpLanguageName, Name= " RenameUnusedValue" ); Shared>]
19+ type internal FSharpRenameUnusedValueCodeFixProvider
20+ [<ImportingConstructor>]
21+ (
22+ checkerProvider: FSharpCheckerProvider,
23+ projectInfoManager: ProjectInfoManager
24+ ) =
25+
26+ inherit CodeFixProvider()
27+ let fixableDiagnosticIds = [ " FS1182" ]
28+
29+ let createCodeFix ( context : CodeFixContext , symbolName : string , titleFormat : string , textChange : TextChange ) =
30+ let title = String.Format( titleFormat, symbolName)
31+ let codeAction =
32+ CodeAction.Create(
33+ title,
34+ ( fun ( cancellationToken : CancellationToken ) ->
35+ async {
36+ let! sourceText = context.Document.GetTextAsync()
37+ return context.Document.WithText( sourceText.WithChanges( textChange))
38+ } |> RoslynHelpers.StartAsyncAsTask( cancellationToken)),
39+ title)
40+ let diagnostics = context.Diagnostics|> Seq.filter( fun x -> fixableDiagnosticIds|> List.contains x.Id) |> Seq.toImmutableArray
41+ context.RegisterCodeFix( codeAction, diagnostics)
42+
43+ override __.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds
44+
45+ override __.RegisterCodeFixesAsync context : Task =
46+ asyncMaybe {
47+ let document = context.Document
48+ let! sourceText = document.GetTextAsync()
49+ let ident = sourceText.ToString( context.Span)
50+ // Prefixing operators and backticked identifiers does not make sense.
51+ // We have to use the additional check for backtickes because `IsOperatorOrBacktickedName` operates on display names
52+ // where backtickes are replaced with parens.
53+ if not ( PrettyNaming.IsOperatorOrBacktickedName ident) && not ( ident.StartsWith" ``" ) then
54+ let! options = projectInfoManager.TryGetOptionsForEditingDocumentOrProject document
55+ let! _ , _ , checkResults = checkerProvider.Checker.ParseAndCheckDocument( document, options, allowStaleResults= true , sourceText= sourceText)
56+ let m = RoslynHelpers.TextSpanToFSharpRange( document.FilePath, context.Span, sourceText)
57+ let defines = CompilerEnvironment.GetCompilationDefinesForEditing( document.FilePath, options.OtherOptions|> Seq.toList)
58+ let! lexerSymbol = Tokenizer.getSymbolAtPosition( document.Id, sourceText, context.Span.Start, document.FilePath, defines, SymbolLookupKind.Greedy, false )
59+ let lineText = ( sourceText.Lines.GetLineFromPosition context.Span.Start). ToString()
60+ let! symbolUse = checkResults.GetSymbolUseAtLocation( m.StartLine, m.EndColumn, lineText, lexerSymbol.FullIsland)
61+ let symbolName = symbolUse.Symbol.DisplayName
62+
63+ match symbolUse.Symbolwith
64+ | : ? FSharpMemberOrFunctionOrValue as func ->
65+ createCodeFix( context, symbolName, SR.PrefixValueNameWithUnderscore.Value, TextChange( TextSpan( context.Span.Start, 0 ), " _" ))
66+
67+ if func.IsMemberThisValuethen
68+ createCodeFix( context, symbolName, SR.RenameValueToDoubleUnderscore.Value, TextChange( context.Span, " __" ))
69+ elif not func.IsMemberthen
70+ createCodeFix( context, symbolName, SR.RenameValueToUnderscore.Value, TextChange( context.Span, " _" ))
71+ | _ -> ()
72+ }
73+ |> Async.Ignore
74+ |> RoslynHelpers.StartAsyncUnitAsTask( context.CancellationToken)