Skip to content
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
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* Allow `_` in `use!` bindings values (lift FS1228 restriction) ([PR #18487](https://github.com/dotnet/fsharp/pull/18487))
* Make `[<CallerMemberName; Struct>]` combination work([PR #18444](https://github.com/dotnet/fsharp/pull/18444/))
* Fix code completion considers types from own namespace non-imported ([PR #18518](https://github.com/dotnet/fsharp/issues/18518))
* Code completion: fix getting qualifier expression in do statements in type decls ([PR #18524](https://github.com/dotnet/fsharp/pull/18524))

### Added
* Added missing type constraints in FCS. ([PR #18241](https://github.com/dotnet/fsharp/pull/18241))
Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/Service/ServiceParseTreeWalk.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,8 @@ module SyntaxTraversal =
let path = SyntaxNode.SynBinding b :: origPath

match b with
| SynBinding(kind = SynBindingKind.Do; expr = expr) -> traverseSynExpr path expr

| SynBinding(headPat = synPat; expr = synExpr; attributes = attributes; range = m) ->
[
yield! attributeApplicationDives path attributes
Expand Down
10 changes: 10 additions & 0 deletions tests/FSharp.Compiler.Service.Tests/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,16 @@ let rec allSymbolsInEntities compGen (entities: IList<FSharpEntity>) =
yield! allSymbolsInEntities compGen entity.NestedEntities ]


let getCursorPosAndPrepareSource (source: string) : string * string * pos =
let lines = source.Split([|"\r\n"; "\n"|], StringSplitOptions.None)
let line = lines |> Seq.findIndex _.Contains("{caret}")
let lineText = lines[line]
let column = lineText.IndexOf("{caret}")

let source = source.Replace("{caret}", "")
let lineText = lineText.Replace("{caret}", "")
source, lineText, Position.mkPos (line + 1) (column - 1)

let getParseResults (source: string) =
parseSourceCode("Test.fsx", source)

Expand Down
113 changes: 79 additions & 34 deletions tests/FSharp.Compiler.Service.Tests/CompletionTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ open FSharp.Compiler.Service.Tests.Common
open FSharp.Compiler.EditorServices
open Xunit

let getCompletionInfo lineText (line, column) source =
let getCompletionInfo source =
let source, lineText, pos = getCursorPosAndPrepareSource source
let parseResults, checkResults = getParseAndCheckResultsPreview source
let plid = QuickParse.GetPartialLongNameEx(lineText, column)
checkResults.GetDeclarationListInfo(Some parseResults, line, lineText, plid)
let plid = QuickParse.GetPartialLongNameEx(lineText, pos.Column)
checkResults.GetDeclarationListInfo(Some parseResults, pos.Line, lineText, plid)

let getCompletionItemNames (completionInfo: DeclarationListInfo) =
completionInfo.Items |> Array.map (fun item -> item.NameInCode)
Expand All @@ -19,92 +20,100 @@ let assertHasItemWithNames names (completionInfo: DeclarationListInfo) =
Assert.True(Set.contains name itemNames, $"{name} not found in {itemNames}")

[<Fact>]
let ``Expr - After record decl`` () =
let info = getCompletionInfo "{ Fi }" (4, 0) """
let ``Expr - After record decl 01`` () =
let info = getCompletionInfo """
type Record = { Field: int }

{ Fi{caret} }
"""
assertHasItemWithNames ["ignore"] info

[<Fact>]
let ``Expr - After record decl 02`` () =
let info = getCompletionInfo """
type Record = { Field: int }

{caret}
"""
assertHasItemWithNames ["ignore"] info

[<Fact>]
let ``Expr - record - field 01 - anon module`` () =
let info = getCompletionInfo "{ Fi }" (4, 3) """
let info = getCompletionInfo """
type Record = { Field: int }

{ Fi }
{ Fi{caret} }
"""
assertHasItemWithNames ["Field"] info

[<Fact>]
let ``Expr - record - field 02 - anon module`` () =
let info = getCompletionInfo "{ Fi }" (6, 3) """
let info = getCompletionInfo """
type Record = { Field: int }

let record = { Field = 1 }

{ Fi }
{ Fi{caret} }
"""
assertHasItemWithNames ["Field"] info

[<Fact>]
let ``Expr - record - empty 01`` () =
let info = getCompletionInfo "{ }" (4, 2) """
let info = getCompletionInfo """
type Record = { Field: int }

{ }
{ {caret} }
"""
assertHasItemWithNames ["Field"] info

[<Fact>]
let ``Expr - record - empty 02`` () =
let info = getCompletionInfo "{ }" (6, 2) """
let info = getCompletionInfo """
type Record = { Field: int }

let record = { Field = 1 }

{ }
{ {caret} }
"""
assertHasItemWithNames ["Field"; "record"] info

[<Fact>]
let ``Underscore dot lambda - completion`` () =
let info = getCompletionInfo " |> _.Len" (4, 11) """
let info = getCompletionInfo """
let myFancyFunc (x:string) =
x
|> _.Len"""
|> _.Len{caret}"""
assertHasItemWithNames ["Length"] info

[<Fact>]
let ``Underscore dot lambda - method completion`` () =
let info = getCompletionInfo " |> _.ToL" (4, 11) """
let info = getCompletionInfo """
let myFancyFunc (x:string) =
x
|> _.ToL"""
|> _.ToL{caret}"""
assertHasItemWithNames ["ToLower"] info

[<Fact>]
let ``Underscore dot lambda - No prefix`` () =
let info = getCompletionInfo "[s] |> List.map _. " (3, 18) """
let info = getCompletionInfo """
let s = ""
[s] |> List.map _.
[s] |> List.map _.{caret}
"""
assertHasItemWithNames ["Length"] info

[<Fact>]
let ``Type decl - Record - Field type 01`` () =
let info = getCompletionInfo "type Record = { Field: }" (2, 23) """
type Record = { Field: }
let info = getCompletionInfo """
type Record = { Field: {caret} }
"""
assertHasItemWithNames ["string"] info


[<Fact>]
let ``Expr - Qualifier 01`` () =
let info =
getCompletionInfo "s.Trim(). " (3, 13) """
let info = getCompletionInfo """
let f (s: string) =
s.Trim().
s.Trim().{caret}
s.Trim()
s.Trim()
()
Expand All @@ -114,10 +123,10 @@ let f (s: string) =
[<Fact>]
let ``Expr - Qualifier 02`` () =
let info =
getCompletionInfo "s.Trim(). " (4, 13) """
getCompletionInfo """
let f (s: string) =
s.Trim()
s.Trim().
s.Trim().{caret}
s.Trim()
()
"""
Expand All @@ -126,20 +135,56 @@ let f (s: string) =
[<Fact>]
let ``Expr - Qualifier 03`` () =
let info =
getCompletionInfo "s.Trim(). " (5, 13) """
getCompletionInfo """
let f (s: string) =
s.Trim()
s.Trim()
s.Trim().
s.Trim().{caret}
()
"""
assertHasItemWithNames ["Length"] info

[<Fact>]
let ``Expr - Qualifier 04`` () =
let info =
getCompletionInfo """
type T() =
do
System.String.Empty.ToString().L{caret}
"""
assertHasItemWithNames ["Length"] info

[<Fact>]
let ``Expr - Qualifier 05`` () =
let info =
getCompletionInfo """
System.String.Empty.ToString().{caret}
"""
assertHasItemWithNames ["Length"] info

[<Fact>]
let ``Expr - Qualifier 06`` () =
let info =
getCompletionInfo """
System.String.Empty.ToString().L{caret}
"""
assertHasItemWithNames ["Length"] info

[<Fact>]
let ``Expr - Qualifier 07`` () =
let info =
getCompletionInfo """
type T() =
do
System.String.Empty.ToString().L{caret}
()
"""
assertHasItemWithNames ["Length"] info

[<Fact>]
let ``Import - Ns 01`` () =
let info =
getCompletionInfo "let _: R " (14, 12) """
getCompletionInfo """
namespace Ns

type Rec1 = { F: int }
Expand All @@ -153,14 +198,14 @@ module M =

type Rec3 = { F: int }

let _: R = ()
let _: R{caret} = ()
"""
assertHasItemWithNames ["Rec1"; "Rec2"; "Rec3"] info

[<Fact>]
let ``Import - Ns 02 - Rec`` () =
let info =
getCompletionInfo "let _: R " (14, 12) """
getCompletionInfo """
namespace Ns

type Rec1 = { F: int }
Expand All @@ -174,14 +219,14 @@ module M =

type Rec3 = { F: int }

let _: R = ()
let _: R{caret} = ()
"""
assertHasItemWithNames ["Rec1"; "Rec2"; "Rec3"] info

[<Fact>]
let ``Import - Ns 03 - Rec`` () =
let info =
getCompletionInfo "let _: R " (14, 12) """
getCompletionInfo """
namespace Ns

type Rec1 = { F: int }
Expand All @@ -195,6 +240,6 @@ module rec M =

type Rec3 = { F: int }

let _: R = ()
let _: R{caret} = ()
"""
assertHasItemWithNames ["Rec1"; "Rec2"; "Rec3"] info