Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add codefix to convert C# using to F# open #14064

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// 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.Collections.Immutable

open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.Text
open Microsoft.CodeAnalysis.CodeFixes

[<ExportCodeFixProvider(FSharpConstants.FSharpLanguageName, Name = "ConvertCSharpUsingToFSharpOpen"); Shared>]
type internal FSharpConvertCSharpUsingToFSharpOpen
[<ImportingConstructor>]
(
) =
inherit CodeFixProvider()

let fixableDiagnosticIds = set ["FS0039"; "FS0201"]
let usingLength = "using".Length

let isCSharpUsingShapeWithPos (context: CodeFixContext) (sourceText: SourceText) =
// Walk back until whitespace
let mutable pos = context.Span.Start - 1
let mutable ch = sourceText.[pos]
while pos > 0 && not(Char.IsWhiteSpace(ch)) do
pos <- pos - 1
ch <- sourceText.[pos]

// Walk back whitespace
ch <- sourceText.[pos]
while pos > 0 && Char.IsWhiteSpace(ch) do
pos <- pos - 1
ch <- sourceText.[pos]

// Take 'using' slice and don't forget that offset because computer math is annoying
let start = pos - usingLength + 1
let span = TextSpan(start, usingLength)
let slice = sourceText.GetSubText(span).ToString()
struct(slice = "using", start)

let registerCodeFix (context: CodeFixContext) (diagnostics: ImmutableArray<Diagnostic>) (str: string) (span: TextSpan) =
let replacement =
let str = str.Replace("using", "open").Replace(";", "")
TextChange(span, str)
let title = SR.ConvertCSharpUsingToFSharpOpen()
let codeFix =
CodeFixHelpers.createTextChangeCodeFix(
title,
context,
(fun () -> asyncMaybe.Return [| replacement |]))
context.RegisterCodeFix(codeFix, diagnostics)

override _.FixableDiagnosticIds = Seq.toImmutableArray fixableDiagnosticIds

override _.RegisterCodeFixesAsync context =
asyncMaybe {
let diagnostics =
context.Diagnostics
|> Seq.filter (fun x -> fixableDiagnosticIds |> Set.contains x.Id)
|> Seq.toImmutableArray

do! Option.guard(diagnostics.Length > 0)

let! sourceText = context.Document.GetTextAsync(context.CancellationToken)

// TODO: handle single-line case?
let statementWithSemicolonSpan = TextSpan(context.Span.Start, context.Span.Length + 1)

do! Option.guard (sourceText.Length >= statementWithSemicolonSpan.End)

let statementWithSemicolon = sourceText.GetSubText(statementWithSemicolonSpan).ToString()

// Top of the file case -- entire line gets a diagnostic
if (statementWithSemicolon.StartsWith("using") && statementWithSemicolon.EndsWith(";")) then
registerCodeFix context diagnostics statementWithSemicolon statementWithSemicolonSpan
else
// Only the identifier being opened has a diagnostic, so we try to find the rest of the statement
let struct(isCSharpUsingShape, start) = isCSharpUsingShapeWithPos context sourceText
if isCSharpUsingShape then
let len = (context.Span.Start - start) + statementWithSemicolonSpan.Length
let fullSpan = TextSpan(start, len)
let str = sourceText.GetSubText(fullSpan).ToString()
registerCodeFix context diagnostics str fullSpan
}
|> Async.Ignore
|> RoslynHelpers.StartAsyncUnitAsTask(context.CancellationToken)
1 change: 1 addition & 0 deletions vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
<Compile Include="CodeFix\AddInstanceMemberParameter.fs" />
<Compile Include="CodeFix\AddTypeAnnotationToObjectOfIndeterminateType.fs" />
<Compile Include="CodeFix\AddMissingRecToMutuallyRecFunctions.fs" />
<Compile Include="CodeFix\ConvertCSharpUsingToFSharpOpen.fs" />
<Compile Include="CodeFix\ConvertCSharpLambdaToFSharpLambda.fs" />
<Compile Include="CodeFix\MakeOuterBindingRecursive.fs" />
<Compile Include="CodeFix\RemoveReturnOrYield.fs" />
Expand Down
3 changes: 3 additions & 0 deletions vsintegration/src/FSharp.Editor/FSharp.Editor.resx
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,7 @@
<data name="UseTripleQuotedInterpolation" xml:space="preserve">
<value>Use triple quoted string interpolation.</value>
</data>
<data name="ConvertCSharpUsingToFSharpOpen" xml:space="preserve">
<value>Convert C# 'using' to F# 'open'</value>
</data>
</root>
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Přidat anotaci typu</target>
<note />
</trans-unit>
<trans-unit id="ConvertCSharpUsingToFSharpOpen">
<source>Convert C# 'using' to F# 'open'</source>
<target state="new">Convert C# 'using' to F# 'open'</target>
<note />
</trans-unit>
<trans-unit id="ConvertToAnonymousRecord">
<source>Convert to Anonymous Record</source>
<target state="translated">Převést na anonymní záznam</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Typanmerkung hinzufügen</target>
<note />
</trans-unit>
<trans-unit id="ConvertCSharpUsingToFSharpOpen">
<source>Convert C# 'using' to F# 'open'</source>
<target state="new">Convert C# 'using' to F# 'open'</target>
<note />
</trans-unit>
<trans-unit id="ConvertToAnonymousRecord">
<source>Convert to Anonymous Record</source>
<target state="translated">In anonymen Datensatz konvertieren</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Agregar una anotación de tipo</target>
<note />
</trans-unit>
<trans-unit id="ConvertCSharpUsingToFSharpOpen">
<source>Convert C# 'using' to F# 'open'</source>
<target state="new">Convert C# 'using' to F# 'open'</target>
<note />
</trans-unit>
<trans-unit id="ConvertToAnonymousRecord">
<source>Convert to Anonymous Record</source>
<target state="translated">Convertir en registro anónimo</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Ajouter une annotation de type</target>
<note />
</trans-unit>
<trans-unit id="ConvertCSharpUsingToFSharpOpen">
<source>Convert C# 'using' to F# 'open'</source>
<target state="new">Convert C# 'using' to F# 'open'</target>
<note />
</trans-unit>
<trans-unit id="ConvertToAnonymousRecord">
<source>Convert to Anonymous Record</source>
<target state="translated">Convertir en enregistrement anonyme</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.it.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Aggiungere l'annotazione di tipo</target>
<note />
</trans-unit>
<trans-unit id="ConvertCSharpUsingToFSharpOpen">
<source>Convert C# 'using' to F# 'open'</source>
<target state="new">Convert C# 'using' to F# 'open'</target>
<note />
</trans-unit>
<trans-unit id="ConvertToAnonymousRecord">
<source>Convert to Anonymous Record</source>
<target state="translated">Converti in record anonimo</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ja.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">型の注釈の追加</target>
<note />
</trans-unit>
<trans-unit id="ConvertCSharpUsingToFSharpOpen">
<source>Convert C# 'using' to F# 'open'</source>
<target state="new">Convert C# 'using' to F# 'open'</target>
<note />
</trans-unit>
<trans-unit id="ConvertToAnonymousRecord">
<source>Convert to Anonymous Record</source>
<target state="translated">匿名レコードに変換</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ko.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">형식 주석 추가</target>
<note />
</trans-unit>
<trans-unit id="ConvertCSharpUsingToFSharpOpen">
<source>Convert C# 'using' to F# 'open'</source>
<target state="new">Convert C# 'using' to F# 'open'</target>
<note />
</trans-unit>
<trans-unit id="ConvertToAnonymousRecord">
<source>Convert to Anonymous Record</source>
<target state="translated">익명 레코드로 변환</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pl.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Dodaj adnotację typu</target>
<note />
</trans-unit>
<trans-unit id="ConvertCSharpUsingToFSharpOpen">
<source>Convert C# 'using' to F# 'open'</source>
<target state="new">Convert C# 'using' to F# 'open'</target>
<note />
</trans-unit>
<trans-unit id="ConvertToAnonymousRecord">
<source>Convert to Anonymous Record</source>
<target state="translated">Konwertuj na rekord anonimowy</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.pt-BR.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Adicionar uma anotação de tipo</target>
<note />
</trans-unit>
<trans-unit id="ConvertCSharpUsingToFSharpOpen">
<source>Convert C# 'using' to F# 'open'</source>
<target state="new">Convert C# 'using' to F# 'open'</target>
<note />
</trans-unit>
<trans-unit id="ConvertToAnonymousRecord">
<source>Convert to Anonymous Record</source>
<target state="translated">Converter em Registro Anônimo</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.ru.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Добавить заметку типа</target>
<note />
</trans-unit>
<trans-unit id="ConvertCSharpUsingToFSharpOpen">
<source>Convert C# 'using' to F# 'open'</source>
<target state="new">Convert C# 'using' to F# 'open'</target>
<note />
</trans-unit>
<trans-unit id="ConvertToAnonymousRecord">
<source>Convert to Anonymous Record</source>
<target state="translated">Преобразовать в анонимную запись</target>
Expand Down
5 changes: 5 additions & 0 deletions vsintegration/src/FSharp.Editor/xlf/FSharp.Editor.tr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Tür ek açıklaması ekle</target>
<note />
</trans-unit>
<trans-unit id="ConvertCSharpUsingToFSharpOpen">
<source>Convert C# 'using' to F# 'open'</source>
<target state="new">Convert C# 'using' to F# 'open'</target>
<note />
</trans-unit>
<trans-unit id="ConvertToAnonymousRecord">
<source>Convert to Anonymous Record</source>
<target state="translated">Anonim Kayda Dönüştür</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">添加类型注释</target>
<note />
</trans-unit>
<trans-unit id="ConvertCSharpUsingToFSharpOpen">
<source>Convert C# 'using' to F# 'open'</source>
<target state="new">Convert C# 'using' to F# 'open'</target>
<note />
</trans-unit>
<trans-unit id="ConvertToAnonymousRecord">
<source>Convert to Anonymous Record</source>
<target state="translated">转换为匿名记录</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">新增型別註解</target>
<note />
</trans-unit>
<trans-unit id="ConvertCSharpUsingToFSharpOpen">
<source>Convert C# 'using' to F# 'open'</source>
<target state="new">Convert C# 'using' to F# 'open'</target>
<note />
</trans-unit>
<trans-unit id="ConvertToAnonymousRecord">
<source>Convert to Anonymous Record</source>
<target state="translated">轉換為匿名記錄</target>
Expand Down