From b2e654c05a1394ee8a09906ecfbf389b081a1307 Mon Sep 17 00:00:00 2001 From: Phillip Carter Date: Sat, 13 Mar 2021 07:34:15 -0800 Subject: [PATCH] Add "Add type annotation to parameter" refactoring + remove dangling file lost in recent refactorings (#10937) Co-authored-by: Chet Husk --- FSharp.Editor.fsproj | 1 + FSharp.Editor.resx | 3 + Refactor/AddExplicitTypeToParameter.fs | 102 +++++++++++++++++++++++++ xlf/FSharp.Editor.cs.xlf | 5 ++ xlf/FSharp.Editor.de.xlf | 5 ++ xlf/FSharp.Editor.es.xlf | 5 ++ xlf/FSharp.Editor.fr.xlf | 5 ++ xlf/FSharp.Editor.it.xlf | 5 ++ xlf/FSharp.Editor.ja.xlf | 5 ++ xlf/FSharp.Editor.ko.xlf | 5 ++ xlf/FSharp.Editor.pl.xlf | 5 ++ xlf/FSharp.Editor.pt-BR.xlf | 5 ++ xlf/FSharp.Editor.ru.xlf | 5 ++ xlf/FSharp.Editor.tr.xlf | 5 ++ xlf/FSharp.Editor.zh-Hans.xlf | 5 ++ xlf/FSharp.Editor.zh-Hant.xlf | 5 ++ 16 files changed, 171 insertions(+) create mode 100644 Refactor/AddExplicitTypeToParameter.fs diff --git a/FSharp.Editor.fsproj b/FSharp.Editor.fsproj index 9315b599893..9130c7573c7 100644 --- a/FSharp.Editor.fsproj +++ b/FSharp.Editor.fsproj @@ -89,6 +89,7 @@ + diff --git a/FSharp.Editor.resx b/FSharp.Editor.resx index 68466c81559..57bb282a098 100644 --- a/FSharp.Editor.resx +++ b/FSharp.Editor.resx @@ -273,4 +273,7 @@ Use '<>' for inequality check + + Add type annotation + \ No newline at end of file diff --git a/Refactor/AddExplicitTypeToParameter.fs b/Refactor/AddExplicitTypeToParameter.fs new file mode 100644 index 00000000000..569cc1b57dd --- /dev/null +++ b/Refactor/AddExplicitTypeToParameter.fs @@ -0,0 +1,102 @@ +// 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 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 + +[] +type internal FSharpAddExplicitTypeToParameterRefactoring + [] + ( + checkerProvider: FSharpCheckerProvider, + projectInfoManager: FSharpProjectOptionsManager + ) = + inherit CodeRefactoringProvider() + + static let userOpName = "AddExplicitTypeToParameter" + + override _.ComputeRefactoringsAsync context = + asyncMaybe { + let document = context.Document + let position = context.Span.Start + let checker = checkerProvider.Checker + let! parsingOptions, projectOptions = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, CancellationToken.None, userOpName) + let! sourceText = document.GetTextAsync () |> liftTaskAsync + let defines = CompilerEnvironment.GetCompilationDefinesForEditing parsingOptions + let textLine = sourceText.Lines.GetLineFromPosition position + let textLinePos = sourceText.Lines.GetLinePosition position + let fcsTextLineNumber = Line.fromZ textLinePos.Line + let! parseFileResults, _, checkFileResults = checker.ParseAndCheckDocument (document, projectOptions, sourceText=sourceText, userOpName=userOpName) + let! lexerSymbol = Tokenizer.getSymbolAtPosition (document.Id, sourceText, position, document.FilePath, defines, SymbolLookupKind.Greedy, false, false) + let! symbolUse = checkFileResults.GetSymbolUseAtLocation(fcsTextLineNumber, lexerSymbol.Ident.idRange.EndColumn, textLine.ToString(), lexerSymbol.FullIsland) + + let isValidParameterWithoutTypeAnnotation (funcOrValue: FSharpMemberOrFunctionOrValue) (symbolUse: FSharpSymbolUse) = + let isLambdaIfFunction = + funcOrValue.IsFunction && + parseFileResults.IsBindingALambdaAtPosition symbolUse.Range.Start + + (funcOrValue.IsValue || isLambdaIfFunction) && + parseFileResults.IsPositionContainedInACurriedParameter symbolUse.Range.Start && + not (parseFileResults.IsTypeAnnotationGivenAtPosition symbolUse.Range.Start) && + not funcOrValue.IsMember && + not funcOrValue.IsMemberThisValue && + not funcOrValue.IsConstructorThisValue && + not (PrettyNaming.IsOperatorName funcOrValue.DisplayName) + + match symbolUse.Symbol with + | :? FSharpMemberOrFunctionOrValue as v when isValidParameterWithoutTypeAnnotation v symbolUse -> + let typeString = v.FullType.FormatWithConstraints symbolUse.DisplayContext + let title = SR.AddTypeAnnotation() + + let! symbolSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, symbolUse.Range) + + let alreadyWrappedInParens = + let rec leftLoop ch pos = + if not (Char.IsWhiteSpace(ch)) then + ch = '(' + else + leftLoop sourceText.[pos - 1] (pos - 1) + + let rec rightLoop ch pos = + if not (Char.IsWhiteSpace(ch)) then + ch = ')' + else + rightLoop sourceText.[pos + 1] (pos + 1) + + let hasLeftParen = leftLoop sourceText.[symbolSpan.Start - 1] (symbolSpan.Start - 1) + let hasRightParen = rightLoop sourceText.[symbolSpan.End] symbolSpan.End + hasLeftParen && hasRightParen + + let getChangedText (sourceText: SourceText) = + if alreadyWrappedInParens then + sourceText.WithChanges(TextChange(TextSpan(symbolSpan.End, 0), ": " + typeString)) + else + sourceText.WithChanges(TextChange(TextSpan(symbolSpan.Start, 0), "(")) + .WithChanges(TextChange(TextSpan(symbolSpan.End + 1, 0), ": " + typeString + ")")) + + let codeAction = + CodeAction.Create( + title, + (fun (cancellationToken: CancellationToken) -> + async { + let! sourceText = context.Document.GetTextAsync(cancellationToken) |> Async.AwaitTask + return context.Document.WithText(getChangedText sourceText) + } |> RoslynHelpers.StartAsyncAsTask(cancellationToken)), + title) + context.RegisterRefactoring(codeAction) + | _ -> () + } + |> Async.Ignore + |> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken) diff --git a/xlf/FSharp.Editor.cs.xlf b/xlf/FSharp.Editor.cs.xlf index b7c7e461466..251a1388662 100644 --- a/xlf/FSharp.Editor.cs.xlf +++ b/xlf/FSharp.Editor.cs.xlf @@ -17,6 +17,11 @@ Přidejte klíčové slovo new. + + Add type annotation + Add type annotation + + Convert to Anonymous Record Převést na anonymní záznam diff --git a/xlf/FSharp.Editor.de.xlf b/xlf/FSharp.Editor.de.xlf index 81f0fb805f4..9249c295f7a 100644 --- a/xlf/FSharp.Editor.de.xlf +++ b/xlf/FSharp.Editor.de.xlf @@ -17,6 +17,11 @@ Schlüsselwort "new" hinzufügen + + Add type annotation + Add type annotation + + Convert to Anonymous Record In anonymen Datensatz konvertieren diff --git a/xlf/FSharp.Editor.es.xlf b/xlf/FSharp.Editor.es.xlf index 82b3c3dc8b5..4b002337fc9 100644 --- a/xlf/FSharp.Editor.es.xlf +++ b/xlf/FSharp.Editor.es.xlf @@ -17,6 +17,11 @@ Agregar "nueva" palabra clave + + Add type annotation + Add type annotation + + Convert to Anonymous Record Convertir en registro anónimo diff --git a/xlf/FSharp.Editor.fr.xlf b/xlf/FSharp.Editor.fr.xlf index 7a5eeb6038e..3f304fecda3 100644 --- a/xlf/FSharp.Editor.fr.xlf +++ b/xlf/FSharp.Editor.fr.xlf @@ -17,6 +17,11 @@ Ajouter le mot clé 'new' + + Add type annotation + Add type annotation + + Convert to Anonymous Record Convertir en enregistrement anonyme diff --git a/xlf/FSharp.Editor.it.xlf b/xlf/FSharp.Editor.it.xlf index 0c734760f8e..5f2e37301c0 100644 --- a/xlf/FSharp.Editor.it.xlf +++ b/xlf/FSharp.Editor.it.xlf @@ -17,6 +17,11 @@ Aggiungi la parola chiave 'new' + + Add type annotation + Add type annotation + + Convert to Anonymous Record Converti in record anonimo diff --git a/xlf/FSharp.Editor.ja.xlf b/xlf/FSharp.Editor.ja.xlf index b295d28b496..d8e55789efe 100644 --- a/xlf/FSharp.Editor.ja.xlf +++ b/xlf/FSharp.Editor.ja.xlf @@ -17,6 +17,11 @@ 'new' キーワードを追加する + + Add type annotation + Add type annotation + + Convert to Anonymous Record 匿名レコードに変換 diff --git a/xlf/FSharp.Editor.ko.xlf b/xlf/FSharp.Editor.ko.xlf index d07b4e00079..1b3c41a2268 100644 --- a/xlf/FSharp.Editor.ko.xlf +++ b/xlf/FSharp.Editor.ko.xlf @@ -17,6 +17,11 @@ 'new' 키워드 추가 + + Add type annotation + Add type annotation + + Convert to Anonymous Record 익명 레코드로 변환 diff --git a/xlf/FSharp.Editor.pl.xlf b/xlf/FSharp.Editor.pl.xlf index 7e04e28c303..d9ea6371acd 100644 --- a/xlf/FSharp.Editor.pl.xlf +++ b/xlf/FSharp.Editor.pl.xlf @@ -17,6 +17,11 @@ Dodaj słowo kluczowe „new” + + Add type annotation + Add type annotation + + Convert to Anonymous Record Konwertuj na rekord anonimowy diff --git a/xlf/FSharp.Editor.pt-BR.xlf b/xlf/FSharp.Editor.pt-BR.xlf index b5a9a077815..b76c694378d 100644 --- a/xlf/FSharp.Editor.pt-BR.xlf +++ b/xlf/FSharp.Editor.pt-BR.xlf @@ -17,6 +17,11 @@ Adicionar a palavra-chave 'new' + + Add type annotation + Add type annotation + + Convert to Anonymous Record Converter em Registro Anônimo diff --git a/xlf/FSharp.Editor.ru.xlf b/xlf/FSharp.Editor.ru.xlf index 508c9c4ab6e..3f6596d7492 100644 --- a/xlf/FSharp.Editor.ru.xlf +++ b/xlf/FSharp.Editor.ru.xlf @@ -17,6 +17,11 @@ Добавить ключевое слово "new" + + Add type annotation + Add type annotation + + Convert to Anonymous Record Преобразовать в анонимную запись diff --git a/xlf/FSharp.Editor.tr.xlf b/xlf/FSharp.Editor.tr.xlf index 02937e23c91..0d433910885 100644 --- a/xlf/FSharp.Editor.tr.xlf +++ b/xlf/FSharp.Editor.tr.xlf @@ -17,6 +17,11 @@ 'new' anahtar sözcüğünü ekleme + + Add type annotation + Add type annotation + + Convert to Anonymous Record Anonim Kayda Dönüştür diff --git a/xlf/FSharp.Editor.zh-Hans.xlf b/xlf/FSharp.Editor.zh-Hans.xlf index b30d5243dcf..020ff6e6b43 100644 --- a/xlf/FSharp.Editor.zh-Hans.xlf +++ b/xlf/FSharp.Editor.zh-Hans.xlf @@ -17,6 +17,11 @@ 添加“新”关键字 + + Add type annotation + Add type annotation + + Convert to Anonymous Record 转换为匿名记录 diff --git a/xlf/FSharp.Editor.zh-Hant.xlf b/xlf/FSharp.Editor.zh-Hant.xlf index 9b9d615aa0d..e6e56785150 100644 --- a/xlf/FSharp.Editor.zh-Hant.xlf +++ b/xlf/FSharp.Editor.zh-Hant.xlf @@ -17,6 +17,11 @@ 新增 'new' 關鍵字 + + Add type annotation + Add type annotation + + Convert to Anonymous Record 轉換為匿名記錄