Skip to content

Commit

Permalink
POC: update value in signature file.
Browse files Browse the repository at this point in the history
  • Loading branch information
nojaf committed Sep 5, 2023
1 parent daf3619 commit ffe2cca
Show file tree
Hide file tree
Showing 10 changed files with 473 additions and 342 deletions.
12 changes: 12 additions & 0 deletions src/FsAutoComplete/CodeFixes/ToInterpolatedString.fsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module FsAutoComplete.CodeFix.ToInterpolatedString

open FsAutoComplete.CodeFix.Types
open Ionide.LanguageServerProtocol.Types

val title: string

val fix:
getParseResultsForFile: GetParseResultsForFile ->
getLanguageVersion: GetLanguageVersion ->
codeActionParams: CodeActionParams ->
Async<Result<Fix list, string>>
81 changes: 81 additions & 0 deletions src/FsAutoComplete/CodeFixes/UpdateValueInSignatureFile.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
module FsAutoComplete.CodeFix.UpdateValueInSignatureFile

open FSharp.Compiler.Symbols
open FSharp.Compiler.Syntax
open FsToolkit.ErrorHandling
open Ionide.LanguageServerProtocol.Types
open FsAutoComplete.CodeFix.Types
open FsAutoComplete
open FsAutoComplete.LspHelpers

let visitSynModuleSigDecl (name: string) (decl: SynModuleSigDecl) =
match decl with
| SynModuleSigDecl.Val(valSig = SynValSig(ident = SynIdent(ident = ident)); range = m) when ident.idText = name ->
Some m
| _ -> None

let visitSynModuleOrNamespaceSig (name: string) (SynModuleOrNamespaceSig(decls = decls)) =
decls |> List.tryPick (visitSynModuleSigDecl name)

let visitParsedSigFileInput (name: string) (ParsedSigFileInput(contents = contents)) =
contents |> List.tryPick (visitSynModuleOrNamespaceSig name)

let visitTree (name: string) (tree: ParsedInput) =
match tree with
| ParsedInput.ImplFile _ -> None
| ParsedInput.SigFile parsedSigFileInput -> visitParsedSigFileInput name parsedSigFileInput

let title = "Update val in signature file"

let fix (getParseResultsForFile: GetParseResultsForFile) : CodeFix =
Run.ifDiagnosticByCode (Set.ofList [ "34" ]) (fun diagnostic codeActionParams ->
asyncResult {
let implFilePath = codeActionParams.TextDocument.GetFilePath()
let sigFilePath = $"%s{implFilePath}i"

let implFileName = Utils.normalizePath implFilePath
let sigFileName = Utils.normalizePath sigFilePath

let sigTextDocumentIdentifier: TextDocumentIdentifier =
{ Uri = $"%s{codeActionParams.TextDocument.Uri}i" }

let! (implParseAndCheckResults: ParseAndCheckResults, implLine: string, implSourceText: IFSACSourceText) =
getParseResultsForFile implFileName (protocolPosToPos diagnostic.Range.Start)

let! implBindingName =
implSourceText.GetText(protocolRangeToRange implParseAndCheckResults.GetParseResults.FileName diagnostic.Range)

let! (sigParseAndCheckResults: ParseAndCheckResults, _sigLine: string, _sigSourceText: IFSACSourceText) =
getParseResultsForFile sigFileName (protocolPosToPos diagnostic.Range.Start)

match visitTree implBindingName sigParseAndCheckResults.GetParseResults.ParseTree with
| None -> return []
| Some mVal ->
let endPos = protocolPosToPos diagnostic.Range.End

let symbolUse =
implParseAndCheckResults.GetCheckResults.GetSymbolUseAtLocation(
endPos.Line,
endPos.Column,
implLine,
[ implBindingName ]
)

match symbolUse with
| None -> return []
| Some symbolUse ->
match symbolUse.Symbol with
| :? FSharpMemberOrFunctionOrValue as mfv ->
match mfv.GetValSignatureText(symbolUse.DisplayContext, symbolUse.Range) with
| None -> return []
| Some valText ->
return
[ { SourceDiagnostic = None
Title = title
File = sigTextDocumentIdentifier
Edits =
[| { Range = fcsRangeToLsp mVal
NewText = valText } |]
Kind = FixKind.Fix } ]
| _ -> return []
})
6 changes: 6 additions & 0 deletions src/FsAutoComplete/CodeFixes/UpdateValueInSignatureFile.fsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module FsAutoComplete.CodeFix.UpdateValueInSignatureFile

open FsAutoComplete.CodeFix.Types

val title: string
val fix: getParseResultsForFile: GetParseResultsForFile -> CodeFix
3 changes: 2 additions & 1 deletion src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1835,7 +1835,8 @@ type AdaptiveFSharpLspServer
UseTripleQuotedInterpolation.fix tryGetParseResultsForFile getRangeText
RenameParamToMatchSignature.fix tryGetParseResultsForFile
RemovePatternArgument.fix tryGetParseResultsForFile
ToInterpolatedString.fix tryGetParseResultsForFile getLanguageVersion |])
ToInterpolatedString.fix tryGetParseResultsForFile getLanguageVersion
UpdateValueInSignatureFile.fix tryGetParseResultsForFile |])

let forgetDocument (uri: DocumentUri) =
async {
Expand Down
1 change: 1 addition & 0 deletions src/FsAutoComplete/LspServers/FsAutoComplete.Lsp.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,7 @@ type FSharpLspServer(state: State, lspClient: FSharpLspClient, sourceTextFactory
RenameParamToMatchSignature.fix tryGetParseResultsForFile
RemovePatternArgument.fix tryGetParseResultsForFile
ToInterpolatedString.fix tryGetParseResultsForFile tryGetLanguageVersion
UpdateValueInSignatureFile.fix tryGetParseResultsForFile

|]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,43 @@ open Utils.CursorbasedTests.CodeFix


let tests state =
let selectCodeFix expectedName = CodeFix.withTitle (RenameParamToMatchSignature.title expectedName)
let selectCodeFix expectedName =
CodeFix.withTitle (RenameParamToMatchSignature.title expectedName)

// requires `fsi` and corresponding `fs` file (and a project!)
// -> cannot use untitled doc
// -> use existing files, but load with text specified in tests
let path = Path.Combine(__SOURCE_DIRECTORY__, @"../TestCases/CodeFixTests/RenameParamToMatchSignature/")
let path =
Path.Combine(__SOURCE_DIRECTORY__, @"../TestCases/CodeFixTests/RenameParamToMatchSignature/")

let (fsiFile, fsFile) = ("Code.fsi", "Code.fs")

serverTestList (nameof RenameParamToMatchSignature) state defaultConfigDto (Some path) (fun server -> [
let checkWithFsi
fsiSource
fsSourceWithCursor
selectCodeFix
fsSourceExpected
= async {
let fsiSource = fsiSource |> Text.trimTripleQuotation
let (cursor, fsSource) =
fsSourceWithCursor
|> Text.trimTripleQuotation
|> Cursor.assertExtractRange
let! (fsiDoc, diags) = server |> Server.openDocumentWithText fsiFile fsiSource
use fsiDoc = fsiDoc
Expect.isEmpty diags "There should be no diagnostics in fsi doc"
let! (fsDoc, diags) = server |> Server.openDocumentWithText fsFile fsSource
use fsDoc = fsDoc

do!
checkFixAt
(fsDoc, diags)
(fsSource, cursor)
(Diagnostics.expectCode "3218")
selectCodeFix
(After (fsSourceExpected |> Text.trimTripleQuotation))
}

testCaseAsync "can rename parameter in F# function" <|
checkWithFsi
serverTestList (nameof RenameParamToMatchSignature) state defaultConfigDto (Some path) (fun server ->
[ let checkWithFsi fsiSource fsSourceWithCursor selectCodeFix fsSourceExpected =
async {
let fsiSource = fsiSource |> Text.trimTripleQuotation

let (cursor, fsSource) =
fsSourceWithCursor |> Text.trimTripleQuotation |> Cursor.assertExtractRange

let! (fsiDoc, diags) = server |> Server.openDocumentWithText fsiFile fsiSource
use fsiDoc = fsiDoc
Expect.isEmpty diags "There should be no diagnostics in fsi doc"
let! (fsDoc, diags) = server |> Server.openDocumentWithText fsFile fsSource
use fsDoc = fsDoc

do!
checkFixAt
(fsDoc, diags)
fsDoc.VersionedTextDocumentIdentifier
(fsSource, cursor)
(Diagnostics.expectCode "3218")
selectCodeFix
(After(fsSourceExpected |> Text.trimTripleQuotation))
}

testCaseAsync "can rename parameter in F# function"
<| checkWithFsi
"""
module Code
Expand All @@ -66,8 +66,9 @@ let tests state =
let f value1 = value1 + 1
"""
testCaseAsync "can rename parameter with backticks in signature in F# function" <|
checkWithFsi

testCaseAsync "can rename parameter with backticks in signature in F# function"
<| checkWithFsi
"""
module Code
Expand All @@ -84,8 +85,9 @@ let tests state =
let f ``my value2`` = ``my value2`` + 1
"""
testCaseAsync "can rename parameter with backticks in implementation in F# function" <|
checkWithFsi

testCaseAsync "can rename parameter with backticks in implementation in F# function"
<| checkWithFsi
"""
module Code
Expand All @@ -102,8 +104,9 @@ let tests state =
let f value3 = value3 + 1
"""
testCaseAsync "can rename all usage in F# function" <|
checkWithFsi

testCaseAsync "can rename all usage in F# function"
<| checkWithFsi
"""
module Code
Expand All @@ -128,8 +131,9 @@ let tests state =
let v = a + b
v + x * y
"""
testCaseAsync "can rename parameter with type in F# function" <|
checkWithFsi

testCaseAsync "can rename parameter with type in F# function"
<| checkWithFsi
"""
module Code
Expand All @@ -146,8 +150,9 @@ let tests state =
let f (value5: int) = value5 + 1
"""
testCaseAsync "can rename parameter in constructor" <|
checkWithFsi

testCaseAsync "can rename parameter in constructor"
<| checkWithFsi
"""
module Code
Expand All @@ -167,8 +172,9 @@ let tests state =
type T(value6: int) =
let _ = value6 + 3
"""
testCaseAsync "can rename parameter in member" <|
checkWithFsi

testCaseAsync "can rename parameter in member"
<| checkWithFsi
"""
module Code
Expand All @@ -189,8 +195,9 @@ let tests state =
type T() =
member _.F(value7) = value7 + 1
"""
testCaseAsync "can rename parameter with ' in signature in F# function" <|
checkWithFsi

testCaseAsync "can rename parameter with ' in signature in F# function"
<| checkWithFsi
"""
module Code
Expand All @@ -207,8 +214,9 @@ let tests state =
let f value8' = value8' + 1
"""
testCaseAsync "can rename parameter with ' in implementation in F# function" <|
checkWithFsi

testCaseAsync "can rename parameter with ' in implementation in F# function"
<| checkWithFsi
"""
module Code
Expand All @@ -225,8 +233,9 @@ let tests state =
let f value9 = value9 + 1
"""
testCaseAsync "can rename parameter with ' (not in last place) in signature in F# function" <|
checkWithFsi

testCaseAsync "can rename parameter with ' (not in last place) in signature in F# function"
<| checkWithFsi
"""
module Code
Expand All @@ -243,8 +252,9 @@ let tests state =
let f v10'2 = v10'2 + 1
"""
testCaseAsync "can rename parameter with ' (not in last place) in implementation in F# function" <|
checkWithFsi

testCaseAsync "can rename parameter with ' (not in last place) in implementation in F# function"
<| checkWithFsi
"""
module Code
Expand All @@ -261,8 +271,9 @@ let tests state =
let f value11 = value11 + 1
"""
testCaseAsync "can rename parameter with multiple ' in signature in F# function" <|
checkWithFsi

testCaseAsync "can rename parameter with multiple ' in signature in F# function"
<| checkWithFsi
"""
module Code
Expand All @@ -279,8 +290,9 @@ let tests state =
let f value12'v'2 = value12'v'2 + 1
"""
testCaseAsync "can rename parameter with multiple ' in implementation in F# function" <|
checkWithFsi

testCaseAsync "can rename parameter with multiple ' in implementation in F# function"
<| checkWithFsi
"""
module Code
Expand All @@ -297,8 +309,9 @@ let tests state =
let f value13 = value13 + 1
"""
itestCaseAsync "can handle `' and implementation '` in impl name" <|
checkWithFsi

itestCaseAsync "can handle `' and implementation '` in impl name"
<| checkWithFsi
"""
module Code
Expand All @@ -315,9 +328,9 @@ let tests state =
let f value14 = value14 + 1
"""
//ENHANCEMENT: correctly detect below. Currently: detects sig name `sig`
itestCaseAsync "can handle `' and implementation '` in sig name" <|
checkWithFsi
//ENHANCEMENT: correctly detect below. Currently: detects sig name `sig`
itestCaseAsync "can handle `' and implementation '` in sig name"
<| checkWithFsi
"""
module Code
Expand All @@ -333,5 +346,4 @@ let tests state =
module Code
let f ``sig' and implementation 'impl' do not match`` = ``sig' and implementation 'impl' do not match`` + 1
"""
])
""" ])
3 changes: 2 additions & 1 deletion test/FsAutoComplete.Tests.Lsp/CodeFixTests/Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3351,4 +3351,5 @@ let tests state =
useTripleQuotedInterpolationTests state
wrapExpressionInParenthesesTests state
removeRedundantAttributeSuffixTests state
removePatternArgumentTests state ]
removePatternArgumentTests state
UpdateValueInSignatureFileTests.tests state ]
Loading

0 comments on commit ffe2cca

Please sign in to comment.