diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index cc037aad4..d78473731 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -64,6 +64,27 @@ type ProjectResponse = Framework: string } +type OverloadParameter = + { + Name : string + CanonicalTypeTextForSorting : string + Display : string + Description : string + } +type Overload = + { + Tip : string + TypeText : string + Parameters : OverloadParameter list + IsStaticArguments : bool + } +type MethodResponse = + { + Name : string + CurrentParameter : int + Overloads : Overload list + } + type FSharpErrorSeverityConverter() = inherit JsonConverter() @@ -127,6 +148,8 @@ module internal CommandInput = - get tool tip for the specified location finddecl """" [timeout] - find the point of declaration of the symbol at specified location + methods """" [timeout] + - find the method signatures at specified location project """" - associates the current session with the specified project outputmode {json,text} @@ -164,6 +187,7 @@ module internal CommandInput = // The types of commands that need position information type PosCommand = | Completion + | Methods | ToolTip | FindDeclaration @@ -237,6 +261,7 @@ module internal CommandInput = let completionTipOrDecl = parser { let! f = (string "completion " |> Parser.map (fun _ -> Completion)) <|> (string "tooltip " |> Parser.map (fun _ -> ToolTip)) <|> + (string "methods " |> Parser.map (fun _ -> Methods)) <|> (string "finddecl " |> Parser.map (fun _ -> FindDeclaration)) let! _ = char '"' let! filename = some (sat ((<>) '"')) |> Parser.map String.ofSeq @@ -472,7 +497,7 @@ module internal Main = Data = { Project = file Files = files Output = targetFilename - References = List.sort p.References + References = List.sortBy Path.GetFileName p.References Framework = framework } } let projects = files @@ -607,6 +632,68 @@ module internal Main = main state + + | Methods -> + // Find the starting point, ideally right after the first '(' + let lineCutoff = line - 3 + let commas, line, col = + let rec prevPos (line,col) = + match line, col with + | 1, 1 + | _ when line < lineCutoff -> 1, 1 + | _, 1 -> + let prevLine = state.Files.[file].Lines.[line - 2] + if prevLine.Length = 0 then prevPos(line-1, 1) + else line - 1, prevLine.Length + | _ -> line, col - 1 + + let rec loop commas depth (line, col) = + if (line,col) <= (1,1) then (0, line, col) else + let ch = state.Files.[file].Lines.[line - 1].[col - 1] + let commas = if depth = 0 && ch = ',' then commas + 1 else commas + if (ch = '(' || ch = '{' || ch = '[') && depth > 0 then loop commas (depth - 1) (prevPos (line,col)) + elif ch = ')' || ch = '}' || ch = ']' then loop commas (depth + 1) (prevPos (line,col)) + elif ch = '(' || ch = '<' then commas, line, col + else loop commas depth (prevPos (line,col)) + match loop 0 0 (prevPos(line,col)) with + | _, 1, 1 -> 0, line, col + | newPos -> newPos + + let meth = tyRes.GetMethods(line, col, state.Files.[file].Lines.[line - 1]) + |> Async.RunSynchronously + match meth with + | Some (name,overloads) when overloads.Length > 0 -> + match state.OutputMode with + | Text -> + printMsg "ERROR" "methods not supported in text mode" + | Json -> + prAsJson + { Kind = "method" + Data = { Name = name + CurrentParameter = commas + Overloads = + [ for o in overloads do + let tip = TipFormatter.formatTip o.Description + yield { + Tip = tip + TypeText = o.TypeText + Parameters = + [ for p in o.Parameters do + yield { + Name = p.ParameterName + CanonicalTypeTextForSorting = p.CanonicalTypeTextForSorting + Display = p.Display + Description = p.Description + } + ] + IsStaticArguments = o.IsStaticArguments + } + + ] } } + | _ -> printMsg "ERROR" "Could not find method" + + main state + else main state diff --git a/FSharp.AutoComplete/test/integration/ParamCompletion/FileTwo.fs b/FSharp.AutoComplete/test/integration/ParamCompletion/FileTwo.fs new file mode 100644 index 000000000..37e1138b4 --- /dev/null +++ b/FSharp.AutoComplete/test/integration/ParamCompletion/FileTwo.fs @@ -0,0 +1,16 @@ +module FileTwo + +type Foo = + | Bar + | Qux + +let addition x y = x + y + +let add x y = x + y + +type NewObjectType() = + + member x.Terrific (y : int, z : char) : int = y + member x.Terrific (y : int, z : System.DateTime) : int = y + member x.Terrific (y : Set<'a>, z : int) : Set<'a> = y + diff --git a/FSharp.AutoComplete/test/integration/ParamCompletion/ParamCompletionRunner.fsx b/FSharp.AutoComplete/test/integration/ParamCompletion/ParamCompletionRunner.fsx new file mode 100644 index 000000000..272996a88 --- /dev/null +++ b/FSharp.AutoComplete/test/integration/ParamCompletion/ParamCompletionRunner.fsx @@ -0,0 +1,30 @@ +#load "../TestHelpers.fsx" +open TestHelpers +open System.IO +open System + +(* + * This test is a simple sanity check of a basic run of the program. + * A few completions, files and script. + *) + +Environment.CurrentDirectory <- __SOURCE_DIRECTORY__ +File.Delete "output.txt" + +let p = new FSharpAutoCompleteWrapper() + +p.send "outputmode json\n" +p.project "Test1.fsproj" +p.parse "FileTwo.fs" +p.parse "Program.fs" +Threading.Thread.Sleep(6000) +p.methods "Program.fs" 4 36 +p.methods "Program.fs" 4 37 +p.methods "Program.fs" 8 30 +p.methods "Program.fs" 8 35 +p.methods "Program.fs" 10 39 +p.methods "Program.fs" 14 3 +Threading.Thread.Sleep(1000) +p.send "quit\n" +p.finalOutput () +|> writeNormalizedOutput "output.json" diff --git a/FSharp.AutoComplete/test/integration/ParamCompletion/Program.fs b/FSharp.AutoComplete/test/integration/ParamCompletion/Program.fs new file mode 100644 index 000000000..633de4b84 --- /dev/null +++ b/FSharp.AutoComplete/test/integration/ParamCompletion/Program.fs @@ -0,0 +1,22 @@ +module X = + let func x = x + 1 + +let testval = FileTwo.NewObjectType() + +let val2 = X.func(2) + +let val3 = testval.Terrific(val2, 'c') + +let val4 = System.DateTime.Parse("hello") + +let v5 = System.DateTime.Parse( + + "hello" + + ) + + +[] +let main args = + printfn "Hello %d" val2 + 0 diff --git a/FSharp.AutoComplete/test/integration/ParamCompletion/Script.fsx b/FSharp.AutoComplete/test/integration/ParamCompletion/Script.fsx new file mode 100644 index 000000000..eea456da4 --- /dev/null +++ b/FSharp.AutoComplete/test/integration/ParamCompletion/Script.fsx @@ -0,0 +1,6 @@ + + +module XA = + let funky x = x + 1 + +let val99 = XA.funky 21 diff --git a/FSharp.AutoComplete/test/integration/ParamCompletion/Test1.fsproj b/FSharp.AutoComplete/test/integration/ParamCompletion/Test1.fsproj new file mode 100644 index 000000000..b4dda57b8 --- /dev/null +++ b/FSharp.AutoComplete/test/integration/ParamCompletion/Test1.fsproj @@ -0,0 +1,75 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {116CC2F9-F987-4B3D-915A-34CAC04A73DA} + Exe + Test1 + Test1 + Test1 + False + + + Program.fs + + + 4.3.0.0 + 11 + + + True + False + False + bin\Debug\ + DEBUG;TRACE + 3 + x86 + bin\Debug\Test1.XML + + + pdbonly + True + True + bin\Release\ + TRACE + 3 + x86 + bin\Release\Test1.XML + False + + + + + + + True + + + + + + + + + + $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets + + + + + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets + + + + + + \ No newline at end of file diff --git a/FSharp.AutoComplete/test/integration/ParamCompletion/Test1.sln b/FSharp.AutoComplete/test/integration/ParamCompletion/Test1.sln new file mode 100644 index 000000000..21d739f0c --- /dev/null +++ b/FSharp.AutoComplete/test/integration/ParamCompletion/Test1.sln @@ -0,0 +1,24 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{f2a71f9b-5d33-465a-a702-920d77279786}") = "Test1", "Test1.fsproj", "{116CC2F9-F987-4B3D-915A-34CAC04A73DA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3069EBCA-546F-4208-9019-7952B8978616}" + ProjectSection(SolutionItems) = preProject + ParamCompletionRunner.fsx = ParamCompletionRunner.fsx + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {116CC2F9-F987-4B3D-915A-34CAC04A73DA}.Debug|x86.ActiveCfg = Debug|x86 + {116CC2F9-F987-4B3D-915A-34CAC04A73DA}.Debug|x86.Build.0 = Debug|x86 + {116CC2F9-F987-4B3D-915A-34CAC04A73DA}.Release|x86.ActiveCfg = Release|x86 + {116CC2F9-F987-4B3D-915A-34CAC04A73DA}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + EndGlobalSection +EndGlobal diff --git a/FSharp.AutoComplete/test/integration/ParamCompletion/output.json b/FSharp.AutoComplete/test/integration/ParamCompletion/output.json new file mode 100644 index 000000000..9557bb10f --- /dev/null +++ b/FSharp.AutoComplete/test/integration/ParamCompletion/output.json @@ -0,0 +1,328 @@ +{ + "Kind": "project", + "Data": { + "Project": "/test/integration/ParamCompletion/Test1.fsproj", + "Files": [ + "/test/integration/ParamCompletion/FileTwo.fs", + "/test/integration/ParamCompletion/Program.fs" + ], + "Output": "/test/integration/ParamCompletion/bin/Debug/Test1.exe", + "References": [ + "/FSharp.Core.dll", + "/System.Core.dll", + "/System.dll", + "/mscorlib.dll" + ], + "Framework": "v4.0" + } +} +{ + "Kind": "INFO", + "Data": "Synchronous parsing started" +} +{ + "Kind": "errors", + "Data": [] +} +{ + "Kind": "INFO", + "Data": "Synchronous parsing started" +} +{ + "Kind": "errors", + "Data": [] +} +{ + "Kind": "method", + "Data": { + "Name": "NewObjectType", + "CurrentParameter": 0, + "Overloads": [ + { + "Tip": "new : unit -> FileTwo.NewObjectType", + "TypeText": ": FileTwo.NewObjectType", + "Parameters": [], + "IsStaticArguments": false + } + ] + } +} +{ + "Kind": "method", + "Data": { + "Name": "NewObjectType", + "CurrentParameter": 0, + "Overloads": [ + { + "Tip": "new : unit -> FileTwo.NewObjectType", + "TypeText": ": FileTwo.NewObjectType", + "Parameters": [], + "IsStaticArguments": false + } + ] + } +} +{ + "Kind": "method", + "Data": { + "Name": "Terrific", + "CurrentParameter": 0, + "Overloads": [ + { + "Tip": "member FileTwo.NewObjectType.Terrific : y:Set<'a> * z:int -> Set<'a> (requires comparison)", + "TypeText": ": Set<'a> (requires comparison)", + "Parameters": [ + { + "Name": "y", + "CanonicalTypeTextForSorting": "Microsoft.FSharp.Collections.Set<'a>", + "Display": "y: Set<'a>", + "Description": "" + }, + { + "Name": "z", + "CanonicalTypeTextForSorting": "System.Int32", + "Display": "z: int", + "Description": "" + } + ], + "IsStaticArguments": false + }, + { + "Tip": "member FileTwo.NewObjectType.Terrific : y:int * z:char -> int", + "TypeText": ": int", + "Parameters": [ + { + "Name": "y", + "CanonicalTypeTextForSorting": "System.Int32", + "Display": "y: int", + "Description": "" + }, + { + "Name": "z", + "CanonicalTypeTextForSorting": "System.Char", + "Display": "z: char", + "Description": "" + } + ], + "IsStaticArguments": false + }, + { + "Tip": "member FileTwo.NewObjectType.Terrific : y:int * z:System.DateTime -> int", + "TypeText": ": int", + "Parameters": [ + { + "Name": "y", + "CanonicalTypeTextForSorting": "System.Int32", + "Display": "y: int", + "Description": "" + }, + { + "Name": "z", + "CanonicalTypeTextForSorting": "System.DateTime", + "Display": "z: System.DateTime", + "Description": "" + } + ], + "IsStaticArguments": false + } + ] + } +} +{ + "Kind": "method", + "Data": { + "Name": "Terrific", + "CurrentParameter": 1, + "Overloads": [ + { + "Tip": "member FileTwo.NewObjectType.Terrific : y:Set<'a> * z:int -> Set<'a> (requires comparison)", + "TypeText": ": Set<'a> (requires comparison)", + "Parameters": [ + { + "Name": "y", + "CanonicalTypeTextForSorting": "Microsoft.FSharp.Collections.Set<'a>", + "Display": "y: Set<'a>", + "Description": "" + }, + { + "Name": "z", + "CanonicalTypeTextForSorting": "System.Int32", + "Display": "z: int", + "Description": "" + } + ], + "IsStaticArguments": false + }, + { + "Tip": "member FileTwo.NewObjectType.Terrific : y:int * z:char -> int", + "TypeText": ": int", + "Parameters": [ + { + "Name": "y", + "CanonicalTypeTextForSorting": "System.Int32", + "Display": "y: int", + "Description": "" + }, + { + "Name": "z", + "CanonicalTypeTextForSorting": "System.Char", + "Display": "z: char", + "Description": "" + } + ], + "IsStaticArguments": false + }, + { + "Tip": "member FileTwo.NewObjectType.Terrific : y:int * z:System.DateTime -> int", + "TypeText": ": int", + "Parameters": [ + { + "Name": "y", + "CanonicalTypeTextForSorting": "System.Int32", + "Display": "y: int", + "Description": "" + }, + { + "Name": "z", + "CanonicalTypeTextForSorting": "System.DateTime", + "Display": "z: System.DateTime", + "Description": "" + } + ], + "IsStaticArguments": false + } + ] + } +} +{ + "Kind": "method", + "Data": { + "Name": "Parse", + "CurrentParameter": 0, + "Overloads": [ + { + "Tip": "System.DateTime.Parse(s: string) : System.DateTime", + "TypeText": ": System.DateTime", + "Parameters": [ + { + "Name": "s", + "CanonicalTypeTextForSorting": "System.String", + "Display": "s: string", + "Description": "" + } + ], + "IsStaticArguments": false + }, + { + "Tip": "System.DateTime.Parse(s: string, provider: System.IFormatProvider) : System.DateTime", + "TypeText": ": System.DateTime", + "Parameters": [ + { + "Name": "s", + "CanonicalTypeTextForSorting": "System.String", + "Display": "s: string", + "Description": "" + }, + { + "Name": "provider", + "CanonicalTypeTextForSorting": "System.IFormatProvider", + "Display": "provider: System.IFormatProvider", + "Description": "" + } + ], + "IsStaticArguments": false + }, + { + "Tip": "System.DateTime.Parse(s: string, provider: System.IFormatProvider, styles: System.Globalization.DateTimeStyles) : System.DateTime", + "TypeText": ": System.DateTime", + "Parameters": [ + { + "Name": "s", + "CanonicalTypeTextForSorting": "System.String", + "Display": "s: string", + "Description": "" + }, + { + "Name": "provider", + "CanonicalTypeTextForSorting": "System.IFormatProvider", + "Display": "provider: System.IFormatProvider", + "Description": "" + }, + { + "Name": "styles", + "CanonicalTypeTextForSorting": "System.Globalization.DateTimeStyles", + "Display": "styles: System.Globalization.DateTimeStyles", + "Description": "" + } + ], + "IsStaticArguments": false + } + ] + } +} +{ + "Kind": "method", + "Data": { + "Name": "Parse", + "CurrentParameter": 0, + "Overloads": [ + { + "Tip": "System.DateTime.Parse(s: string) : System.DateTime", + "TypeText": ": System.DateTime", + "Parameters": [ + { + "Name": "s", + "CanonicalTypeTextForSorting": "System.String", + "Display": "s: string", + "Description": "" + } + ], + "IsStaticArguments": false + }, + { + "Tip": "System.DateTime.Parse(s: string, provider: System.IFormatProvider) : System.DateTime", + "TypeText": ": System.DateTime", + "Parameters": [ + { + "Name": "s", + "CanonicalTypeTextForSorting": "System.String", + "Display": "s: string", + "Description": "" + }, + { + "Name": "provider", + "CanonicalTypeTextForSorting": "System.IFormatProvider", + "Display": "provider: System.IFormatProvider", + "Description": "" + } + ], + "IsStaticArguments": false + }, + { + "Tip": "System.DateTime.Parse(s: string, provider: System.IFormatProvider, styles: System.Globalization.DateTimeStyles) : System.DateTime", + "TypeText": ": System.DateTime", + "Parameters": [ + { + "Name": "s", + "CanonicalTypeTextForSorting": "System.String", + "Display": "s: string", + "Description": "" + }, + { + "Name": "provider", + "CanonicalTypeTextForSorting": "System.IFormatProvider", + "Display": "provider: System.IFormatProvider", + "Description": "" + }, + { + "Name": "styles", + "CanonicalTypeTextForSorting": "System.Globalization.DateTimeStyles", + "Display": "styles: System.Globalization.DateTimeStyles", + "Description": "" + } + ], + "IsStaticArguments": false + } + ] + } +} diff --git a/FSharp.AutoComplete/test/integration/TestHelpers.fsx b/FSharp.AutoComplete/test/integration/TestHelpers.fsx index df687a8ab..99e20542b 100644 --- a/FSharp.AutoComplete/test/integration/TestHelpers.fsx +++ b/FSharp.AutoComplete/test/integration/TestHelpers.fsx @@ -34,6 +34,9 @@ type FSharpAutoCompleteWrapper() = member x.completion (fn: string) (line: int) (col: int) : unit = fprintf p.StandardInput "completion \"%s\" %d %d\n" fn line col + member x.methods (fn: string) (line: int) (col: int) : unit = + fprintf p.StandardInput "methods \"%s\" %d %d\n" fn line col + member x.completionFilter (fn: string) (line: int) (col: int) (filter: string) : unit = fprintf p.StandardInput "completion \"%s\" %d %d filter=%s\n" fn line col filter