forked from dotnet/fsharp
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First Draft for AddExplicitReturnType (dotnet#15562) with exploration…
… test and resource file entry
- Loading branch information
1 parent
1af023b
commit e27012e
Showing
5 changed files
with
167 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
vsintegration/src/FSharp.Editor/Refactor/AddExplicitReturnType.fs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. | ||
|
||
namespace Microsoft.VisualStudio.FSharp.Editor | ||
|
||
open System | ||
open System.Composition | ||
open System.Threading | ||
open System.Threading.Tasks | ||
|
||
open FSharp.Compiler | ||
open FSharp.Compiler.CodeAnalysis | ||
open FSharp.Compiler.Symbols | ||
open FSharp.Compiler.Text | ||
open FSharp.Compiler.Syntax | ||
|
||
open Microsoft.CodeAnalysis.Text | ||
open Microsoft.CodeAnalysis.CodeRefactorings | ||
open Microsoft.CodeAnalysis.CodeActions | ||
open CancellableTasks | ||
|
||
[<ExportCodeRefactoringProvider(FSharpConstants.FSharpLanguageName, Name = "AddExplicitReturnType"); Shared>] | ||
type internal AddExplicitReturnType [<ImportingConstructor>] () = | ||
inherit CodeRefactoringProvider() | ||
|
||
static member isValidMethodWithoutTypeAnnotation (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) (parseFileResults:FSharpParseFileResults) = | ||
let isLambdaIfFunction = | ||
funcOrValue.IsFunction | ||
&& parseFileResults.IsBindingALambdaAtPosition symbolUse.Range.Start | ||
|
||
(not funcOrValue.IsValue || not isLambdaIfFunction) | ||
&& not (funcOrValue.ReturnParameter.Type.IsUnresolved) | ||
&& not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start) | ||
|
||
|
||
static member refactor (context:CodeRefactoringContext) (symbolUse:FSharpSymbolUse,memberFunc:FSharpMemberOrFunctionOrValue,symbolSpan) = | ||
let typeString = memberFunc.FullType.FormatWithConstraints symbolUse.DisplayContext | ||
let title = SR.AddExplicitReturnTypeAnnotation() | ||
|
||
let getChangedText (sourceText: SourceText) = | ||
let debugInfo = $"{sourceText} : {typeString} : {symbolSpan}" | ||
debugInfo | ||
let sub = sourceText.ToString(symbolSpan) | ||
|
||
let newSub = | ||
sub.Replace("=", $" :{memberFunc.ReturnParameter.Type.TypeDefinition.DisplayName}=") | ||
|
||
sourceText.Replace(symbolSpan, newSub) | ||
|
||
let codeActionFunc = (fun (cancellationToken: CancellationToken) -> | ||
backgroundTask { | ||
let! sourceText = context.Document.GetTextAsync(cancellationToken) | ||
let changedText = getChangedText sourceText | ||
return context.Document.WithText(changedText) | ||
} | ||
) | ||
let codeAction = | ||
CodeAction.Create( | ||
title, | ||
codeActionFunc, | ||
title | ||
) | ||
|
||
|
||
do context.RegisterRefactoring(codeAction) | ||
|
||
override _.ComputeRefactoringsAsync context = | ||
backgroundTask { | ||
let document = context.Document | ||
let position = context.Span.Start | ||
let! sourceText = document.GetTextAsync() | ||
let textLine = sourceText.Lines.GetLineFromPosition position | ||
let textLinePos = sourceText.Lines.GetLinePosition position | ||
let fcsTextLineNumber = Line.fromZ textLinePos.Line | ||
|
||
let! ct = Async.CancellationToken | ||
|
||
let! lexerSymbol = | ||
document.TryFindFSharpLexerSymbolAsync(position, SymbolLookupKind.Greedy, false, false, nameof (AddExplicitReturnType)) | ||
|
||
|
||
let! (parseFileResults,checkFileResults) = | ||
document.GetFSharpParseAndCheckResultsAsync(nameof (AddExplicitReturnType)) | ||
|> CancellableTask.start ct | ||
|
||
let res = | ||
lexerSymbol | ||
|> Option.bind(fun lexer -> | ||
checkFileResults.GetSymbolUseAtLocation( | ||
fcsTextLineNumber, | ||
lexer.Ident.idRange.EndColumn, | ||
textLine.ToString(), | ||
lexer.FullIsland | ||
)) | ||
|> Option.bind(fun symbolUse-> | ||
match symbolUse.Symbol with | ||
| :? FSharpMemberOrFunctionOrValue as v | ||
when AddExplicitReturnType.isValidMethodWithoutTypeAnnotation v symbolUse parseFileResults-> | ||
Some (symbolUse,v) | ||
| _ -> None | ||
) | ||
|> Option.bind(fun (symbolUse,memberFunc)-> | ||
match RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) with | ||
| Some span -> Some(symbolUse,memberFunc,span) | ||
| None -> None | ||
) | ||
|> Option.map(fun (symbolUse,memberFunc,textSpan) -> AddExplicitReturnType.refactor context (symbolUse,memberFunc,textSpan)) | ||
|
||
return res | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
vsintegration/tests/FSharp.Editor.Tests/Refactors/AddExplicitReturnType.fs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
module FSharp.Editor.Tests.Refactors.AddExplicitReturnType | ||
|
||
open Microsoft.VisualStudio.FSharp.Editor | ||
open Xunit | ||
open System | ||
open System.Collections.Immutable | ||
open System.Text.RegularExpressions | ||
|
||
open Microsoft.CodeAnalysis | ||
open Microsoft.CodeAnalysis.CodeFixes | ||
open Microsoft.CodeAnalysis.Text | ||
open Microsoft.VisualStudio.FSharp.Editor | ||
open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks | ||
|
||
open FSharp.Compiler.Diagnostics | ||
open FSharp.Editor.Tests.Helpers | ||
|
||
open Microsoft.CodeAnalysis.CodeRefactorings | ||
open NUnit.Framework | ||
|
||
|
||
[<Fact>] | ||
let ``Refactor changes something`` () = | ||
task { | ||
let code = | ||
""" | ||
let sum a b = a + b | ||
""" | ||
|
||
let! ct = Async.CancellationToken | ||
|
||
let sourceText = SourceText.From code | ||
let document = RoslynTestHelpers.GetFsDocument code | ||
let spanStart = code.IndexOf "sum" | ||
let span = TextSpan(spanStart, 3) | ||
|
||
let mutable refactorContext = | ||
CodeRefactoringContext(document, span, Action<CodeActions.CodeAction>(fun a -> ()), ct) | ||
|
||
let refactorProvider = AddExplicitReturnType() | ||
let expected = None | ||
|
||
do! refactorProvider.ComputeRefactoringsAsync refactorContext | ||
let newText = match refactorContext.TextDocument.TryGetText() with | ||
| true,result -> result | ||
| false,_ -> sourceText | ||
|
||
let! text = refactorContext.TextDocument.GetTextAsync( ct) | ||
Assert.AreNotEqual(sourceText.ToString(),newText.ToString(),"") | ||
|
||
() | ||
} |