diff --git a/FsAutoComplete/CommandResponse.fs b/FsAutoComplete/CommandResponse.fs index e77e89f10..28e463009 100644 --- a/FsAutoComplete/CommandResponse.fs +++ b/FsAutoComplete/CommandResponse.fs @@ -218,11 +218,11 @@ module CommandResponse = let (glyph, glyphChar) = CompletionUtils.getIcon d.Glyph yield { Name = d.Name; Glyph = glyph; GlyphChar = glyphChar } ] } - let symbolUse(symboluses: FSharpSymbolUse[]) = + let symbolUse(symbol: FSharpSymbolUse, uses: FSharpSymbolUse[]) = let su = - { Name = symboluses.[0].Symbol.DisplayName + { Name = symbol.Symbol.DisplayName Uses = - [ for su in symboluses do + [ for su in uses do yield { StartLine = su.RangeAlternate.StartLine StartColumn = su.RangeAlternate.StartColumn + 1 EndLine = su.RangeAlternate.EndLine diff --git a/FsAutoComplete/CompilerServiceInterface.fs b/FsAutoComplete/CompilerServiceInterface.fs index 2607b821a..edbc5b781 100644 --- a/FsAutoComplete/CompilerServiceInterface.fs +++ b/FsAutoComplete/CompilerServiceInterface.fs @@ -82,7 +82,7 @@ type ParseAndCheckResults(parseResults: FSharpParseFileResults, | Some symboluse -> let! symboluses = checkResults.GetUsesOfSymbolInFile symboluse.Symbol - return Success symboluses } + return Success (symboluse, symboluses) } |> Async.RunSynchronously member x.TryGetCompletions line col lineStr timeout filter = diff --git a/FsAutoComplete/Program.fs b/FsAutoComplete/Program.fs index 070477320..f3629a15f 100644 --- a/FsAutoComplete/Program.fs +++ b/FsAutoComplete/Program.fs @@ -80,160 +80,166 @@ module internal Main = let rec main (state:State) : int = currentFiles <- state.Files - match CommandInput.parseCommand(Console.ReadLine()) with - | Parse(file,kind) -> - let parse fileName text options = - let task = - async { - let! _parseResults, checkResults = checker.ParseAndCheckFileInProject(fileName, 0, text, options) - match checkResults with - | FSharpCheckFileAnswer.Aborted -> () - | FSharpCheckFileAnswer.Succeeded results -> Response.errors(results.Errors) - } - match kind with - | Synchronous -> Response.info "Synchronous parsing started" - Async.RunSynchronously task - | Normal -> Response.info "Background parsing started" - Async.StartImmediate task - - let file = Path.GetFullPath file - let lines = CommandInput.readInput [] |> Array.ofList - - let text = String.concat "\n" lines - - if Utils.isAScript file then - let checkOptions = checker.GetProjectOptionsFromScript(file, text) - let state = state.WithFileTextAndCheckerOptions(file, lines, checkOptions) - parse file text checkOptions - main state - else - let state, checkOptions = state.WithFileTextGetCheckerOptions(file, lines) - parse file text checkOptions - main state - - | Project file -> - let file = Path.GetFullPath file - match checker.TryGetProjectOptions(file) with - | Result.Failure s -> - Response.error(s) + try + match CommandInput.parseCommand(Console.ReadLine()) with + | Parse(file,kind) -> + let parse fileName text options = + let task = + async { + let! _parseResults, checkResults = checker.ParseAndCheckFileInProject(fileName, 0, text, options) + match checkResults with + | FSharpCheckFileAnswer.Aborted -> () + | FSharpCheckFileAnswer.Succeeded results -> Response.errors(results.Errors) + } + match kind with + | Synchronous -> Response.info "Synchronous parsing started" + Async.RunSynchronously task + | Normal -> Response.info "Background parsing started" + Async.StartImmediate task + + let file = Path.GetFullPath file + let lines = CommandInput.readInput [] |> Array.ofList + + let text = String.concat "\n" lines + + if Utils.isAScript file then + let checkOptions = checker.GetProjectOptionsFromScript(file, text) + let state = state.WithFileTextAndCheckerOptions(file, lines, checkOptions) + parse file text checkOptions + main state + else + let state, checkOptions = state.WithFileTextGetCheckerOptions(file, lines) + parse file text checkOptions main state - | Result.Success(po, projectFiles, outFileOpt, references, frameworkOpt) -> - Response.project(file, projectFiles, outFileOpt, references, frameworkOpt) - - let projects = - projectFiles - |> List.fold (fun s f -> Map.add f po s) state.FileCheckOptions - main { state with FileCheckOptions = projects } - - | Declarations file -> - let file = Path.GetFullPath file - match state.TryGetFileCheckerOptionsWithSource(file) with - | Failure s -> Response.error(s) - | Success (checkOptions, source) -> - let decls = checker.GetDeclarations(file, source, checkOptions) - Response.declarations(decls) - - main state - - | HelpText sym -> - match Map.tryFind sym state.HelpText with - | None -> Response.error (sprintf "No help text available for symbol '%s'" sym) - | Some tip -> Response.helpText(sym, tip) - - main state - - | PosCommand(cmd, file, line, col, timeout, filter) -> - let file = Path.GetFullPath file - match state.TryGetFileCheckerOptionsWithLinesAndLineStr(file, line, col) with - | Failure s -> Response.error(s) - main state - | Success (options, lines, lineStr) -> - // TODO: Should sometimes pass options.Source in here to force a reparse - // for completions e.g. `(some typed expr).$` - let tyResOpt = checker.TryGetRecentTypeCheckResultsForFile(file, options) - match tyResOpt with - | None -> Response.error "Cached typecheck results not yet available"; main state - | Some tyRes -> - - match cmd with - | Completion -> - match tyRes.TryGetCompletions line col lineStr timeout filter with - | Some (decls, residue) -> - let declName (d: FSharpDeclarationListItem) = d.Name - - // Send the first helptext without being requested. - // This allows it to be displayed immediately in the editor. - let firstMatchOpt = - Array.sortBy declName decls - |> Array.tryFind (fun d -> (declName d).StartsWith residue) - match firstMatchOpt with - | None -> () - | Some d -> Response.helpText(d.Name, d.DescriptionText) - - Response.completion(decls) - - let helptext = - Seq.fold (fun m d -> Map.add (declName d) d.DescriptionText m) Map.empty decls - main { state with HelpText = helptext } - - | None -> - Response.error "Timed out while fetching completions" - main state - - | ToolTip -> - // A failure is only info here, as this command is expected to be - // used 'on idle', and frequent errors are expected. - match tyRes.TryGetToolTip line col lineStr with - | Result.Failure s -> Response.info(s) - | Result.Success tip -> Response.toolTip(tip) - + | Project file -> + let file = Path.GetFullPath file + match checker.TryGetProjectOptions(file) with + | Result.Failure s -> + Response.error(s) main state - | SymbolUse -> - // A failure is only info here, as this command is expected to be - // used 'on idle', and frequent errors are expected. - match tyRes.TryGetSymbolUse line col lineStr with - | Result.Failure s -> Response.info(s) - | Result.Success su -> Response.symbolUse(su) + | Result.Success(po, projectFiles, outFileOpt, references, frameworkOpt) -> + Response.project(file, projectFiles, outFileOpt, references, frameworkOpt) - main state + let projects = + projectFiles + |> List.fold (fun s f -> Map.add f po s) state.FileCheckOptions + main { state with FileCheckOptions = projects } - | FindDeclaration -> - match tyRes.TryFindDeclaration line col lineStr with - | Result.Failure s -> Response.error s - | Result.Success range -> Response.findDeclaration(range) + | Declarations file -> + let file = Path.GetFullPath file + match state.TryGetFileCheckerOptionsWithSource(file) with + | Failure s -> Response.error(s) + | Success (checkOptions, source) -> + let decls = checker.GetDeclarations(file, source, checkOptions) + Response.declarations(decls) - main state + main state - | Methods -> - match tyRes.TryGetMethodOverrides lines line col with - | Result.Failure s -> Response.error s - | Result.Success (meth, commas) -> Response.methods(meth, commas) + | HelpText sym -> + match Map.tryFind sym state.HelpText with + | None -> Response.error (sprintf "No help text available for symbol '%s'" sym) + | Some tip -> Response.helpText(sym, tip) - main state + main state - | CompilerLocation -> - let locopt = FSharpEnvironment.BinFolderOfDefaultFSharpCompiler None - match locopt with - | None -> Response.error "Could not find compiler" - | Some loc -> Response.message("compilerlocation", loc) + | PosCommand(cmd, file, line, col, timeout, filter) -> + let file = Path.GetFullPath file + match state.TryGetFileCheckerOptionsWithLinesAndLineStr(file, line, col) with + | Failure s -> Response.error(s) + main state + | Success (options, lines, lineStr) -> + // TODO: Should sometimes pass options.Source in here to force a reparse + // for completions e.g. `(some typed expr).$` + let tyResOpt = checker.TryGetRecentTypeCheckResultsForFile(file, options) + match tyResOpt with + | None -> Response.error "Cached typecheck results not yet available"; main state + | Some tyRes -> + + match cmd with + | Completion -> + match tyRes.TryGetCompletions line col lineStr timeout filter with + | Some (decls, residue) -> + let declName (d: FSharpDeclarationListItem) = d.Name + + // Send the first helptext without being requested. + // This allows it to be displayed immediately in the editor. + let firstMatchOpt = + Array.sortBy declName decls + |> Array.tryFind (fun d -> (declName d).StartsWith residue) + match firstMatchOpt with + | None -> () + | Some d -> Response.helpText(d.Name, d.DescriptionText) + + Response.completion(decls) + + let helptext = + Seq.fold (fun m d -> Map.add (declName d) d.DescriptionText m) Map.empty decls + main { state with HelpText = helptext } + + | None -> + Response.error "Timed out while fetching completions" + main state + + | ToolTip -> + // A failure is only info here, as this command is expected to be + // used 'on idle', and frequent errors are expected. + match tyRes.TryGetToolTip line col lineStr with + | Result.Failure s -> Response.info(s) + | Result.Success tip -> Response.toolTip(tip) + + main state + + | SymbolUse -> + // A failure is only info here, as this command is expected to be + // used 'on idle', and frequent errors are expected. + match tyRes.TryGetSymbolUse line col lineStr with + | Result.Failure s -> Response.info(s) + | Result.Success (sym,usages) -> Response.symbolUse(sym,usages) + + main state + + | FindDeclaration -> + match tyRes.TryFindDeclaration line col lineStr with + | Result.Failure s -> Response.error s + | Result.Success range -> Response.findDeclaration(range) + + main state + + | Methods -> + match tyRes.TryGetMethodOverrides lines line col with + | Result.Failure s -> Response.error s + | Result.Success (meth, commas) -> Response.methods(meth, commas) + + main state + + | CompilerLocation -> + let locopt = FSharpEnvironment.BinFolderOfDefaultFSharpCompiler None + match locopt with + | None -> Response.error "Could not find compiler" + | Some loc -> Response.message("compilerlocation", loc) - main state + main state - | Error(msg) -> - Response.error msg - main state + | Error(msg) -> + Response.error msg + main state - | Quit -> - (!Debug.output).Close () - 0 + | Quit -> + (!Debug.output).Close () + 0 + + with e -> + let msg = "Unexpected internal error. Please report at\ + https://github.com/fsharp/FsAutoComplete/issues,\ + attaching the following stack trace:\n" + + e.Message + e.StackTrace + Response.error msg + main state [] let entry args = - // System.Diagnostics.Debug.Listeners.Add( - // new System.Diagnostics.TextWriterTraceListener(Console.Out)) - // |> ignore let extra = Options.p.Parse args if extra.Count <> 0 then printfn "Unrecognised arguments: %s" (String.concat "," extra) diff --git a/FsAutoComplete/test/integration/SymbolUse/Script.fsx b/FsAutoComplete/test/integration/SymbolUse/Script.fsx index eea456da4..9470cf71b 100644 --- a/FsAutoComplete/test/integration/SymbolUse/Script.fsx +++ b/FsAutoComplete/test/integration/SymbolUse/Script.fsx @@ -1,6 +1,8 @@ +module console -module XA = - let funky x = x + 1 +[] +let main argv = + System.IO.Directory -let val99 = XA.funky 21 + 0 // return an integer exit code diff --git a/FsAutoComplete/test/integration/SymbolUse/SymbolUseRunner.fsx b/FsAutoComplete/test/integration/SymbolUse/SymbolUseRunner.fsx index 2f482452f..196cb18bd 100644 --- a/FsAutoComplete/test/integration/SymbolUse/SymbolUseRunner.fsx +++ b/FsAutoComplete/test/integration/SymbolUse/SymbolUseRunner.fsx @@ -23,6 +23,7 @@ p.symboluse "Program.fs" 14 12 //testval shadowed p.symboluse "Program.fs" 12 4 //miss p.symboluse "Program.fs" 12 5 //shadowed start p.symboluse "Program.fs" 12 13 //shadowed end +p.symboluse "Script.fsx" 6 18 // no uses due to compile error Threading.Thread.Sleep(1000) p.send "quit\n" diff --git a/FsAutoComplete/test/integration/SymbolUse/output.json b/FsAutoComplete/test/integration/SymbolUse/output.json index 94aa3cfc8..65b621240 100644 --- a/FsAutoComplete/test/integration/SymbolUse/output.json +++ b/FsAutoComplete/test/integration/SymbolUse/output.json @@ -30,7 +30,18 @@ } { "Kind": "errors", - "Data": [] + "Data": [ + { + "FileName": "/test/integration/SymbolUse/Script.fsx", + "StartLine": 6, + "EndLine": 6, + "StartColumn": 15, + "EndColumn": 24, + "Severity": "Error", + "Message": "The value, constructor, namespace or type 'Directory' is not defined", + "Subcategory": "typecheck" + } + ] } { "Kind": "info", @@ -167,3 +178,10 @@ ] } } +{ + "Kind": "symboluse", + "Data": { + "Name": "Directory", + "Uses": [] + } +}