未验证 提交 3a3fac20 编写于 作者: P Phillip Carter 提交者: GitHub

Add refactoring to type typeof<expr>.Name into nameof(expr) (#11566)

上级 35cbed42
......@@ -9,36 +9,6 @@ open System.Threading.Tasks
open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.CodeFixes
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Syntax
open FSharp.Compiler.Text
[<AutoOpen>]
module FSharpParseFileResultsExtensions =
type FSharpParseFileResults with
member this.TryRangeOfBindingWithHeadPatternWithPos pos =
let input = this.ParseTree
SyntaxTraversal.Traverse(pos, input, { new SyntaxVisitorBase<_>() with
member _.VisitExpr(_, _, defaultTraverse, expr) =
defaultTraverse expr
override _.VisitBinding(_path, defaultTraverse, binding) =
match binding with
| SynBinding(_, SynBindingKind.Normal, _, _, _, _, _, pat, _, _, _, _) as binding ->
if Position.posEq binding.RangeOfHeadPattern.Start pos then
Some binding.RangeOfBindingWithRhs
else
// Check if it's an operator
match pat with
| SynPat.LongIdent(LongIdentWithDots([id], _), _, _, _, _, _) when id.idText.StartsWith("op_") ->
if Position.posEq id.idRange.Start pos then
Some binding.RangeOfBindingWithRhs
else
defaultTraverse binding
| _ -> defaultTraverse binding
| _ -> defaultTraverse binding })
[<ExportCodeFixProvider(FSharpConstants.FSharpLanguageName, Name = "RemoveUnusedBinding"); Shared>]
type internal FSharpRemoveUnusedBindingCodeFixProvider
[<ImportingConstructor>]
......
[<AutoOpen>]
module internal FSharpParseFileResultsExtensions
open FSharp.Compiler.CodeAnalysis
open FSharp.Compiler.Syntax
open FSharp.Compiler.Text
type FSharpParseFileResults with
member this.TryRangeOfBindingWithHeadPatternWithPos pos =
let input = this.ParseTree
SyntaxTraversal.Traverse(pos, input, { new SyntaxVisitorBase<_>() with
member _.VisitExpr(_, _, defaultTraverse, expr) =
defaultTraverse expr
override _.VisitBinding(_path, defaultTraverse, binding) =
match binding with
| SynBinding(_, SynBindingKind.Normal, _, _, _, _, _, pat, _, _, _, _) as binding ->
if Position.posEq binding.RangeOfHeadPattern.Start pos then
Some binding.RangeOfBindingWithRhs
else
// Check if it's an operator
match pat with
| SynPat.LongIdent(LongIdentWithDots([id], _), _, _, _, _, _) when id.idText.StartsWith("op_") ->
if Position.posEq id.idRange.Start pos then
Some binding.RangeOfBindingWithRhs
else
defaultTraverse binding
| _ -> defaultTraverse binding
| _ -> defaultTraverse binding })
member this.TryRangeOfTypeofWithNameAndTypeExpr pos =
SyntaxTraversal.Traverse(pos, this.ParseTree, { new SyntaxVisitorBase<_>() with
member _.VisitExpr(_path, _, defaultTraverse, expr) =
match expr with
| SynExpr.DotGet(expr, _, _, range) ->
match expr with
| SynExpr.TypeApp(SynExpr.Ident(ident), _, typeArgs, _, _, _, _) ->
let onlyOneTypeArg =
match typeArgs with
| [] -> false
| [_] -> true
| _ -> false
if ident.idText = "typeof" && onlyOneTypeArg then
Some {| NamedIdentRange = typeArgs.Head.Range; FullExpressionRange = range |}
else
defaultTraverse expr
| _ -> defaultTraverse expr
| _ -> defaultTraverse expr })
\ No newline at end of file
......@@ -31,6 +31,7 @@
<Compile Include="Common\Error.fs" />
<Compile Include="Common\Logging.fs" />
<Compile Include="Common\RoslynHelpers.fs" />
<Compile Include="Common\FSharpCodeAnalysisExtensions.fs" />
<Compile Include="Common\CodeAnalysisExtensions.fs" />
<Compile Include="Common\Vs.fs" />
<Compile Include="Options\SettingsPersistence.fs" />
......@@ -88,6 +89,7 @@
<Compile Include="Commands\HelpContextService.fs" />
<Compile Include="Commands\FsiCommandService.fs" />
<Compile Include="Commands\XmlDocCommandService.fs" />
<Compile Include="Refactor\ChangeTypeofWithNameToNameofExpression.fs" />
<Compile Include="Refactor\AddExplicitTypeToParameter.fs" />
<Compile Include="Refactor\ChangeDerefToValueRefactoring.fs" />
<Compile Include="CodeFix\CodeFixHelpers.fs" />
......
......@@ -282,4 +282,7 @@
<data name="RemoveUnusedBinding" xml:space="preserve">
<value>Remove unused binding</value>
</data>
<data name="UseNameof" xml:space="preserve">
<value>Use 'nameof'</value>
</data>
</root>
\ No newline at end of file
// 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
[<ExportCodeRefactoringProvider(FSharpConstants.FSharpLanguageName, Name = "ChangeTypeofWithNameToNameofExpression"); Shared>]
type internal FSharpChangeTypeofWithNameToNameofExpressionRefactoring
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
projectInfoManager: FSharpProjectOptionsManager
) =
inherit CodeRefactoringProvider()
static let userOpName = "ChangeTypeofWithNameToNameofExpression"
override _.ComputeRefactoringsAsync context =
asyncMaybe {
let document = context.Document
let! parsingOptions, _ = projectInfoManager.TryGetOptionsForEditingDocumentOrProject(document, context.CancellationToken, userOpName)
let! sourceText = context.Document.GetTextAsync(context.CancellationToken)
let! parseResults = checkerProvider.Checker.ParseDocument(document, parsingOptions, userOpName=userOpName)
let selectionRange = RoslynHelpers.TextSpanToFSharpRange(document.FilePath, context.Span, sourceText)
let! namedTypeOfResults = parseResults.TryRangeOfTypeofWithNameAndTypeExpr(selectionRange.Start)
let! namedTypeSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText, namedTypeOfResults.NamedIdentRange)
let! typeofAndNameSpan = RoslynHelpers.TryFSharpRangeToTextSpan(sourceText,namedTypeOfResults.FullExpressionRange)
let namedTypeName = sourceText.GetSubText(namedTypeSpan)
let replacementString = $"nameof({namedTypeName})"
let title = SR.UseNameof()
let getChangedText (sourceText: SourceText) =
sourceText.WithChanges(TextChange(typeofAndNameSpan, replacementString))
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)
......@@ -252,6 +252,11 @@
<target state="translated">Pokud chcete změnit hodnotu, použijte &lt;-.</target>
<note />
</trans-unit>
<trans-unit id="UseNameof">
<source>Use 'nameof'</source>
<target state="new">Use 'nameof'</target>
<note />
</trans-unit>
<trans-unit id="UseUpcastKeyword">
<source>Use 'upcast'</source>
<target state="translated">Použijte upcast</target>
......
......@@ -252,6 +252,11 @@
<target state="translated">"&lt;-" zum Ändern des Werts verwenden</target>
<note />
</trans-unit>
<trans-unit id="UseNameof">
<source>Use 'nameof'</source>
<target state="new">Use 'nameof'</target>
<note />
</trans-unit>
<trans-unit id="UseUpcastKeyword">
<source>Use 'upcast'</source>
<target state="translated">"upcast" verwenden</target>
......
......@@ -252,6 +252,11 @@
<target state="translated">Usar "&lt;-" para mutar el valor</target>
<note />
</trans-unit>
<trans-unit id="UseNameof">
<source>Use 'nameof'</source>
<target state="new">Use 'nameof'</target>
<note />
</trans-unit>
<trans-unit id="UseUpcastKeyword">
<source>Use 'upcast'</source>
<target state="translated">Usar "upcast"</target>
......
......@@ -252,6 +252,11 @@
<target state="translated">Utiliser '&lt;-' pour muter la valeur</target>
<note />
</trans-unit>
<trans-unit id="UseNameof">
<source>Use 'nameof'</source>
<target state="new">Use 'nameof'</target>
<note />
</trans-unit>
<trans-unit id="UseUpcastKeyword">
<source>Use 'upcast'</source>
<target state="translated">Utiliser 'upcast'</target>
......
......@@ -252,6 +252,11 @@
<target state="translated">Usare '&lt;-' per modificare il valore</target>
<note />
</trans-unit>
<trans-unit id="UseNameof">
<source>Use 'nameof'</source>
<target state="new">Use 'nameof'</target>
<note />
</trans-unit>
<trans-unit id="UseUpcastKeyword">
<source>Use 'upcast'</source>
<target state="translated">Usare 'upcast'</target>
......
......@@ -252,6 +252,11 @@
<target state="translated">'&lt;-' を使用して値を変換する</target>
<note />
</trans-unit>
<trans-unit id="UseNameof">
<source>Use 'nameof'</source>
<target state="new">Use 'nameof'</target>
<note />
</trans-unit>
<trans-unit id="UseUpcastKeyword">
<source>Use 'upcast'</source>
<target state="translated">'upcast' を使用する</target>
......
......@@ -252,6 +252,11 @@
<target state="translated">'&lt;-'를 사용하여 값 변경</target>
<note />
</trans-unit>
<trans-unit id="UseNameof">
<source>Use 'nameof'</source>
<target state="new">Use 'nameof'</target>
<note />
</trans-unit>
<trans-unit id="UseUpcastKeyword">
<source>Use 'upcast'</source>
<target state="translated">'upcast' 사용</target>
......
......@@ -252,6 +252,11 @@
<target state="translated">Użyj znaku „&lt;-” w celu zmodyfikowania wartości</target>
<note />
</trans-unit>
<trans-unit id="UseNameof">
<source>Use 'nameof'</source>
<target state="new">Use 'nameof'</target>
<note />
</trans-unit>
<trans-unit id="UseUpcastKeyword">
<source>Use 'upcast'</source>
<target state="translated">Użyj operatora „upcast”</target>
......
......@@ -252,6 +252,11 @@
<target state="translated">Usar '&lt;-' para modificar o valor</target>
<note />
</trans-unit>
<trans-unit id="UseNameof">
<source>Use 'nameof'</source>
<target state="new">Use 'nameof'</target>
<note />
</trans-unit>
<trans-unit id="UseUpcastKeyword">
<source>Use 'upcast'</source>
<target state="translated">Usar 'upcast'</target>
......
......@@ -252,6 +252,11 @@
<target state="translated">Используйте "&lt;-", чтобы изменить значение</target>
<note />
</trans-unit>
<trans-unit id="UseNameof">
<source>Use 'nameof'</source>
<target state="new">Use 'nameof'</target>
<note />
</trans-unit>
<trans-unit id="UseUpcastKeyword">
<source>Use 'upcast'</source>
<target state="translated">Используйте "upcast"</target>
......
......@@ -252,6 +252,11 @@
<target state="translated">Değeri değiştirmek için '&lt;-' kullan</target>
<note />
</trans-unit>
<trans-unit id="UseNameof">
<source>Use 'nameof'</source>
<target state="new">Use 'nameof'</target>
<note />
</trans-unit>
<trans-unit id="UseUpcastKeyword">
<source>Use 'upcast'</source>
<target state="translated">'upcast' kullan</target>
......
......@@ -252,6 +252,11 @@
<target state="translated">使用 "&lt;-" 来更改值</target>
<note />
</trans-unit>
<trans-unit id="UseNameof">
<source>Use 'nameof'</source>
<target state="new">Use 'nameof'</target>
<note />
</trans-unit>
<trans-unit id="UseUpcastKeyword">
<source>Use 'upcast'</source>
<target state="translated">使用“向上转换”</target>
......
......@@ -252,6 +252,11 @@
<target state="translated">使用 '&lt;-' 來變動值</target>
<note />
</trans-unit>
<trans-unit id="UseNameof">
<source>Use 'nameof'</source>
<target state="new">Use 'nameof'</target>
<note />
</trans-unit>
<trans-unit id="UseUpcastKeyword">
<source>Use 'upcast'</source>
<target state="translated">使用「向上轉型」</target>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册