From ec51d361c39e72633eea2e456e64c78f2cfaad14 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Thu, 17 Oct 2013 22:29:03 +0100 Subject: [PATCH 01/24] First cut of this --- FSharp.AutoComplete/Program.fs | 80 ++++++++++++++++----------------- emacs/fsharp-mode-completion.el | 32 +++++++------ 2 files changed, 58 insertions(+), 54 deletions(-) diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index 600ecbbc..5f43d9bb 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -30,13 +30,13 @@ type OutputMode = /// Represents information needed to call the F# IntelliSense service /// (including project/script options, file name and source) -type internal RequestOptions(opts, file, src, mode) = +type internal RequestOptions(opts, file, src, help) = member x.Options : CheckOptions = opts member x.FileName : string = file member x.Source : string = src - member x.OutputMode : OutputMode = mode + member x.HelpText : bool = help member x.WithSource(source) = - RequestOptions(opts, file, source, mode) + RequestOptions(opts, file, source, help) override x.ToString() = sprintf "FileName: '%s'\nSource length: '%d'\nOptions: %s, %A, %A, %b, %b" @@ -164,15 +164,14 @@ type internal IntelliSenseAgent() = UnresolvedReferences = None } // Print contents of check option for debugging purposes - Debug.print "Checkoptions: ProjectFileName: %s, ProjectFileNames: %A, ProjectOptions: %A, IsIncompleteTypeCheckEnvironment: %A, UseScriptResolutionRules: %A" - opts.ProjectFileName - opts.ProjectFileNames - opts.ProjectOptions - opts.IsIncompleteTypeCheckEnvironment - opts.UseScriptResolutionRules + // Debug.print "Checkoptions: ProjectFileName: %s, ProjectFileNames: %A, ProjectOptions: %A, IsIncompleteTypeCheckEnvironment: %A, UseScriptResolutionRules: %A" + // opts.ProjectFileName + // opts.ProjectFileNames + // opts.ProjectOptions + // opts.IsIncompleteTypeCheckEnvironment + // opts.UseScriptResolutionRules opts - /// Get errors from the last parse request member x.GetErrors() = agent.PostAndReply(GetErrors) @@ -220,16 +219,15 @@ type internal IntelliSenseAgent() = match decls with | Some decls -> printfn "DATA: completion" - match opts.OutputMode with - | Json -> + for d in decls.Items do Console.WriteLine(d.Name) + printfn "<>" + if opts.HelpText then + printfn "DATA: helptext" let cs = [ for d in decls.Items do - yield { Name = d.Name - Help = TipFormatter.formatTip d.DescriptionText } ] + yield TipFormatter.formatTip d.DescriptionText ] Console.WriteLine(JsonConvert.SerializeObject(cs)) - | Text -> - for d in decls.Items do Console.WriteLine(d.Name) - printfn "<>" + printfn "<>" | None -> printfn "ERROR: Could not get type information\n<>" @@ -334,9 +332,9 @@ module internal CommandInput = - find the point of declaration of the object at specified position project """" - associates the current session with the specified project - outputmode {json,text} - - switches the output format. 'json' offers richer data - for some commands. default is 'text'" + helptext {on,off} + - toggles whether type signatures are sent after each + completion request. default is 'off'" let outputText = @" Output format @@ -376,7 +374,7 @@ module internal CommandInput = | Parse of string * bool | Error of string | Project of string - | OutputMode of OutputMode + | HelpText of bool | Help | Quit @@ -397,14 +395,14 @@ module internal CommandInput = /// Parse 'errors' command let errors = string "errors" |> Parser.map (fun _ -> GetErrors) - /// Parse 'outputmode' command - let outputmode = parser { - let! _ = string "outputmode " - let! mode = (parser { let! _ = string "json" - return Json }) <|> - (parser { let! _ = string "text" - return Text }) - return OutputMode mode } + /// Parse helptext' command + let helptext = parser { + let! _ = string "helptext " + let! mode = (parser { let! _ = string "on" + return true }) <|> + (parser { let! _ = string "off" + return false }) + return HelpText mode } /// Parse 'project' command let project = parser { @@ -460,7 +458,7 @@ module internal CommandInput = | null -> Quit | input -> let reader = Parsing.createForwardStringReader input 0 - let cmds = errors <|> help <|> declarations <|> parse <|> project <|> completionTipOrDecl <|> outputmode <|> quit <|> error + let cmds = errors <|> helptext <|> help <|> declarations <|> parse <|> project <|> completionTipOrDecl <|> quit <|> error reader |> Parsing.getFirst cmds // -------------------------------------------------------------------------------------- @@ -472,14 +470,14 @@ type internal State = { Files : Map Project : Option - OutputMode : OutputMode + HelpText : bool } /// Contains main loop of the application module internal Main = open CommandInput - let initialState = { Files = Map.empty; Project = None; OutputMode = Text } + let initialState = { Files = Map.empty; Project = None; HelpText = false } // Main agent that handles IntelliSense requests let agent = new IntelliSenseAgent() @@ -498,10 +496,10 @@ module internal Main = if not ok then Console.WriteLine("ERROR: Position is out of range\n<>") ok - Debug.print "main state is:\nproject: %b\nfiles: %A\nmode: %A" - (Option.isSome state.Project) - (Map.fold (fun ks k _ -> k::ks) [] state.Files) - state.OutputMode + // Debug.print "main state is:\nproject: %b\nfiles: %A\nmode: %A" + // (Option.isSome state.Project) + // (Map.fold (fun ks k _ -> k::ks) [] state.Files) + // state.OutputMode match parseCommand(Console.ReadLine()) with | GetErrors -> let errs = agent.GetErrors() @@ -512,8 +510,8 @@ module internal Main = Console.WriteLine("<>") main state - | OutputMode m -> - main { state with OutputMode = m } + | HelpText b -> + main { state with HelpText = b } | Parse(file,full) -> // Trigger parse request for a particular file @@ -524,7 +522,7 @@ module internal Main = let opts = RequestOptions(agent.GetCheckerOptions(file, text, state.Project), file, text, - state.OutputMode) + state.HelpText) agent.TriggerParseRequest(opts, full) Console.WriteLine("INFO: Background parsing started\n<>") main { state with Files = Map.add file lines state.Files } @@ -554,7 +552,7 @@ module internal Main = let opts = RequestOptions(agent.GetCheckerOptions(file, text, state.Project), file, text, - state.OutputMode) + state.HelpText) let decls = agent.GetDeclarations(opts) printfn "DATA: declarations" for tld in decls do @@ -573,7 +571,7 @@ module internal Main = let opts = RequestOptions(agent.GetCheckerOptions(file, text, state.Project), file, text, - state.OutputMode) + state.HelpText) match cmd with | Completion -> agent.DoCompletion(opts, pos, state.Files.[file].[line], timeout) diff --git a/emacs/fsharp-mode-completion.el b/emacs/fsharp-mode-completion.el index 625db616..eb55828a 100644 --- a/emacs/fsharp-mode-completion.el +++ b/emacs/fsharp-mode-completion.el @@ -213,7 +213,7 @@ display in a help buffer instead.") (with-current-buffer (process-buffer proc) (delete-region (point-min) (point-max))) (add-to-list 'ac-modes 'fsharp-mode) - (log-psendstr proc "outputmode json\n") + (log-psendstr proc "helptext on\n") proc) (fsharp-ac-message-safely "Failed to launch: '%s'" (s-join " " fsharp-ac-complete-command)) @@ -238,9 +238,9 @@ display in a help buffer instead.") )) (defun fsharp-ac-document (item) - (pos-tip-fill-string - (cdr (get-text-property 0 'fsharp-ac-doc item)) - popup-tip-max-width)) + (let* ((prop (assoc item fsharp-ac-current-helptext)) + (help (if prop (cdr prop) "Loading documentation..."))) + (pos-tip-fill-string help popup-tip-max-width))) (defun fsharp-ac-candidate () (interactive) @@ -510,6 +510,7 @@ around to the start of the buffer." ;(message "[filter] length(msg) = %d" (length msg)) (cond ((s-starts-with? "DATA: completion" msg) (fsharp-ac-handle-completion msg)) + ((s-starts-with? "DATA: helptext" msg) (fsharp-ac-handle-doctext msg)) ((s-starts-with? "DATA: finddecl" msg) (fsharp-ac-visit-definition msg)) ((s-starts-with? "DATA: tooltip" msg) (fsharp-ac-handle-tooltip msg)) ((s-starts-with? "DATA: errors" msg) (fsharp-ac-handle-errors msg)) @@ -523,13 +524,8 @@ around to the start of the buffer." (defun fsharp-ac-handle-completion (str) (setq str - (s-replace "DATA: completion" "" str)) - (let* ((json-array-type 'list) - (cs (json-read-from-string str)) - (names (-map (lambda (e) (propertize (cdr (assq 'Name e)) - 'fsharp-ac-doc - (assq 'Help e))) cs))) - + (s-lines (s-replace "DATA: completion\n" "" str))) + (case fsharp-ac-status (preempted (setq fsharp-ac-status 'idle) @@ -537,11 +533,21 @@ around to the start of the buffer." (ac-update)) (otherwise - (setq fsharp-ac-current-candidate names + (setq fsharp-ac-current-candidate str fsharp-ac-status 'acknowledged) (fsharp-ac--ac-start :force-init t) (ac-update) - (setq fsharp-ac-status 'idle))))) + (setq fsharp-ac-status 'idle)))) + +(defun fsharp-ac-handle-doctext (str) + (setq str + (s-replace "DATA: helptext" "" str)) + (let* ((json-array-type 'list) + (help (json-read-from-string str))) + (when (eq (length fsharp-ac-current-candidate) + (length help)) + (setq fsharp-ac-current-helptext + (-zip fsharp-ac-current-candidate help))))) (defun fsharp-ac-visit-definition (str) (if (string-match "\n\\(.*\\):\\([0-9]+\\):\\([0-9]+\\)" str) From 9fe3011fffd509901eb710422ccbc3f392fbc330 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Mon, 21 Oct 2013 20:39:22 +0100 Subject: [PATCH 02/24] Start to move logic out of the agent --- FSharp.AutoComplete/Program.fs | 132 ++++++++++++++++----------------- Makefile | 3 +- 2 files changed, 66 insertions(+), 69 deletions(-) diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index 5f43d9bb..eb905269 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -30,13 +30,12 @@ type OutputMode = /// Represents information needed to call the F# IntelliSense service /// (including project/script options, file name and source) -type internal RequestOptions(opts, file, src, help) = +type internal RequestOptions(opts, file, src) = member x.Options : CheckOptions = opts member x.FileName : string = file member x.Source : string = src - member x.HelpText : bool = help member x.WithSource(source) = - RequestOptions(opts, file, source, help) + RequestOptions(opts, file, source) override x.ToString() = sprintf "FileName: '%s'\nSource length: '%d'\nOptions: %s, %A, %A, %b, %b" @@ -197,42 +196,25 @@ type internal IntelliSenseAgent() = agent.PostAndReply(fun r -> GetTypeCheckInfo(opts, time, r)) /// Invokes dot-completion request and writes information to the standard output - member x.DoCompletion(opts : RequestOptions, ((line, column) as pos), lineStr, time) = + member x.DoCompletion(opts : RequestOptions, ((line, column) as pos), lineStr, time) : Option = let info = x.GetTypeCheckInfo(opts, time) - let decls = - Option.bind (fun (info: TypeCheckResults) -> - // Get the long identifier before the current location - // 'residue' is the part after the last dot and 'longName' is before - // e.g. System.Console.Wri --> "Wri", [ "System"; "Console"; ] - let lookBack = Parsing.createBackStringReader lineStr (column - 1) - let residue, longName = - lookBack |> Parsing.getFirst Parsing.parseBackIdentWithResidue - - // Get items & generate output - try - Some (info.GetDeclarations(None, pos, lineStr, (longName, residue), fun (_,_) -> false) - |> Async.RunSynchronously) - with :? System.TimeoutException as e -> - printfn "ERROR: GetDeclarations timed out\n<>" - None) info - - match decls with - | Some decls -> - printfn "DATA: completion" - for d in decls.Items do Console.WriteLine(d.Name) - printfn "<>" - if opts.HelpText then - printfn "DATA: helptext" - let cs = - [ for d in decls.Items do - yield TipFormatter.formatTip d.DescriptionText ] - Console.WriteLine(JsonConvert.SerializeObject(cs)) - printfn "<>" - | None -> printfn "ERROR: Could not get type information\n<>" - + Option.bind (fun (info: TypeCheckResults) -> + // Get the long identifier before the current location + // 'residue' is the part after the last dot and 'longName' is before + // e.g. System.Console.Wri --> "Wri", [ "System"; "Console"; ] + let lookBack = Parsing.createBackStringReader lineStr (column - 1) + let residue, longName = + lookBack |> Parsing.getFirst Parsing.parseBackIdentWithResidue + + // Get items & generate output + try + Some (info.GetDeclarations(None, pos, lineStr, (longName, residue), fun (_,_) -> false) + |> Async.RunSynchronously) + with :? System.TimeoutException as e -> + None) info /// Gets ToolTip for the specified location (and prints it to the output) - member x.GetToolTip(opts, ((line, column) as pos), lineStr, time) = + member x.GetToolTip(opts, ((line, column) as pos), lineStr, time) : Option = match x.GetTypeCheckInfo(opts, time) with | Some(info) -> // Parsing - find the identifier around the current location @@ -250,23 +232,13 @@ type internal IntelliSenseAgent() = | [] -> [] match identIsland with - | [ "" ] -> - // There is no identifier at the current location - printfn "INFO: No identifier found at this location\n<>" + | [ "" ] -> None | _ -> // Assume that we are inside identifier (F# services can also handle // case when we're in a string in '#r "Foo.dll"' but we don't do that) - let tip = info.GetDataTipText(pos, lineStr, identIsland, identToken) - match tip with - | DataTipText(elems) - when elems |> List.forall (function - DataTipElementNone -> true | _ -> false) -> - printfn "INFO: No tooltip information\n<>" - | _ -> - Console.WriteLine("DATA: tooltip") - Console.WriteLine(TipFormatter.formatTip tip) - Console.WriteLine("<>") - | None -> printfn "ERROR: Could not get type information\n<>" + Some (info.GetDataTipText(pos, lineStr, identIsland, identToken)) + + | None -> None /// Finds the point of declaration of the symbol at pos /// and writes information to the standard output @@ -496,6 +468,13 @@ module internal Main = if not ok then Console.WriteLine("ERROR: Position is out of range\n<>") ok + let getoptions file = + let text = String.concat "\n" state.Files.[file] + RequestOptions(agent.GetCheckerOptions(file, text, state.Project), + file, + text) + + // Debug.print "main state is:\nproject: %b\nfiles: %A\nmode: %A" // (Option.isSome state.Project) // (Map.fold (fun ks k _ -> k::ks) [] state.Files) @@ -519,11 +498,7 @@ module internal Main = let text = String.concat "\n" lines let file = Path.GetFullPath file if File.Exists file then - let opts = RequestOptions(agent.GetCheckerOptions(file, text, state.Project), - file, - text, - state.HelpText) - agent.TriggerParseRequest(opts, full) + agent.TriggerParseRequest(getoptions file, full) Console.WriteLine("INFO: Background parsing started\n<>") main { state with Files = Map.add file lines state.Files } else @@ -548,12 +523,7 @@ module internal Main = | Declarations file -> let file = Path.GetFullPath file if parsed file then - let text = String.concat "\n" state.Files.[file] - let opts = RequestOptions(agent.GetCheckerOptions(file, text, state.Project), - file, - text, - state.HelpText) - let decls = agent.GetDeclarations(opts) + let decls = agent.GetDeclarations(getoptions file) printfn "DATA: declarations" for tld in decls do let (s1, e1), (s2, e2) = tld.Declaration.Range @@ -567,15 +537,41 @@ module internal Main = | PosCommand(cmd, file, ((line, col) as pos), timeout) -> let file = Path.GetFullPath file if parsed file && posok file line col then - let text = String.concat "\n" state.Files.[file] - let opts = RequestOptions(agent.GetCheckerOptions(file, text, state.Project), - file, - text, - state.HelpText) + let opts = getoptions file match cmd with - | Completion -> agent.DoCompletion(opts, pos, state.Files.[file].[line], timeout) - | ToolTip -> agent.GetToolTip(opts, pos, state.Files.[file].[line], timeout) + | Completion -> + let decls = agent.DoCompletion(opts, pos, state.Files.[file].[line], timeout) + + match decls with + | Some decls -> + printfn "DATA: completion" + for d in decls.Items do Console.WriteLine(d.Name) + printfn "<>" + if state.HelpText then + printfn "DATA: helptext" + let cs = + [ for d in decls.Items do + yield TipFormatter.formatTip d.DescriptionText ] + Console.WriteLine(JsonConvert.SerializeObject(cs)) + printfn "<>" + | None -> printfn "ERROR: Could not get type information\n<>" + + | ToolTip -> + let tipopt = agent.GetToolTip(opts, pos, state.Files.[file].[line], timeout) + + match tipopt with + | None -> printfn "INFO: Could not fetch tooltip\n<>" + | Some tip -> + match tip with + | DataTipText(elems) when elems |> List.forall (function + DataTipElementNone -> true | _ -> false) -> + printfn "INFO: No tooltip information\n<>" + | _ -> + Console.WriteLine("DATA: tooltip") + Console.WriteLine(TipFormatter.formatTip tip) + Console.WriteLine("<>") + | FindDeclaration -> agent.FindDeclaration(opts, pos, state.Files.[file].[line], timeout) main state diff --git a/Makefile b/Makefile index e5829dd1..831e2285 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ bin/FSharp.CompilerBinding.dll: $(FSHARP_COMPILER_EDITOR) (cd FSharp.CompilerBinding && xbuild FSharp.CompilerBinding.fsproj) clean: - -rm -fr bin + -rm -fr FSharp.AutoComplete/bin + -rm -fr FSharp.CompilerBinding/bin autocomplete: bin/fsautocomplete.exe From 65e144acba13c50f8a0161a60cf21d10d23c4fd9 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Mon, 21 Oct 2013 22:49:30 +0100 Subject: [PATCH 03/24] fix options building --- FSharp.AutoComplete/Program.fs | 5 ++++- FSharp.AutoComplete/test/integration/Timeouts/output.txt | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index eb905269..c9f8918b 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -498,7 +498,10 @@ module internal Main = let text = String.concat "\n" lines let file = Path.GetFullPath file if File.Exists file then - agent.TriggerParseRequest(getoptions file, full) + let opts = RequestOptions(agent.GetCheckerOptions(file, text, state.Project), + file, + text) + agent.TriggerParseRequest(opts, full) Console.WriteLine("INFO: Background parsing started\n<>") main { state with Files = Map.add file lines state.Files } else diff --git a/FSharp.AutoComplete/test/integration/Timeouts/output.txt b/FSharp.AutoComplete/test/integration/Timeouts/output.txt index 5fd85296..61ca2f82 100644 --- a/FSharp.AutoComplete/test/integration/Timeouts/output.txt +++ b/FSharp.AutoComplete/test/integration/Timeouts/output.txt @@ -6,7 +6,7 @@ INFO: Background parsing started <> ERROR: Could not get type information <> -ERROR: Could not get type information +INFO: Could not fetch tooltip <> DATA: completion Equals From a6db7d00ace2a3b60c8118f5b68357727c71c9c1 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Tue, 22 Oct 2013 21:25:02 +0100 Subject: [PATCH 04/24] Pull all printing out to main loop --- FSharp.AutoComplete/Program.fs | 66 ++++++++----------- .../test/integration/OutOfRange/output.txt | 10 ++- 2 files changed, 32 insertions(+), 44 deletions(-) diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index c9f8918b..5cacbf8c 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -213,10 +213,7 @@ type internal IntelliSenseAgent() = with :? System.TimeoutException as e -> None) info - /// Gets ToolTip for the specified location (and prints it to the output) - member x.GetToolTip(opts, ((line, column) as pos), lineStr, time) : Option = - match x.GetTypeCheckInfo(opts, time) with - | Some(info) -> + member private x.FindLongIdents(lineStr, column) = // Parsing - find the identifier around the current location // (we look for full identifier in the backward direction, but only // for a short identifier forward - this means that when you hover @@ -233,47 +230,32 @@ type internal IntelliSenseAgent() = match identIsland with | [ "" ] -> None - | _ -> + | _ -> Some identIsland + + /// Gets ToolTip for the specified location (and prints it to the output) + member x.GetToolTip(opts, ((line, column) as pos), lineStr, time) : Option = + + Option.bind (fun identIsland -> + Option.map (fun (info:TypeCheckResults) -> // Assume that we are inside identifier (F# services can also handle // case when we're in a string in '#r "Foo.dll"' but we don't do that) - Some (info.GetDataTipText(pos, lineStr, identIsland, identToken)) - - | None -> None + info.GetDataTipText(pos, lineStr, identIsland, identToken) + ) (x.GetTypeCheckInfo(opts, time)) + ) (x.FindLongIdents(lineStr, column)) /// Finds the point of declaration of the symbol at pos /// and writes information to the standard output member x.FindDeclaration(opts : RequestOptions, ((line, column) as pos), lineStr, time) = - match x.GetTypeCheckInfo(opts, time) with - | Some(info) -> - // Parsing - find the identifier around the current location - // (we look for full identifier in the backward direction, but only - // for a short identifier forward - this means that when you hover - // 'B' in 'A.B.C', you will get intellisense for 'A.B' module) - let lookBack = Parsing.createBackStringReader lineStr column - let lookForw = Parsing.createForwardStringReader lineStr (column + 1) - let backIdent = Parsing.getFirst Parsing.parseBackLongIdent lookBack - let nextIdent = Parsing.getFirst Parsing.parseIdent lookForw - let identIsland = - match List.rev backIdent with - | last::prev -> (last + nextIdent)::prev |> List.rev - | [] -> [] - - match identIsland with - | [ "" ] -> - // There is no identifier at the current location - printfn "ERROR: No identifier found at this location\n<>" - | _ -> + Option.bind (fun identIsland -> + Option.map (fun (info:TypeCheckResults) -> // Assume that we are inside identifier (F# services can also handle // case when we're in a string in '#r "Foo.dll"' but we don't do that) - // Get items & generate output - // TODO: Need this first because of VS debug info coming out - Console.WriteLine("DATA: finddecl") - match info.GetDeclarationLocation(pos, lineStr, identIsland, identToken, true) with - | DeclFound ((line,col),file) -> - printfn "%s:%d:%d\n<>" file line col - | DeclNotFound _ -> printfn "ERROR: Could not find point of declaration\n<>" - | None -> printfn "ERROR: Could not get type information\n<>" + info.GetDeclarationLocation(pos, lineStr, identIsland, identToken, true) + ) (x.GetTypeCheckInfo(opts, time)) + ) (x.FindLongIdents(lineStr, column)) + + // -------------------------------------------------------------------------------------- // Utilities for parsing & processing command line input @@ -569,13 +551,21 @@ module internal Main = match tip with | DataTipText(elems) when elems |> List.forall (function DataTipElementNone -> true | _ -> false) -> - printfn "INFO: No tooltip information\n<>" + printfn "ERROR: No tooltip information\n<>" | _ -> Console.WriteLine("DATA: tooltip") Console.WriteLine(TipFormatter.formatTip tip) Console.WriteLine("<>") - | FindDeclaration -> agent.FindDeclaration(opts, pos, state.Files.[file].[line], timeout) + | FindDeclaration -> + + match agent.FindDeclaration(opts, pos, state.Files.[file].[line], timeout) with + | None + | Some (DeclNotFound _) -> printfn "ERROR: Could not get find declaration\n<>" + | Some (DeclFound ((line,col),file)) -> + + printfn "DATA: finddecl\n%s:%d:%d\n<>" file line col + main state | Help -> diff --git a/FSharp.AutoComplete/test/integration/OutOfRange/output.txt b/FSharp.AutoComplete/test/integration/OutOfRange/output.txt index e6fafdd5..83dd97f6 100644 --- a/FSharp.AutoComplete/test/integration/OutOfRange/output.txt +++ b/FSharp.AutoComplete/test/integration/OutOfRange/output.txt @@ -249,20 +249,18 @@ module XA from Script <> -INFO: No tooltip information +ERROR: No tooltip information <> -INFO: No tooltip information +ERROR: No tooltip information <> ERROR: Position is out of range <> DATA: finddecl /test/integration/OutOfRange/Script.fsx:2:7 <> -DATA: finddecl -ERROR: Could not find point of declaration +ERROR: Could not get find declaration <> -DATA: finddecl -ERROR: Could not find point of declaration +ERROR: Could not get find declaration <> ERROR: Position is out of range <> From 66c5f8e0c2b59be2bde12d4ea75481e5308a9afe Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Wed, 23 Oct 2013 21:56:17 +0100 Subject: [PATCH 05/24] readd outputmode --- FSharp.AutoComplete/Program.fs | 53 ++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index 5cacbf8c..559e713a 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -27,7 +27,6 @@ type OutputMode = | Json | Text - /// Represents information needed to call the F# IntelliSense service /// (including project/script options, file name and source) type internal RequestOptions(opts, file, src) = @@ -240,7 +239,7 @@ type internal IntelliSenseAgent() = // Assume that we are inside identifier (F# services can also handle // case when we're in a string in '#r "Foo.dll"' but we don't do that) info.GetDataTipText(pos, lineStr, identIsland, identToken) - ) (x.GetTypeCheckInfo(opts, time)) + ) (x.GetTypeCheckInfo(opts, time)) ) (x.FindLongIdents(lineStr, column)) /// Finds the point of declaration of the symbol at pos @@ -252,7 +251,7 @@ type internal IntelliSenseAgent() = // Assume that we are inside identifier (F# services can also handle // case when we're in a string in '#r "Foo.dll"' but we don't do that) info.GetDeclarationLocation(pos, lineStr, identIsland, identToken, true) - ) (x.GetTypeCheckInfo(opts, time)) + ) (x.GetTypeCheckInfo(opts, time)) ) (x.FindLongIdents(lineStr, column)) @@ -329,6 +328,7 @@ module internal CommandInput = | Error of string | Project of string | HelpText of bool + | OutputMode of OutputMode | Help | Quit @@ -358,6 +358,15 @@ module internal CommandInput = return false }) return HelpText mode } + /// Parse 'outputmode' command + let outputmode = parser { + let! _ = string "outputmode " + let! mode = (parser { let! _ = string "json" + return Json }) <|> + (parser { let! _ = string "text" + return Text }) + return OutputMode mode } + /// Parse 'project' command let project = parser { let! _ = string "project " @@ -412,7 +421,7 @@ module internal CommandInput = | null -> Quit | input -> let reader = Parsing.createForwardStringReader input 0 - let cmds = errors <|> helptext <|> help <|> declarations <|> parse <|> project <|> completionTipOrDecl <|> quit <|> error + let cmds = errors <|> helptext <|> outputmode <|> help <|> declarations <|> parse <|> project <|> completionTipOrDecl <|> quit <|> error reader |> Parsing.getFirst cmds // -------------------------------------------------------------------------------------- @@ -425,13 +434,14 @@ type internal State = Files : Map Project : Option HelpText : bool + OutputMode : OutputMode } /// Contains main loop of the application module internal Main = open CommandInput - let initialState = { Files = Map.empty; Project = None; HelpText = false } + let initialState = { Files = Map.empty; Project = None; HelpText = false; OutputMode = Text } // Main agent that handles IntelliSense requests let agent = new IntelliSenseAgent() @@ -471,8 +481,9 @@ module internal Main = Console.WriteLine("<>") main state - | HelpText b -> - main { state with HelpText = b } + | HelpText b -> main { state with HelpText = b } + + | OutputMode m -> main { state with OutputMode = m } | Parse(file,full) -> // Trigger parse request for a particular file @@ -530,15 +541,25 @@ module internal Main = match decls with | Some decls -> - printfn "DATA: completion" - for d in decls.Items do Console.WriteLine(d.Name) - printfn "<>" - if state.HelpText then - printfn "DATA: helptext" - let cs = - [ for d in decls.Items do - yield TipFormatter.formatTip d.DescriptionText ] - Console.WriteLine(JsonConvert.SerializeObject(cs)) + match state.OutputMode with + | Text -> + printfn "DATA: completion" + for d in decls.Items do Console.WriteLine(d.Name) + printfn "<>" + + | Json -> + + let cs = + [ for d in decls.Items do + yield TipFormatter.formatTip d.DescriptionText ] + let payload = JsonConvert.SerializeObject(cs) + + Map.add "kind" "data" + |> Map.add "data" "completion" + |> Map.add "payload" payload + + printfn "DATA: helptext" + printfn "<>" | None -> printfn "ERROR: Could not get type information\n<>" From fd7722d43bb01f99c2da52fa4a970324fbcb36f1 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Wed, 23 Oct 2013 22:49:40 +0100 Subject: [PATCH 06/24] Json implementation for completions+helptext --- FSharp.AutoComplete/Program.fs | 91 ++++++++++++------- .../test/integration/Test1Json/FileTwo.fs | 14 +++ .../test/integration/Test1Json/Program.fs | 15 +++ .../test/integration/Test1Json/Script.fsx | 6 ++ .../test/integration/Test1Json/Test1.fsproj | 61 +++++++++++++ .../integration/Test1Json/Test1JsonRunner.fsx | 37 ++++++++ .../test/integration/Test1Json/output.txt | 63 +++++++++++++ 7 files changed, 252 insertions(+), 35 deletions(-) create mode 100644 FSharp.AutoComplete/test/integration/Test1Json/FileTwo.fs create mode 100644 FSharp.AutoComplete/test/integration/Test1Json/Program.fs create mode 100644 FSharp.AutoComplete/test/integration/Test1Json/Script.fsx create mode 100644 FSharp.AutoComplete/test/integration/Test1Json/Test1.fsproj create mode 100644 FSharp.AutoComplete/test/integration/Test1Json/Test1JsonRunner.fsx create mode 100644 FSharp.AutoComplete/test/integration/Test1Json/output.txt diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index 559e713a..b8cd2131 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -5,7 +5,7 @@ namespace FSharp.InteractiveAutocomplete open System open System.IO -open System.Collections.Generic +//open System.Collections.Generic open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.SourceCodeServices @@ -52,12 +52,30 @@ type internal IntelliSenseAgentMessage = /// Used to marshal completion candidates /// before serializing to JSON -type Candidate = +// type Candidate = +// { +// Name: string +// Help: string +// } + +type ResponseMsg<'T> = + { + Kind: string + Data: 'T + } + +type CompletionPayload = { - Name: string - Help: string + Completions: List + HelpText: Map } +type HelpTextPayload = + { + HelpText: Map + } + + /// Provides an easy access to F# IntelliSense service type internal IntelliSenseAgent() = @@ -285,10 +303,8 @@ module internal CommandInput = - find the point of declaration of the object at specified position project """" - associates the current session with the specified project - helptext {on,off} - - toggles whether type signatures are sent after each - completion request. default is 'off'" - + " + let outputText = @" Output format ============= @@ -327,7 +343,6 @@ module internal CommandInput = | Parse of string * bool | Error of string | Project of string - | HelpText of bool | OutputMode of OutputMode | Help | Quit @@ -349,15 +364,6 @@ module internal CommandInput = /// Parse 'errors' command let errors = string "errors" |> Parser.map (fun _ -> GetErrors) - /// Parse helptext' command - let helptext = parser { - let! _ = string "helptext " - let! mode = (parser { let! _ = string "on" - return true }) <|> - (parser { let! _ = string "off" - return false }) - return HelpText mode } - /// Parse 'outputmode' command let outputmode = parser { let! _ = string "outputmode " @@ -421,7 +427,7 @@ module internal CommandInput = | null -> Quit | input -> let reader = Parsing.createForwardStringReader input 0 - let cmds = errors <|> helptext <|> outputmode <|> help <|> declarations <|> parse <|> project <|> completionTipOrDecl <|> quit <|> error + let cmds = errors <|> outputmode <|> help <|> declarations <|> parse <|> project <|> completionTipOrDecl <|> quit <|> error reader |> Parsing.getFirst cmds // -------------------------------------------------------------------------------------- @@ -433,7 +439,6 @@ type internal State = { Files : Map Project : Option - HelpText : bool OutputMode : OutputMode } @@ -441,7 +446,7 @@ type internal State = module internal Main = open CommandInput - let initialState = { Files = Map.empty; Project = None; HelpText = false; OutputMode = Text } + let initialState = { Files = Map.empty; Project = None; OutputMode = Text } // Main agent that handles IntelliSense requests let agent = new IntelliSenseAgent() @@ -481,8 +486,6 @@ module internal Main = Console.WriteLine("<>") main state - | HelpText b -> main { state with HelpText = b } - | OutputMode m -> main { state with OutputMode = m } | Parse(file,full) -> @@ -548,19 +551,37 @@ module internal Main = printfn "<>" | Json -> - - let cs = - [ for d in decls.Items do - yield TipFormatter.formatTip d.DescriptionText ] - let payload = JsonConvert.SerializeObject(cs) - - Map.add "kind" "data" - |> Map.add "data" "completion" - |> Map.add "payload" payload - printfn "DATA: helptext" - - printfn "<>" + let ds = List.sortBy (fun (d: Declaration) -> d.Name) + [ for d in decls.Items do yield d ] + let helptext = + match ds with + | [] -> Map.empty + | d::_ -> let tip = TipFormatter.formatTip d.DescriptionText + Map.add d.Name tip Map.empty + { + Kind = "completion" + Data = + { + Completions = [ for d in ds do yield d.Name ] + HelpText = helptext + } + } + |> JsonConvert.SerializeObject + |> Console.WriteLine + + { + Kind = "helptext" + Data = + { + HelpText = [ for d in decls.Items do + yield d.Name, TipFormatter.formatTip d.DescriptionText ] + |> Map.ofList + } + } + |> JsonConvert.SerializeObject + |> Console.WriteLine + | None -> printfn "ERROR: Could not get type information\n<>" | ToolTip -> diff --git a/FSharp.AutoComplete/test/integration/Test1Json/FileTwo.fs b/FSharp.AutoComplete/test/integration/Test1Json/FileTwo.fs new file mode 100644 index 00000000..105081bd --- /dev/null +++ b/FSharp.AutoComplete/test/integration/Test1Json/FileTwo.fs @@ -0,0 +1,14 @@ +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) : int = + y diff --git a/FSharp.AutoComplete/test/integration/Test1Json/Program.fs b/FSharp.AutoComplete/test/integration/Test1Json/Program.fs new file mode 100644 index 00000000..60181db4 --- /dev/null +++ b/FSharp.AutoComplete/test/integration/Test1Json/Program.fs @@ -0,0 +1,15 @@ +module X = + let func x = x + 1 + +let testval = FileTwo.NewObjectType() + +let val2 = X.func 2 + +let val3 = testval.Terrific val2 + +let val4 : FileTwo.NewObjectType = testval + +[] +let main args = + printfn "Hello %d" val2 + 0 diff --git a/FSharp.AutoComplete/test/integration/Test1Json/Script.fsx b/FSharp.AutoComplete/test/integration/Test1Json/Script.fsx new file mode 100644 index 00000000..eea456da --- /dev/null +++ b/FSharp.AutoComplete/test/integration/Test1Json/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/Test1Json/Test1.fsproj b/FSharp.AutoComplete/test/integration/Test1Json/Test1.fsproj new file mode 100644 index 00000000..d3002694 --- /dev/null +++ b/FSharp.AutoComplete/test/integration/Test1Json/Test1.fsproj @@ -0,0 +1,61 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {116cc2f9-f987-4b3d-915a-34cac04a73da} + Exe + Test1 + Test1 + Test1 + False + + + Program.fs + + + + + True + full + 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 + + + + + + + + + + + + + + + diff --git a/FSharp.AutoComplete/test/integration/Test1Json/Test1JsonRunner.fsx b/FSharp.AutoComplete/test/integration/Test1Json/Test1JsonRunner.fsx new file mode 100644 index 00000000..a167aa6e --- /dev/null +++ b/FSharp.AutoComplete/test/integration/Test1Json/Test1JsonRunner.fsx @@ -0,0 +1,37 @@ +#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 "Script.fsx" +p.parse "Program.fs" +p.completion "Script.fsx" 5 15 +p.completion "Program.fs" 7 19 +p.completion "Program.fs" 3 22 +p.completion "Program.fs" 5 13 +p.completion "Program.fs" 9 19 +p.tooltip "FileTwo.fs" 8 6 +p.tooltip "Program.fs" 5 15 +p.tooltip "Program.fs" 3 8 +p.tooltip "Script.fsx" 3 9 +p.declarations "Program.fs" +p.declarations "FileTwo.fs" +p.declarations "Script.fsx" +p.send "errors\n" +p.send "quit\n" +let output = p.finalOutput () +File.WriteAllText("output.txt", output) + diff --git a/FSharp.AutoComplete/test/integration/Test1Json/output.txt b/FSharp.AutoComplete/test/integration/Test1Json/output.txt new file mode 100644 index 00000000..77fd3cc0 --- /dev/null +++ b/FSharp.AutoComplete/test/integration/Test1Json/output.txt @@ -0,0 +1,63 @@ +DATA: project +/Users/robnea/dev/rneatherway-fsharpbinding/FSharp.AutoComplete/test/integration/Test1Json/FileTwo.fs +/Users/robnea/dev/rneatherway-fsharpbinding/FSharp.AutoComplete/test/integration/Test1Json/Program.fs +<> +INFO: Background parsing started +<> +INFO: Background parsing started +<> +INFO: Background parsing started +<> +{"Kind":"completion","Data":{"Completions":["funky"],"HelpText":{"funky":"val funky : x:int -> int"}}} +{"Kind":"helptext","Data":{"HelpText":{"funky":"val funky : x:int -> int"}}} +{"Kind":"completion","Data":{"Completions":["Equals","GetHashCode","GetType","Terrific","ToString"],"HelpText":{"Equals":"System.Object.Equals(obj: obj) : bool"}}} +{"Kind":"helptext","Data":{"HelpText":{"Equals":"System.Object.Equals(obj: obj) : bool","GetHashCode":"System.Object.GetHashCode() : int","GetType":"System.Object.GetType() : System.Type","Terrific":"member FileTwo.NewObjectType.Terrific : y:int -> int","ToString":"System.Object.ToString() : string"}}} +{"Kind":"completion","Data":{"Completions":["Bar","Foo","NewObjectType","Qux","add","addition"],"HelpText":{"Bar":"union case FileTwo.Foo.Bar: FileTwo.Foo"}}} +{"Kind":"helptext","Data":{"HelpText":{"Bar":"union case FileTwo.Foo.Bar: FileTwo.Foo","Foo":"type Foo =\n | Bar\n | Qux","NewObjectType":"Multiple items\ntype NewObjectType =\n new : unit -> NewObjectType\n member Terrific : y:int -> int\n\n--------------------\n\nnew : unit -> FileTwo.NewObjectType","Qux":"union case FileTwo.Foo.Qux: FileTwo.Foo","add":"val add : x:int -> y:int -> int","addition":"val addition : x:int -> y:int -> int"}}} +{"Kind":"completion","Data":{"Completions":["func"],"HelpText":{"func":"val func : x:int -> int"}}} +{"Kind":"helptext","Data":{"HelpText":{"func":"val func : x:int -> int"}}} +{"Kind":"completion","Data":{"Completions":["Bar","Foo","NewObjectType","Qux","add","addition"],"HelpText":{"Bar":"union case FileTwo.Foo.Bar: FileTwo.Foo"}}} +{"Kind":"helptext","Data":{"HelpText":{"Bar":"union case FileTwo.Foo.Bar: FileTwo.Foo","Foo":"type Foo =\n | Bar\n | Qux","NewObjectType":"Multiple items\ntype NewObjectType =\n new : unit -> NewObjectType\n member Terrific : y:int -> int\n\n--------------------\n\nnew : unit -> FileTwo.NewObjectType","Qux":"union case FileTwo.Foo.Qux: FileTwo.Foo","add":"val add : x:int -> y:int -> int","addition":"val addition : x:int -> y:int -> int"}}} +DATA: tooltip +val add : x:int -> y:int -> int + +Full name: FileTwo.add +<> +DATA: tooltip +val func : x:int -> int + +Full name: Program.X.func +<> +DATA: tooltip +val testval : FileTwo.NewObjectType + +Full name: Program.testval +<> +DATA: tooltip +val funky : x:int -> int + +Full name: Script.XA.funky +<> +DATA: declarations +[1:0-19:0] Program + - [13:4-15:5] main +[1:0-2:20] X + - [2:6-2:20] func +<> +DATA: declarations +[1:0-14:5] FileTwo + - [9:4-9:19] add + - [7:4-7:24] addition +[3:5-5:7] Foo + - [4:4-4:7] Bar + - [5:4-5:7] Qux +[11:5-14:5] NewObjectType + - [13:11-14:5] Terrific +<> +DATA: declarations +[3:0-10:0] Script +[3:0-4:21] XA + - [4:6-4:21] funky +<> +DATA: errors +<> From 91c127ca8635e4e1dbdde85e1c50bfcd43cb579b Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Fri, 25 Oct 2013 18:02:57 +0100 Subject: [PATCH 07/24] Completed ubiquitous json output --- FSharp.AutoComplete/Program.fs | 97 +++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index b8cd2131..4795e7fc 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -75,7 +75,6 @@ type HelpTextPayload = HelpText: Map } - /// Provides an easy access to F# IntelliSense service type internal IntelliSenseAgent() = @@ -471,6 +470,14 @@ module internal Main = file, text) + let printMsg ty s = + match state.OutputMode with + | Text -> + printfn "%s: %s\n<>" ty s + | Json -> + { Kind = ty; Data = s } + |> JsonConvert.SerializeObject + |> Console.WriteLine // Debug.print "main state is:\nproject: %b\nfiles: %A\nmode: %A" // (Option.isSome state.Project) @@ -479,11 +486,17 @@ module internal Main = match parseCommand(Console.ReadLine()) with | GetErrors -> let errs = agent.GetErrors() - printfn "DATA: errors" - for e in errs do - printfn "[%d:%d-%d:%d] %s %s" e.StartLine e.StartColumn e.EndLine e.EndColumn - (if e.Severity = Severity.Error then "ERROR" else "WARNING") e.Message - Console.WriteLine("<>") + let errstrings = + [ for e in errs do + yield sprintf "[%d:%d-%d:%d] %s %s" e.StartLine e.StartColumn e.EndLine e.EndColumn + (if e.Severity = Severity.Error then "ERROR" else "WARNING") e.Message ] + match state.OutputMode with + | Text -> + printfn "DATA: errors\n%s\n<>" (String.concat "\n" errstrings) + | Json -> + { Kind = "errors"; Data = errs } + |> JsonConvert.SerializeObject |> Console.WriteLine + main state | OutputMode m -> main { state with OutputMode = m } @@ -498,39 +511,48 @@ module internal Main = file, text) agent.TriggerParseRequest(opts, full) - Console.WriteLine("INFO: Background parsing started\n<>") + printMsg "INFO" "Background parsing started" main { state with Files = Map.add file lines state.Files } else - printfn "ERROR: File '%s' does not exist\n<>" file + printMsg "ERROR" (sprintf "File '%s' does not exist" file) main state | Project file -> // Load project file and store in state if File.Exists file then match ProjectParser.load file with - | Some p -> Console.WriteLine("DATA: project") - for f in ProjectParser.getFiles p do - Console.WriteLine(IO.Path.Combine(ProjectParser.getDirectory p, f)) - Console.WriteLine("<>") - main { state with Project = Some p } - | None -> printfn "ERROR: Project file '%s' is invalid\n<>" file + | Some p -> + let files = + [ for f in ProjectParser.getFiles p do + yield IO.Path.Combine(ProjectParser.getDirectory p, f) ] + match state.OutputMode with + | Text -> + printfn "DATA: project\n%s\n<>" (String.concat "\n" files) + | Json -> + { Kind = "project"; Data = files } + |> JsonConvert.SerializeObject |> Console.WriteLine + main { state with Project = Some p } + | None -> printMsg "ERROR" (sprintf "Project file '%s' is invalid" file) main state else - printfn "ERROR: File '%s' does not exist\n<>" file + printMsg "ERROR" (sprintf "File '%s' does not exist" file) main state | Declarations file -> let file = Path.GetFullPath file if parsed file then let decls = agent.GetDeclarations(getoptions file) - printfn "DATA: declarations" - for tld in decls do - let (s1, e1), (s2, e2) = tld.Declaration.Range - printfn "[%d:%d-%d:%d] %s" e1 s1 e2 s2 tld.Declaration.Name - for d in tld.Nested do - let (s1, e1), (s2, e2) = d.Range - printfn " - [%d:%d-%d:%d] %s" e1 s1 e2 s2 d.Name - printfn "<>" + let declstrings = + [ for tld in decls do + let (s1, e1), (s2, e2) = tld.Declaration.Range + yield sprintf "[%d:%d-%d:%d] %s" e1 s1 e2 s2 tld.Declaration.Name + for d in tld.Nested do + let (s1, e1), (s2, e2) = d.Range + yield sprintf " - [%d:%d-%d:%d] %s" e1 s1 e2 s2 d.Name ] + match state.OutputMode with + | Text -> printfn "DATA: declarations\n%s\n<>" (String.concat "\n" declstrings) + | Json -> { Kind = "declarations"; Data = decls } + |> JsonConvert.SerializeObject |> Console.WriteLine main state | PosCommand(cmd, file, ((line, col) as pos), timeout) -> @@ -572,38 +594,41 @@ module internal Main = { Kind = "helptext" - Data = - { - HelpText = [ for d in decls.Items do - yield d.Name, TipFormatter.formatTip d.DescriptionText ] - |> Map.ofList - } + Data = [ for d in decls.Items do + yield d.Name, TipFormatter.formatTip d.DescriptionText ] + |> Map.ofList } |> JsonConvert.SerializeObject |> Console.WriteLine - | None -> printfn "ERROR: Could not get type information\n<>" + | None -> + printMsg "ERROR" "Could not get type information" | ToolTip -> let tipopt = agent.GetToolTip(opts, pos, state.Files.[file].[line], timeout) match tipopt with - | None -> printfn "INFO: Could not fetch tooltip\n<>" + | None -> printMsg "INFO" "No tooltip information" | Some tip -> match tip with | DataTipText(elems) when elems |> List.forall (function DataTipElementNone -> true | _ -> false) -> - printfn "ERROR: No tooltip information\n<>" + printMsg "INFO" "No tooltip information" | _ -> - Console.WriteLine("DATA: tooltip") - Console.WriteLine(TipFormatter.formatTip tip) - Console.WriteLine("<>") + match state.OutputMode with + | Text -> + Console.WriteLine("DATA: tooltip") + Console.WriteLine(TipFormatter.formatTip tip) + Console.WriteLine("<>") + | Json -> { Kind = "tooltip"; Data = TipFormatter.formatTip tip } + |> JsonConvert.SerializeObject + |> Console.WriteLine | FindDeclaration -> match agent.FindDeclaration(opts, pos, state.Files.[file].[line], timeout) with | None - | Some (DeclNotFound _) -> printfn "ERROR: Could not get find declaration\n<>" + | Some (DeclNotFound _) -> printMsg "ERROR" "Could not find declaration" | Some (DeclFound ((line,col),file)) -> printfn "DATA: finddecl\n%s:%d:%d\n<>" file line col From 02cd8e986f3103b02556050fe9618752a1a2936c Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Sun, 27 Oct 2013 23:28:48 +0000 Subject: [PATCH 08/24] Almost completed cutover to JSON error severity not parsed correctly and help text not showing up --- FSharp.AutoComplete/Program.fs | 105 ++++++--------- .../integration/Test1Json/Test1JsonRunner.fsx | 2 + emacs/fsharp-mode-completion.el | 120 ++++++++---------- 3 files changed, 94 insertions(+), 133 deletions(-) diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index 4795e7fc..7fd4e8e4 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -50,29 +50,17 @@ type internal IntelliSenseAgentMessage = | GetErrors of AsyncReplyChannel | GetDeclarationsMessage of RequestOptions * AsyncReplyChannel -/// Used to marshal completion candidates -/// before serializing to JSON -// type Candidate = -// { -// Name: string -// Help: string -// } - type ResponseMsg<'T> = { Kind: string Data: 'T } -type CompletionPayload = +type Location = { - Completions: List - HelpText: Map - } - -type HelpTextPayload = - { - HelpText: Map + File: string + Line: int + Column: int } /// Provides an easy access to F# IntelliSense service @@ -158,7 +146,7 @@ type internal IntelliSenseAgent() = // but currently editing a script file checker.GetCheckOptionsFromScriptRoot(fileName, source, System.DateTime.Now) - // The InteractiveChecker resolution doesn't sometimes + // The InteractiveChecker resolution sometimes doesn't // include FSharp.Core and other essential assemblies, so we may // need to bring over some more code from the monodevelop binding to // handle that situation. @@ -417,8 +405,7 @@ module internal CommandInput = return PosCommand(f, filename, (line, col), timeout) } // Parses always and returns default error message - let error = parser { - return Error("ERROR: Unknown command or wrong arguments\n<>") } + let error = parser { return Error("Unknown command or wrong arguments") } // Parase any of the supported commands let parseCommand = @@ -451,9 +438,17 @@ module internal Main = let agent = new IntelliSenseAgent() let rec main (state:State) : int = + + let prAsJson o = Console.WriteLine (JsonConvert.SerializeObject o) + + let printMsg ty s = + match state.OutputMode with + | Text -> printfn "%s: %s\n<>" ty s + | Json -> prAsJson { Kind = ty; Data = s } + let parsed file = let ok = Map.containsKey file state.Files - if not ok then printfn "ERROR: File '%s' not parsed\n<>\n" file + if not ok then printMsg "ERROR" (sprintf "File '%s' not parsed" file) ok /// Is the specified position consistent with internal state of file? @@ -461,7 +456,7 @@ module internal Main = let lines = state.Files.[file] let ok = line < lines.Length && line >= 0 && col <= lines.[line].Length && col >= 0 - if not ok then Console.WriteLine("ERROR: Position is out of range\n<>") + if not ok then printMsg "ERROR" "Position is out of range" ok let getoptions file = @@ -470,15 +465,6 @@ module internal Main = file, text) - let printMsg ty s = - match state.OutputMode with - | Text -> - printfn "%s: %s\n<>" ty s - | Json -> - { Kind = ty; Data = s } - |> JsonConvert.SerializeObject - |> Console.WriteLine - // Debug.print "main state is:\nproject: %b\nfiles: %A\nmode: %A" // (Option.isSome state.Project) // (Map.fold (fun ks k _ -> k::ks) [] state.Files) @@ -491,11 +477,8 @@ module internal Main = yield sprintf "[%d:%d-%d:%d] %s %s" e.StartLine e.StartColumn e.EndLine e.EndColumn (if e.Severity = Severity.Error then "ERROR" else "WARNING") e.Message ] match state.OutputMode with - | Text -> - printfn "DATA: errors\n%s\n<>" (String.concat "\n" errstrings) - | Json -> - { Kind = "errors"; Data = errs } - |> JsonConvert.SerializeObject |> Console.WriteLine + | Text -> printfn "DATA: errors\n%s\n<>" (String.concat "\n" errstrings) + | Json -> prAsJson { Kind = "errors"; Data = errs } main state @@ -526,11 +509,8 @@ module internal Main = [ for f in ProjectParser.getFiles p do yield IO.Path.Combine(ProjectParser.getDirectory p, f) ] match state.OutputMode with - | Text -> - printfn "DATA: project\n%s\n<>" (String.concat "\n" files) - | Json -> - { Kind = "project"; Data = files } - |> JsonConvert.SerializeObject |> Console.WriteLine + | Text -> printfn "DATA: project\n%s\n<>" (String.concat "\n" files) + | Json -> prAsJson { Kind = "project"; Data = files } main { state with Project = Some p } | None -> printMsg "ERROR" (sprintf "Project file '%s' is invalid" file) main state @@ -551,8 +531,7 @@ module internal Main = yield sprintf " - [%d:%d-%d:%d] %s" e1 s1 e2 s2 d.Name ] match state.OutputMode with | Text -> printfn "DATA: declarations\n%s\n<>" (String.concat "\n" declstrings) - | Json -> { Kind = "declarations"; Data = decls } - |> JsonConvert.SerializeObject |> Console.WriteLine + | Json -> prAsJson { Kind = "declarations"; Data = decls } main state | PosCommand(cmd, file, ((line, col) as pos), timeout) -> @@ -581,25 +560,17 @@ module internal Main = | [] -> Map.empty | d::_ -> let tip = TipFormatter.formatTip d.DescriptionText Map.add d.Name tip Map.empty - { - Kind = "completion" - Data = - { - Completions = [ for d in ds do yield d.Name ] - HelpText = helptext - } - } - |> JsonConvert.SerializeObject - |> Console.WriteLine - - { - Kind = "helptext" - Data = [ for d in decls.Items do - yield d.Name, TipFormatter.formatTip d.DescriptionText ] - |> Map.ofList - } - |> JsonConvert.SerializeObject - |> Console.WriteLine + + prAsJson { Kind = "helptext"; Data = helptext } + + prAsJson { Kind = "completion" + Data = [ for d in ds do yield d.Name ] } + + prAsJson + { Kind = "helptext" + Data = [ for d in decls.Items do + yield d.Name, TipFormatter.formatTip d.DescriptionText ] + |> Map.ofList } | None -> printMsg "ERROR" "Could not get type information" @@ -620,9 +591,7 @@ module internal Main = Console.WriteLine("DATA: tooltip") Console.WriteLine(TipFormatter.formatTip tip) Console.WriteLine("<>") - | Json -> { Kind = "tooltip"; Data = TipFormatter.formatTip tip } - |> JsonConvert.SerializeObject - |> Console.WriteLine + | Json -> prAsJson { Kind = "tooltip"; Data = TipFormatter.formatTip tip } | FindDeclaration -> @@ -631,7 +600,11 @@ module internal Main = | Some (DeclNotFound _) -> printMsg "ERROR" "Could not find declaration" | Some (DeclFound ((line,col),file)) -> - printfn "DATA: finddecl\n%s:%d:%d\n<>" file line col + match state.OutputMode with + | Text -> printfn "DATA: finddecl\n%s:%d:%d\n<>" file line col + | Json -> + let data = { Line = line; Column = col; File = file } + prAsJson { Kind = "finddecl"; Data = data } main state @@ -640,7 +613,7 @@ module internal Main = main state | Error(msg) -> - Console.WriteLine(msg) + printMsg "ERROR" msg main state | Quit -> diff --git a/FSharp.AutoComplete/test/integration/Test1Json/Test1JsonRunner.fsx b/FSharp.AutoComplete/test/integration/Test1Json/Test1JsonRunner.fsx index a167aa6e..9672a999 100644 --- a/FSharp.AutoComplete/test/integration/Test1Json/Test1JsonRunner.fsx +++ b/FSharp.AutoComplete/test/integration/Test1Json/Test1JsonRunner.fsx @@ -27,6 +27,8 @@ p.tooltip "FileTwo.fs" 8 6 p.tooltip "Program.fs" 5 15 p.tooltip "Program.fs" 3 8 p.tooltip "Script.fsx" 3 9 +p.finddeclaration "Program.fs" 7 22 +p.finddeclaration "Script.fsx" 5 15 p.declarations "Program.fs" p.declarations "FileTwo.fs" p.declarations "Script.fsx" diff --git a/emacs/fsharp-mode-completion.el b/emacs/fsharp-mode-completion.el index eb55828a..d00990d0 100644 --- a/emacs/fsharp-mode-completion.el +++ b/emacs/fsharp-mode-completion.el @@ -213,7 +213,7 @@ display in a help buffer instead.") (with-current-buffer (process-buffer proc) (delete-region (point-min) (point-max))) (add-to-list 'ac-modes 'fsharp-mode) - (log-psendstr proc "helptext on\n") + (log-psendstr proc "outputmode json\n") proc) (fsharp-ac-message-safely "Failed to launch: '%s'" (s-join " " fsharp-ac-complete-command)) @@ -238,7 +238,7 @@ display in a help buffer instead.") )) (defun fsharp-ac-document (item) - (let* ((prop (assoc item fsharp-ac-current-helptext)) + (let* ((prop (gethash item fsharp-ac-current-helptext)) (help (if prop (cdr prop) "Loading documentation..."))) (pos-tip-fill-string help popup-tip-max-width))) @@ -373,26 +373,24 @@ possibly many lines of description.") (forward-char col) (point)))) -(defun fsharp-ac-parse-errors (str) +(defun fsharp-ac-parse-errors (data) "Extract the errors from the given process response. Returns a list of fsharp-error." (save-match-data (let (parsed) - (while (string-match fsharp-ac-error-regexp str) - (let ((beg (fsharp-ac-line-column-to-pos (+ (string-to-number (match-string 1 str)) 1) - (string-to-number (match-string 2 str)))) - (end (fsharp-ac-line-column-to-pos (+ (string-to-number (match-string 3 str)) 1) - (string-to-number (match-string 4 str)))) - (face (if (string= "ERROR" (match-string 5 str)) + (dolist (err data parsed) + (let ((beg (fsharp-ac-line-column-to-pos (+ (gethash "StartLine" err) 1) + (gethash "StartColumn" err))) + (end (fsharp-ac-line-column-to-pos (+ (gethash "EndLine" err) 1) + (gethash "EndColumn" err))) + (face (if t ;string= "ERROR" (gethash "Severity" err)) 'fsharp-error-face 'fsharp-warning-face)) - (msg (match-string 6 str)) + (msg (gethash "Message" err)) ) - (setq str (substring str (match-end 0))) (add-to-list 'parsed (make-fsharp-error :start beg :end end :face face - :text msg)))) - parsed))) + :text msg))))))) (defun fsharp-ac/show-error-overlay (err) "Draw overlays in the current buffer to represent fsharp-error ERR." @@ -485,16 +483,17 @@ around to the start of the buffer." ;;; ;;; Handle output from the completion process. -(defconst fsharp-ac-eom "\n<>\n") - (defun fsharp-ac--get-msg (proc) (with-current-buffer (process-buffer proc) (goto-char (point-min)) - (let ((eofloc (search-forward fsharp-ac-eom nil t))) + (let ((eofloc (search-forward "\n" nil t))) (when eofloc - (let ((msg (buffer-substring-no-properties (point-min) (match-beginning 0)))) + (let ((json-array-type 'list) + (json-object-type 'hash-table) + (json-key-type 'string) + (msg (buffer-substring-no-properties (point-min) (match-beginning 0)))) (delete-region (point-min) (match-end 0)) - msg))))) + (json-read-from-string msg)))))) (defun fsharp-ac-filter-output (proc str) "Filter output from the completion process and handle appropriately." @@ -508,24 +507,23 @@ around to the start of the buffer." (let ((msg (fsharp-ac--get-msg proc))) (while msg ;(message "[filter] length(msg) = %d" (length msg)) - (cond - ((s-starts-with? "DATA: completion" msg) (fsharp-ac-handle-completion msg)) - ((s-starts-with? "DATA: helptext" msg) (fsharp-ac-handle-doctext msg)) - ((s-starts-with? "DATA: finddecl" msg) (fsharp-ac-visit-definition msg)) - ((s-starts-with? "DATA: tooltip" msg) (fsharp-ac-handle-tooltip msg)) - ((s-starts-with? "DATA: errors" msg) (fsharp-ac-handle-errors msg)) - ((s-starts-with? "DATA: project" msg) (fsharp-ac-handle-project msg)) - ((s-starts-with? "ERROR: " msg) (fsharp-ac-handle-process-error msg)) - ((s-starts-with? "INFO: " msg) (when fsharp-ac-verbose (fsharp-ac-message-safely msg))) + (let ((kind (gethash "Kind" msg)) + (data (gethash "Data" msg))) + (cond + ((s-equals? "ERROR" kind) (fsharp-ac-handle-process-error data)) + ((s-equals? "INFO" kind) (when fsharp-ac-verbose (fsharp-ac-message-safely data))) + ((s-equals? "completion" kind) (fsharp-ac-handle-completion data)) + ((s-equals? "helptext" kind) (fsharp-ac-handle-doctext data)) + ((s-equals? "errors" kind) (fsharp-ac-handle-errors data)) + ((s-equals? "project" kind) (fsharp-ac-handle-project data)) + ((s-equals? "tooltip" kind) (fsharp-ac-handle-tooltip data)) + ((s-equals? "finddecl" kind) (fsharp-ac-visit-definition data)) (t - (fsharp-ac-message-safely "Error: unrecognised message: '%s'" msg))) + (fsharp-ac-message-safely "Error: unrecognised message kind: '%s'" kind)))) (setq msg (fsharp-ac--get-msg proc))))) -(defun fsharp-ac-handle-completion (str) - (setq str - (s-lines (s-replace "DATA: completion\n" "" str))) - +(defun fsharp-ac-handle-completion (data) (case fsharp-ac-status (preempted (setq fsharp-ac-status 'idle) @@ -533,55 +531,43 @@ around to the start of the buffer." (ac-update)) (otherwise - (setq fsharp-ac-current-candidate str + (setq fsharp-ac-current-candidate data fsharp-ac-status 'acknowledged) (fsharp-ac--ac-start :force-init t) (ac-update) (setq fsharp-ac-status 'idle)))) -(defun fsharp-ac-handle-doctext (str) - (setq str - (s-replace "DATA: helptext" "" str)) - (let* ((json-array-type 'list) - (help (json-read-from-string str))) - (when (eq (length fsharp-ac-current-candidate) - (length help)) - (setq fsharp-ac-current-helptext - (-zip fsharp-ac-current-candidate help))))) - -(defun fsharp-ac-visit-definition (str) - (if (string-match "\n\\(.*\\):\\([0-9]+\\):\\([0-9]+\\)" str) - (let ((file (match-string 1 str)) - (line (+ 1 (string-to-number (match-string 2 str)))) - (col (string-to-number (match-string 3 str)))) - (find-file (match-string 1 str)) - (goto-char (fsharp-ac-line-column-to-pos line col))) - (fsharp-ac-message-safely "Unable to find definition."))) - -(defun fsharp-ac-handle-errors (str) +(defun fsharp-ac-handle-doctext (data) + (setq fsharp-ac-current-helptext data)) + +(defun fsharp-ac-visit-definition (data) + (let* ((file (gethash "file" data)) + (line (+ 1 (gethash "line" data))) + (col (gethash "col" data))) + (find-file file) + (goto-char (fsharp-ac-line-column-to-pos line col)))) + +(defun fsharp-ac-handle-errors (data) "Display error overlays and set buffer-local error variables for error navigation." (fsharp-ac-clear-errors) - (let ((errs (fsharp-ac-parse-errors - (concat (replace-regexp-in-string "DATA: errors\n" "" str) "\n"))) - ) + (let ((errs (fsharp-ac-parse-errors data))) (setq fsharp-ac-errors errs) (mapc 'fsharp-ac/show-error-overlay errs))) -(defun fsharp-ac-handle-tooltip (str) +(defun fsharp-ac-handle-tooltip (data) "Display information from the background process. If the user has requested a popup tooltip, display a popup. Otherwise, display a short summary in the minibuffer." ;; Do not display if the current buffer is not an fsharp buffer. (when (equal major-mode 'fsharp-mode) (unless (or (active-minibuffer-window) cursor-in-echo-area) - (let ((cleaned (replace-regexp-in-string "DATA: tooltip\n" "" str))) - (if fsharp-ac-awaiting-tooltip - (progn - (setq fsharp-ac-awaiting-tooltip nil) - (if fsharp-ac-use-popup - (fsharp-ac/show-popup cleaned) - (fsharp-ac/show-info-window cleaned))) - (fsharp-ac-message-safely (fsharp-doc/format-for-minibuffer cleaned))))))) + (if fsharp-ac-awaiting-tooltip + (progn + (setq fsharp-ac-awaiting-tooltip nil) + (if fsharp-ac-use-popup + (fsharp-ac/show-popup data) + (fsharp-ac/show-info-window data))) + (fsharp-ac-message-safely (fsharp-doc/format-for-minibuffer data)))))) (defun fsharp-ac/show-popup (str) (if (display-graphic-p) @@ -599,8 +585,8 @@ display a short summary in the minibuffer." (with-help-window fsharp-ac-info-buffer-name (princ str))))) -(defun fsharp-ac-handle-project (str) - (setq fsharp-ac-project-files (cdr (split-string str "\n"))) +(defun fsharp-ac-handle-project (data) + (setq fsharp-ac-project-files data) (fsharp-ac-parse-file (car (last fsharp-ac-project-files)))) (defun fsharp-ac-handle-process-error (str) From af58f1fc5bf8c2bb3b18509396f729b5b44b07e4 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Mon, 28 Oct 2013 17:28:36 +0000 Subject: [PATCH 09/24] Fix goto definition --- emacs/fsharp-mode-completion.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/emacs/fsharp-mode-completion.el b/emacs/fsharp-mode-completion.el index d00990d0..88e1cab7 100644 --- a/emacs/fsharp-mode-completion.el +++ b/emacs/fsharp-mode-completion.el @@ -541,9 +541,9 @@ around to the start of the buffer." (setq fsharp-ac-current-helptext data)) (defun fsharp-ac-visit-definition (data) - (let* ((file (gethash "file" data)) - (line (+ 1 (gethash "line" data))) - (col (gethash "col" data))) + (let* ((file (gethash "File" data)) + (line (+ 1 (gethash "Line" data))) + (col (gethash "Column" data))) (find-file file) (goto-char (fsharp-ac-line-column-to-pos line col)))) From a2aeee2d7e01ec4064168defacda51da3493b92b Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Mon, 28 Oct 2013 18:53:20 +0000 Subject: [PATCH 10/24] Fix parameter info display --- emacs/fsharp-mode-completion.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emacs/fsharp-mode-completion.el b/emacs/fsharp-mode-completion.el index 88e1cab7..c4e144ae 100644 --- a/emacs/fsharp-mode-completion.el +++ b/emacs/fsharp-mode-completion.el @@ -239,7 +239,7 @@ display in a help buffer instead.") (defun fsharp-ac-document (item) (let* ((prop (gethash item fsharp-ac-current-helptext)) - (help (if prop (cdr prop) "Loading documentation..."))) + (help (if prop prop "Loading documentation..."))) (pos-tip-fill-string help popup-tip-max-width))) (defun fsharp-ac-candidate () From 4ebdc90866227b5988b631f3ddad2da7dca3cc12 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Tue, 29 Oct 2013 19:55:13 +0000 Subject: [PATCH 11/24] Fix error serialization --- FSharp.AutoComplete/Program.fs | 21 ++++++++++++++++++++- emacs/fsharp-mode-completion.el | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index 7fd4e8e4..b659c6b3 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -13,6 +13,7 @@ open FSharp.InteractiveAutocomplete.Parsing open FSharp.CompilerBinding.Reflection open Newtonsoft.Json +open Newtonsoft.Json.Converters module FsParser = Microsoft.FSharp.Compiler.Parser @@ -63,6 +64,22 @@ type Location = Column: int } +type SeverityConverter() = + inherit JsonConverter() + + override x.CanConvert(t:System.Type) = t = typeof + + override x.WriteJson(writer, value, serializer) = + match value :?> Severity with + | Severity.Error -> serializer.Serialize(writer, "Error") + | Severity.Warning -> serializer.Serialize(writer, "Warning") + + override x.ReadJson(reader, t, _, serializer) = + raise (System.NotSupportedException()) + + override x.CanRead = false + override x.CanWrite = true + /// Provides an easy access to F# IntelliSense service type internal IntelliSenseAgent() = @@ -437,9 +454,11 @@ module internal Main = // Main agent that handles IntelliSense requests let agent = new IntelliSenseAgent() + let severityConverter = new SeverityConverter() + let rec main (state:State) : int = - let prAsJson o = Console.WriteLine (JsonConvert.SerializeObject o) + let prAsJson o = Console.WriteLine (JsonConvert.SerializeObject(o, severityConverter)) let printMsg ty s = match state.OutputMode with diff --git a/emacs/fsharp-mode-completion.el b/emacs/fsharp-mode-completion.el index c4e144ae..23810c30 100644 --- a/emacs/fsharp-mode-completion.el +++ b/emacs/fsharp-mode-completion.el @@ -382,7 +382,7 @@ possibly many lines of description.") (gethash "StartColumn" err))) (end (fsharp-ac-line-column-to-pos (+ (gethash "EndLine" err) 1) (gethash "EndColumn" err))) - (face (if t ;string= "ERROR" (gethash "Severity" err)) + (face (if (string= "Error" (gethash "Severity" err)) 'fsharp-error-face 'fsharp-warning-face)) (msg (gethash "Message" err)) From 953933908e529833cba19b2eb229e37cf60aed1c Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Wed, 30 Oct 2013 00:28:35 +0000 Subject: [PATCH 12/24] Fix some failing tests --- emacs/test/fsharp-mode-completion-tests.el | 48 ++++++++-------------- 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/emacs/test/fsharp-mode-completion-tests.el b/emacs/test/fsharp-mode-completion-tests.el index 773ab120..15b5cd57 100644 --- a/emacs/test/fsharp-mode-completion-tests.el +++ b/emacs/test/fsharp-mode-completion-tests.el @@ -4,13 +4,13 @@ (defconst finddeclstr1 (let ((file (concat fs-file-dir "Program.fs"))) - (format "DATA: finddecl\nfile stored in metadata is '%s'\n%s:1:6\n<>\n" file file)) + (format "{\"Kind\":\"finddecl\",\"Data\":{\"File\":\"%s\",\"Line\":1,\"Column\":6}}\n" file)) "A message for jumping to a definition in the same file") (defconst finddeclstr2 (let ((file (concat fs-file-dir "FileTwo.fs"))) - (format "DATA: finddecl\nfile stored in metadata is '%s'\n%s:12:11\n<>\n" file file)) - "A message for jumping to a definition in the another file") + (format "{\"Kind\":\"finddecl\",\"Data\":{\"File\":\"%s\",\"Line\":12,\"Column\":11}}\n" file file)) + "A message for jumping to a definition in another file") (check "jumping to local definition should not change buffer" (let ((f (concat fs-file-dir "Program.fs"))) @@ -42,18 +42,7 @@ ;;; Error parsing (defconst err-brace-str - (mapconcat - 'identity - '("DATA: errors" - "[9:0-9:2] WARNING Possible incorrect indentation: this token is offside of context started at position (2:16)." - "Try indenting this token further or using standard formatting conventions." - "[11:0-11:2] ERROR Unexpected symbol '[<' in expression" - "Followed by more stuff on this line" - "[12:0-12:3] WARNING Possible incorrect indentation: this token is offside of context started at position (2:16). -Try indenting this token further or using standard formatting conventions." - "<>" - "") - "\n") + "{\"Kind\":\"errors\",\"Data\":[{\"FileName\":\"/Users/robnea/dev/rneatherway-fsharpbinding/FSharp.AutoComplete/test/integration/Test1Json/Program.fs\",\"StartLine\":11,\"EndLine\":11,\"StartColumn\":0,\"EndColumn\":2,\"Severity\":\"Warning\",\"Message\":\"Possible incorrect indentation: this token is offside of context started at position (8:1). Try indenting this token further or using standard formatting conventions.\",\"Subcategory\":\"parse\"},{\"FileName\":\"/Users/robnea/dev/rneatherway-fsharpbinding/FSharp.AutoComplete/test/integration/Test1Json/Program.fs\",\"StartLine\":11,\"EndLine\":11,\"StartColumn\":0,\"EndColumn\":2,\"Severity\":\"Error\",\"Message\":\"Unexpected symbol '[<' in expression\",\"Subcategory\":\"parse\"},{\"FileName\":\"/Users/robnea/dev/rneatherway-fsharpbinding/FSharp.AutoComplete/test/integration/Test1Json/Program.fs\",\"StartLine\":12,\"EndLine\":12,\"StartColumn\":0,\"EndColumn\":3,\"Severity\":\"Warning\",\"Message\":\"Possible incorrect indentation: this token is offside of context started at position (8:1). Try indenting this token further or using standard formatting conventions.\",\"Subcategory\":\"parse\"}]}" "A list of errors containing a square bracket to check the parsing") (check "parses errors from given string" @@ -83,7 +72,7 @@ Try indenting this token further or using standard formatting conventions." (should (equal text (concat "Possible incorrect indentation: " "this token is offside of context started at " - "position (2:16)." + "position (8:1)." "\nTry indenting this token further or using standard " "formatting conventions."))))) @@ -146,28 +135,23 @@ function bound to VAR in BODY. " (flet ((,sym (x &rest xs) (setq ,var x))) ,@body))) -(defun format-output (header &optional content) - (concat header "\n" content - fsharp-ac-eom)) - (check-handler "prints message on error" (stub-fn message err - (fsharp-ac-filter-output nil (format-output "ERROR: foo")) + (fsharp-ac-filter-output nil ("{\"Kind\": \"ERROR\", \"Data\": \"foo\"")) (should-match "foo" err))) -(check-handler "does not print message on type information error" - (stub-fn message called - (fsharp-ac-filter-output nil (format-output "ERROR: could not get type information")) - (should-not called))) - ;;; Tooltips and typesigs +(defconst tooltip-msg + "{\"Kind\": \"tooltip\", \"Data\": \"foo\"" + "A simple tooltip message") + (check-handler "uses popup in terminal if tooltip is requested" (let ((fsharp-ac-use-popup t)) (flet ((display-graphic-p () nil)) (stub-fn popup-tip tip (fsharp-ac/show-tooltip-at-point) - (fsharp-ac-filter-output nil (format-output "DATA: tooltip" "foo")) + (fsharp-ac-filter-output nil tooltip-msg) (should-match "foo" tip))))) (check-handler "uses pos-tip in GUI if tooltip is requested" @@ -175,21 +159,21 @@ function bound to VAR in BODY. " (flet ((display-graphic-p () t)) (stub-fn pos-tip-show tip (fsharp-ac/show-tooltip-at-point) - (fsharp-ac-filter-output nil (format-output "DATA: tooltip" "foo")) + (fsharp-ac-filter-output nil tooltip-msg) (should-match "foo" tip))))) (check-handler "does not show popup if typesig is requested" (let ((fsharp-ac-use-popup t)) (stub-fn popup-tip called (fsharp-ac/show-typesig-at-point) - (fsharp-ac-filter-output nil (format-output "DATA: tooltip" "foo")) + (fsharp-ac-filter-output nil tooltip-msg) (should-not called)))) (check-handler "does not show popup if use-popup is nil" (let ((fsharp-ac-use-popup nil)) (stub-fn popup-tip called (fsharp-ac/show-tooltip-at-point) - (fsharp-ac-filter-output nil (format-output "DATA: tooltip" "foo")) + (fsharp-ac-filter-output nil tooltip-msg) (should-not called)))) (check-handler "displays tooltip in info window if use-popup is nil" @@ -198,11 +182,11 @@ function bound to VAR in BODY. " ;; with-help-window is a macro and macrolet and labels don't seem to work. (stub-fn help-window-setup win (fsharp-ac/show-tooltip-at-point) - (fsharp-ac-filter-output nil (format-output "DATA: tooltip" "foo")) + (fsharp-ac-filter-output nil tooltip-msg) (should-match "fsharp info" (buffer-name (window-buffer win)))))) (check-handler "displays typesig in minibuffer if typesig is requested" (stub-fn message sig (fsharp-ac/show-typesig-at-point) - (fsharp-ac-filter-output nil (format-output "DATA: tooltip" "foo")) + (fsharp-ac-filter-output nil tooltip-msg) (should= "foo" sig))) From 266767b354c2ed9247c9d596c4f42943e8629a53 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Wed, 30 Oct 2013 09:19:28 +0000 Subject: [PATCH 13/24] Fix remaining emacs tests --- emacs/test/fsharp-mode-completion-tests.el | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/emacs/test/fsharp-mode-completion-tests.el b/emacs/test/fsharp-mode-completion-tests.el index 15b5cd57..78be48cf 100644 --- a/emacs/test/fsharp-mode-completion-tests.el +++ b/emacs/test/fsharp-mode-completion-tests.el @@ -42,11 +42,16 @@ ;;; Error parsing (defconst err-brace-str - "{\"Kind\":\"errors\",\"Data\":[{\"FileName\":\"/Users/robnea/dev/rneatherway-fsharpbinding/FSharp.AutoComplete/test/integration/Test1Json/Program.fs\",\"StartLine\":11,\"EndLine\":11,\"StartColumn\":0,\"EndColumn\":2,\"Severity\":\"Warning\",\"Message\":\"Possible incorrect indentation: this token is offside of context started at position (8:1). Try indenting this token further or using standard formatting conventions.\",\"Subcategory\":\"parse\"},{\"FileName\":\"/Users/robnea/dev/rneatherway-fsharpbinding/FSharp.AutoComplete/test/integration/Test1Json/Program.fs\",\"StartLine\":11,\"EndLine\":11,\"StartColumn\":0,\"EndColumn\":2,\"Severity\":\"Error\",\"Message\":\"Unexpected symbol '[<' in expression\",\"Subcategory\":\"parse\"},{\"FileName\":\"/Users/robnea/dev/rneatherway-fsharpbinding/FSharp.AutoComplete/test/integration/Test1Json/Program.fs\",\"StartLine\":12,\"EndLine\":12,\"StartColumn\":0,\"EndColumn\":3,\"Severity\":\"Warning\",\"Message\":\"Possible incorrect indentation: this token is offside of context started at position (8:1). Try indenting this token further or using standard formatting conventions.\",\"Subcategory\":\"parse\"}]}" + "{\"Kind\":\"errors\",\"Data\":[{\"FileName\":\"/Users/robnea/dev/rneatherway-fsharpbinding/FSharp.AutoComplete/test/integration/Test1Json/Program.fs\",\"StartLine\":9,\"EndLine\":9,\"StartColumn\":0,\"EndColumn\":2,\"Severity\":\"Warning\",\"Message\":\"Possible incorrect indentation: this token is offside of context started at position (8:1). Try indenting this token further or using standard formatting conventions.\",\"Subcategory\":\"parse\"},{\"FileName\":\"/Users/robnea/dev/rneatherway-fsharpbinding/FSharp.AutoComplete/test/integration/Test1Json/Program.fs\",\"StartLine\":11,\"EndLine\":11,\"StartColumn\":0,\"EndColumn\":2,\"Severity\":\"Error\",\"Message\":\"Unexpected symbol '[<' in expression\",\"Subcategory\":\"parse\"},{\"FileName\":\"/Users/robnea/dev/rneatherway-fsharpbinding/FSharp.AutoComplete/test/integration/Test1Json/Program.fs\",\"StartLine\":12,\"EndLine\":12,\"StartColumn\":0,\"EndColumn\":3,\"Severity\":\"Warning\",\"Message\":\"Possible incorrect indentation: this token is offside of context started at position (8:1). Try indenting this token further or using standard formatting conventions.\",\"Subcategory\":\"parse\"}]}\n" "A list of errors containing a square bracket to check the parsing") (check "parses errors from given string" - (should= 3 (length (fsharp-ac-parse-errors err-brace-str)))) + (using-file (concat fs-file-dir "Program.fs") + (let ((json-array-type 'list) + (json-object-type 'hash-table) + (json-key-type 'string)) + (should= 3 (length (fsharp-ac-parse-errors + (gethash "Data" (json-read-from-string err-brace-str)))))))) (defmacro check-filter (desc &rest body) "Test properties of filtered output from the ac-process." @@ -72,8 +77,8 @@ (should (equal text (concat "Possible incorrect indentation: " "this token is offside of context started at " - "position (8:1)." - "\nTry indenting this token further or using standard " + "position (8:1). " + "Try indenting this token further or using standard " "formatting conventions."))))) (check-filter "first overlay should have the warning face" @@ -137,13 +142,13 @@ function bound to VAR in BODY. " (check-handler "prints message on error" (stub-fn message err - (fsharp-ac-filter-output nil ("{\"Kind\": \"ERROR\", \"Data\": \"foo\"")) + (fsharp-ac-filter-output nil "{\"Kind\": \"ERROR\", \"Data\": \"foo\"}\n") (should-match "foo" err))) ;;; Tooltips and typesigs (defconst tooltip-msg - "{\"Kind\": \"tooltip\", \"Data\": \"foo\"" + "{\"Kind\": \"tooltip\", \"Data\": \"foo\"}\n" "A simple tooltip message") (check-handler "uses popup in terminal if tooltip is requested" From 6a884760afda590e7cff668a25b3769dac653d4b Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Wed, 30 Oct 2013 17:33:17 +0000 Subject: [PATCH 14/24] Minor tidy up and test infrastructure tweaks --- FSharp.AutoComplete/Program.fs | 32 ++++--- .../test/integration/OutOfRange/output.txt | 8 +- .../RobustCommands/completenosuchfile.txt | 1 - .../test/integration/Test1Json/output.txt | 92 ++++++------------- .../test/integration/Timeouts/output.txt | 2 +- .../test/integration/runtests.sh | 2 +- 6 files changed, 54 insertions(+), 83 deletions(-) diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index b659c6b3..aa46bc8b 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -491,12 +491,17 @@ module internal Main = match parseCommand(Console.ReadLine()) with | GetErrors -> let errs = agent.GetErrors() - let errstrings = - [ for e in errs do - yield sprintf "[%d:%d-%d:%d] %s %s" e.StartLine e.StartColumn e.EndLine e.EndColumn - (if e.Severity = Severity.Error then "ERROR" else "WARNING") e.Message ] match state.OutputMode with - | Text -> printfn "DATA: errors\n%s\n<>" (String.concat "\n" errstrings) + | Text -> + let sb = new System.Text.StringBuilder() + sb.AppendLine("DATA: errors") |> ignore + for e in errs do + sb.AppendLine(sprintf "[%d:%d-%d:%d] %s %s" e.StartLine e.StartColumn e.EndLine e.EndColumn + (if e.Severity = Severity.Error then "ERROR" else "WARNING") e.Message) + |> ignore + sb.Append("<>") |> ignore + Console.WriteLine(sb.ToString()) + | Json -> prAsJson { Kind = "errors"; Data = errs } main state @@ -541,15 +546,16 @@ module internal Main = let file = Path.GetFullPath file if parsed file then let decls = agent.GetDeclarations(getoptions file) - let declstrings = - [ for tld in decls do - let (s1, e1), (s2, e2) = tld.Declaration.Range - yield sprintf "[%d:%d-%d:%d] %s" e1 s1 e2 s2 tld.Declaration.Name - for d in tld.Nested do - let (s1, e1), (s2, e2) = d.Range - yield sprintf " - [%d:%d-%d:%d] %s" e1 s1 e2 s2 d.Name ] match state.OutputMode with - | Text -> printfn "DATA: declarations\n%s\n<>" (String.concat "\n" declstrings) + | Text -> + let declstrings = + [ for tld in decls do + let (s1, e1), (s2, e2) = tld.Declaration.Range + yield sprintf "[%d:%d-%d:%d] %s" e1 s1 e2 s2 tld.Declaration.Name + for d in tld.Nested do + let (s1, e1), (s2, e2) = d.Range + yield sprintf " - [%d:%d-%d:%d] %s" e1 s1 e2 s2 d.Name ] + printfn "DATA: declarations\n%s\n<>" (String.concat "\n" declstrings) | Json -> prAsJson { Kind = "declarations"; Data = decls } main state diff --git a/FSharp.AutoComplete/test/integration/OutOfRange/output.txt b/FSharp.AutoComplete/test/integration/OutOfRange/output.txt index 83dd97f6..395691da 100644 --- a/FSharp.AutoComplete/test/integration/OutOfRange/output.txt +++ b/FSharp.AutoComplete/test/integration/OutOfRange/output.txt @@ -249,18 +249,18 @@ module XA from Script <> -ERROR: No tooltip information +INFO: No tooltip information <> -ERROR: No tooltip information +INFO: No tooltip information <> ERROR: Position is out of range <> DATA: finddecl /test/integration/OutOfRange/Script.fsx:2:7 <> -ERROR: Could not get find declaration +ERROR: Could not find declaration <> -ERROR: Could not get find declaration +ERROR: Could not find declaration <> ERROR: Position is out of range <> diff --git a/FSharp.AutoComplete/test/integration/RobustCommands/completenosuchfile.txt b/FSharp.AutoComplete/test/integration/RobustCommands/completenosuchfile.txt index 35045b87..e0a722b1 100644 --- a/FSharp.AutoComplete/test/integration/RobustCommands/completenosuchfile.txt +++ b/FSharp.AutoComplete/test/integration/RobustCommands/completenosuchfile.txt @@ -4,4 +4,3 @@ DATA: project <> ERROR: File '/test/integration/RobustCommands/NoSuchFile.fs' not parsed <> - diff --git a/FSharp.AutoComplete/test/integration/Test1Json/output.txt b/FSharp.AutoComplete/test/integration/Test1Json/output.txt index 77fd3cc0..da67658c 100644 --- a/FSharp.AutoComplete/test/integration/Test1Json/output.txt +++ b/FSharp.AutoComplete/test/integration/Test1Json/output.txt @@ -1,63 +1,29 @@ -DATA: project -/Users/robnea/dev/rneatherway-fsharpbinding/FSharp.AutoComplete/test/integration/Test1Json/FileTwo.fs -/Users/robnea/dev/rneatherway-fsharpbinding/FSharp.AutoComplete/test/integration/Test1Json/Program.fs -<> -INFO: Background parsing started -<> -INFO: Background parsing started -<> -INFO: Background parsing started -<> -{"Kind":"completion","Data":{"Completions":["funky"],"HelpText":{"funky":"val funky : x:int -> int"}}} -{"Kind":"helptext","Data":{"HelpText":{"funky":"val funky : x:int -> int"}}} -{"Kind":"completion","Data":{"Completions":["Equals","GetHashCode","GetType","Terrific","ToString"],"HelpText":{"Equals":"System.Object.Equals(obj: obj) : bool"}}} -{"Kind":"helptext","Data":{"HelpText":{"Equals":"System.Object.Equals(obj: obj) : bool","GetHashCode":"System.Object.GetHashCode() : int","GetType":"System.Object.GetType() : System.Type","Terrific":"member FileTwo.NewObjectType.Terrific : y:int -> int","ToString":"System.Object.ToString() : string"}}} -{"Kind":"completion","Data":{"Completions":["Bar","Foo","NewObjectType","Qux","add","addition"],"HelpText":{"Bar":"union case FileTwo.Foo.Bar: FileTwo.Foo"}}} -{"Kind":"helptext","Data":{"HelpText":{"Bar":"union case FileTwo.Foo.Bar: FileTwo.Foo","Foo":"type Foo =\n | Bar\n | Qux","NewObjectType":"Multiple items\ntype NewObjectType =\n new : unit -> NewObjectType\n member Terrific : y:int -> int\n\n--------------------\n\nnew : unit -> FileTwo.NewObjectType","Qux":"union case FileTwo.Foo.Qux: FileTwo.Foo","add":"val add : x:int -> y:int -> int","addition":"val addition : x:int -> y:int -> int"}}} -{"Kind":"completion","Data":{"Completions":["func"],"HelpText":{"func":"val func : x:int -> int"}}} -{"Kind":"helptext","Data":{"HelpText":{"func":"val func : x:int -> int"}}} -{"Kind":"completion","Data":{"Completions":["Bar","Foo","NewObjectType","Qux","add","addition"],"HelpText":{"Bar":"union case FileTwo.Foo.Bar: FileTwo.Foo"}}} -{"Kind":"helptext","Data":{"HelpText":{"Bar":"union case FileTwo.Foo.Bar: FileTwo.Foo","Foo":"type Foo =\n | Bar\n | Qux","NewObjectType":"Multiple items\ntype NewObjectType =\n new : unit -> NewObjectType\n member Terrific : y:int -> int\n\n--------------------\n\nnew : unit -> FileTwo.NewObjectType","Qux":"union case FileTwo.Foo.Qux: FileTwo.Foo","add":"val add : x:int -> y:int -> int","addition":"val addition : x:int -> y:int -> int"}}} -DATA: tooltip -val add : x:int -> y:int -> int - -Full name: FileTwo.add -<> -DATA: tooltip -val func : x:int -> int - -Full name: Program.X.func -<> -DATA: tooltip -val testval : FileTwo.NewObjectType - -Full name: Program.testval -<> -DATA: tooltip -val funky : x:int -> int - -Full name: Script.XA.funky -<> -DATA: declarations -[1:0-19:0] Program - - [13:4-15:5] main -[1:0-2:20] X - - [2:6-2:20] func -<> -DATA: declarations -[1:0-14:5] FileTwo - - [9:4-9:19] add - - [7:4-7:24] addition -[3:5-5:7] Foo - - [4:4-4:7] Bar - - [5:4-5:7] Qux -[11:5-14:5] NewObjectType - - [13:11-14:5] Terrific -<> -DATA: declarations -[3:0-10:0] Script -[3:0-4:21] XA - - [4:6-4:21] funky -<> -DATA: errors -<> +{"Kind":"project","Data":["/test/integration/Test1Json/FileTwo.fs","/test/integration/Test1Json/Program.fs"]} +{"Kind":"INFO","Data":"Background parsing started"} +{"Kind":"INFO","Data":"Background parsing started"} +{"Kind":"INFO","Data":"Background parsing started"} +{"Kind":"helptext","Data":{"funky":"val funky : x:int -> int"}} +{"Kind":"completion","Data":["funky"]} +{"Kind":"helptext","Data":{"funky":"val funky : x:int -> int"}} +{"Kind":"helptext","Data":{"Equals":"System.Object.Equals(obj: obj) : bool"}} +{"Kind":"completion","Data":["Equals","GetHashCode","GetType","Terrific","ToString"]} +{"Kind":"helptext","Data":{"Equals":"System.Object.Equals(obj: obj) : bool","GetHashCode":"System.Object.GetHashCode() : int","GetType":"System.Object.GetType() : System.Type","Terrific":"member FileTwo.NewObjectType.Terrific : y:int -> int","ToString":"System.Object.ToString() : string"}} +{"Kind":"helptext","Data":{"Bar":"union case FileTwo.Foo.Bar: FileTwo.Foo"}} +{"Kind":"completion","Data":["Bar","Foo","NewObjectType","Qux","add","addition"]} +{"Kind":"helptext","Data":{"Bar":"union case FileTwo.Foo.Bar: FileTwo.Foo","Foo":"type Foo =\n | Bar\n | Qux","NewObjectType":"Multiple items\ntype NewObjectType =\n new : unit -> NewObjectType\n member Terrific : y:int -> int\n\n--------------------\n\nnew : unit -> FileTwo.NewObjectType","Qux":"union case FileTwo.Foo.Qux: FileTwo.Foo","add":"val add : x:int -> y:int -> int","addition":"val addition : x:int -> y:int -> int"}} +{"Kind":"helptext","Data":{"func":"val func : x:int -> int"}} +{"Kind":"completion","Data":["func"]} +{"Kind":"helptext","Data":{"func":"val func : x:int -> int"}} +{"Kind":"helptext","Data":{"Bar":"union case FileTwo.Foo.Bar: FileTwo.Foo"}} +{"Kind":"completion","Data":["Bar","Foo","NewObjectType","Qux","add","addition"]} +{"Kind":"helptext","Data":{"Bar":"union case FileTwo.Foo.Bar: FileTwo.Foo","Foo":"type Foo =\n | Bar\n | Qux","NewObjectType":"Multiple items\ntype NewObjectType =\n new : unit -> NewObjectType\n member Terrific : y:int -> int\n\n--------------------\n\nnew : unit -> FileTwo.NewObjectType","Qux":"union case FileTwo.Foo.Qux: FileTwo.Foo","add":"val add : x:int -> y:int -> int","addition":"val addition : x:int -> y:int -> int"}} +{"Kind":"tooltip","Data":"val add : x:int -> y:int -> int\n\nFull name: FileTwo.add"} +{"Kind":"tooltip","Data":"val func : x:int -> int\n\nFull name: Program.X.func"} +{"Kind":"tooltip","Data":"val testval : FileTwo.NewObjectType\n\nFull name: Program.testval"} +{"Kind":"tooltip","Data":"val funky : x:int -> int\n\nFull name: Script.XA.funky"} +{"Kind":"finddecl","Data":{"File":"/test/integration/Test1Json/FileTwo.fs","Line":12,"Column":11}} +{"Kind":"finddecl","Data":{"File":"/test/integration/Test1Json/Script.fsx","Line":3,"Column":6}} +{"Kind":"declarations","Data":[{"Declaration":{"UniqueName":"Program_1_of_1","Name":"Program","Glyph":84,"Kind":{},"Range":{"Item1":{"Item1":0,"Item2":1},"Item2":{"Item1":0,"Item2":19}},"BodyRange":{"Item1":{"Item1":0,"Item2":1},"Item2":{"Item1":5,"Item2":15}},"IsSingleTopLevel":true},"Nested":[{"UniqueName":"Program_1_of_1","Name":"main","Glyph":6,"Kind":{},"Range":{"Item1":{"Item1":4,"Item2":13},"Item2":{"Item1":5,"Item2":15}},"BodyRange":{"Item1":{"Item1":4,"Item2":13},"Item2":{"Item1":5,"Item2":15}},"IsSingleTopLevel":false}]},{"Declaration":{"UniqueName":"X_1_of_1","Name":"X","Glyph":84,"Kind":{},"Range":{"Item1":{"Item1":0,"Item2":1},"Item2":{"Item1":20,"Item2":2}},"BodyRange":{"Item1":{"Item1":8,"Item2":1},"Item2":{"Item1":20,"Item2":2}},"IsSingleTopLevel":false},"Nested":[{"UniqueName":"X_1_of_1","Name":"func","Glyph":6,"Kind":{},"Range":{"Item1":{"Item1":6,"Item2":2},"Item2":{"Item1":20,"Item2":2}},"BodyRange":{"Item1":{"Item1":6,"Item2":2},"Item2":{"Item1":20,"Item2":2}},"IsSingleTopLevel":false}]}]} +{"Kind":"declarations","Data":[{"Declaration":{"UniqueName":"FileTwo_1_of_1","Name":"FileTwo","Glyph":84,"Kind":{},"Range":{"Item1":{"Item1":0,"Item2":1},"Item2":{"Item1":5,"Item2":14}},"BodyRange":{"Item1":{"Item1":14,"Item2":1},"Item2":{"Item1":5,"Item2":14}},"IsSingleTopLevel":true},"Nested":[{"UniqueName":"FileTwo_1_of_1","Name":"add","Glyph":6,"Kind":{},"Range":{"Item1":{"Item1":4,"Item2":9},"Item2":{"Item1":19,"Item2":9}},"BodyRange":{"Item1":{"Item1":4,"Item2":9},"Item2":{"Item1":19,"Item2":9}},"IsSingleTopLevel":false},{"UniqueName":"FileTwo_1_of_1","Name":"addition","Glyph":6,"Kind":{},"Range":{"Item1":{"Item1":4,"Item2":7},"Item2":{"Item1":24,"Item2":7}},"BodyRange":{"Item1":{"Item1":4,"Item2":7},"Item2":{"Item1":24,"Item2":7}},"IsSingleTopLevel":false}]},{"Declaration":{"UniqueName":"Foo_1_of_1","Name":"Foo","Glyph":132,"Kind":{},"Range":{"Item1":{"Item1":5,"Item2":3},"Item2":{"Item1":7,"Item2":5}},"BodyRange":{"Item1":{"Item1":2,"Item2":4},"Item2":{"Item1":7,"Item2":5}},"IsSingleTopLevel":false},"Nested":[{"UniqueName":"Foo_1_of_1","Name":"Bar","Glyph":144,"Kind":{},"Range":{"Item1":{"Item1":4,"Item2":4},"Item2":{"Item1":7,"Item2":4}},"BodyRange":{"Item1":{"Item1":4,"Item2":4},"Item2":{"Item1":7,"Item2":4}},"IsSingleTopLevel":false},{"UniqueName":"Foo_1_of_1","Name":"Qux","Glyph":144,"Kind":{},"Range":{"Item1":{"Item1":4,"Item2":5},"Item2":{"Item1":7,"Item2":5}},"BodyRange":{"Item1":{"Item1":4,"Item2":5},"Item2":{"Item1":7,"Item2":5}},"IsSingleTopLevel":false}]},{"Declaration":{"UniqueName":"NewObjectType_1_of_1","Name":"NewObjectType","Glyph":0,"Kind":{},"Range":{"Item1":{"Item1":5,"Item2":11},"Item2":{"Item1":5,"Item2":14}},"BodyRange":{"Item1":{"Item1":2,"Item2":13},"Item2":{"Item1":5,"Item2":14}},"IsSingleTopLevel":false},"Nested":[{"UniqueName":"NewObjectType_1_of_1","Name":"Terrific","Glyph":72,"Kind":{},"Range":{"Item1":{"Item1":11,"Item2":13},"Item2":{"Item1":5,"Item2":14}},"BodyRange":{"Item1":{"Item1":11,"Item2":13},"Item2":{"Item1":5,"Item2":14}},"IsSingleTopLevel":false}]}]} +{"Kind":"declarations","Data":[{"Declaration":{"UniqueName":"Script_1_of_1","Name":"Script","Glyph":84,"Kind":{},"Range":{"Item1":{"Item1":0,"Item2":3},"Item2":{"Item1":0,"Item2":10}},"BodyRange":{"Item1":{"Item1":0,"Item2":1},"Item2":{"Item1":21,"Item2":4}},"IsSingleTopLevel":true},"Nested":[]},{"Declaration":{"UniqueName":"XA_1_of_1","Name":"XA","Glyph":84,"Kind":{},"Range":{"Item1":{"Item1":0,"Item2":3},"Item2":{"Item1":21,"Item2":4}},"BodyRange":{"Item1":{"Item1":9,"Item2":3},"Item2":{"Item1":21,"Item2":4}},"IsSingleTopLevel":false},"Nested":[{"UniqueName":"XA_1_of_1","Name":"funky","Glyph":6,"Kind":{},"Range":{"Item1":{"Item1":6,"Item2":4},"Item2":{"Item1":21,"Item2":4}},"BodyRange":{"Item1":{"Item1":6,"Item2":4},"Item2":{"Item1":21,"Item2":4}},"IsSingleTopLevel":false}]}]} +{"Kind":"errors","Data":[]} diff --git a/FSharp.AutoComplete/test/integration/Timeouts/output.txt b/FSharp.AutoComplete/test/integration/Timeouts/output.txt index 61ca2f82..26a6212c 100644 --- a/FSharp.AutoComplete/test/integration/Timeouts/output.txt +++ b/FSharp.AutoComplete/test/integration/Timeouts/output.txt @@ -6,7 +6,7 @@ INFO: Background parsing started <> ERROR: Could not get type information <> -INFO: Could not fetch tooltip +INFO: No tooltip information <> DATA: completion Equals diff --git a/FSharp.AutoComplete/test/integration/runtests.sh b/FSharp.AutoComplete/test/integration/runtests.sh index dd6704a2..0255fd5a 100755 --- a/FSharp.AutoComplete/test/integration/runtests.sh +++ b/FSharp.AutoComplete/test/integration/runtests.sh @@ -2,5 +2,5 @@ cd `dirname $0` find . -name "*Runner.fsx" | xargs -L1 -P1 -t fsharpi --exec -find . -name "*.txt" | xargs perl -p -i -e "s/\/.*FSharp.AutoComplete\/test\/(.*)/\/test\/\$1/" +find . -name "*.txt" | xargs perl -p -i -e "s/\/.*?FSharp.AutoComplete\/test\/(.*?(\"|\$))/\/test\/\$1/g" git status . From b007db3c970f489dbf09341d3df5b199cdb47b4a Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Thu, 31 Oct 2013 21:18:50 +0000 Subject: [PATCH 15/24] Pull in better parsing from Monodevelop binding --- FSharp.AutoComplete/Parser.fs | 99 +++++++++++++++++----------------- FSharp.AutoComplete/Program.fs | 45 +++++++++------- 2 files changed, 77 insertions(+), 67 deletions(-) mode change 100644 => 100755 FSharp.AutoComplete/Parser.fs diff --git a/FSharp.AutoComplete/Parser.fs b/FSharp.AutoComplete/Parser.fs old mode 100644 new mode 100755 index ccbce471..b8a1102e --- a/FSharp.AutoComplete/Parser.fs +++ b/FSharp.AutoComplete/Parser.fs @@ -1,4 +1,4 @@ -// -------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------------------- // (c) Tomas Petricek, http://tomasp.net/blog // -------------------------------------------------------------------------------------- #nowarn "40" // recursive references checked at runtime @@ -7,6 +7,7 @@ namespace FSharp.InteractiveAutocomplete open System open Microsoft.FSharp.Compiler.SourceCodeServices +open Microsoft.FSharp.Compiler open System.Globalization // -------------------------------------------------------------------------------------- @@ -43,7 +44,7 @@ module Parser = type Parser<'T> = P of (LazyList -> ('T * LazyList) list) - // Basic functions needed by the computation builed + // Basic functions needed by the computation builder let result v = P(fun c -> [v, c]) let zero () = P(fun c -> []) @@ -54,6 +55,8 @@ module Parser = let plus (P p) (P q) = P (fun inp -> (p inp) @ (q inp) ) + let (<|>) p1 p2 = plus p1 p2 + type ParserBuilder() = member x.Bind(v, f) = bind v f member x.Zero() = zero() @@ -69,32 +72,26 @@ module Parser = let item = P(function | LazyList.Nil -> [] | LazyList.Cons(c, r) -> [c,r.Value]) - let (<|>) (P p1) (P p2) = - P(fun input -> - let res1 = p1 input - let res2 = p2 input - res1 @ res2) - - let sequence (P p) (P q) = P (fun inp -> + let sequence (P p) (P q) = P (fun inp -> [ for (pr, inp') in p inp do - for (qr, inp'') in q inp' do + for (qr, inp'') in q inp' do yield (pr, qr), inp'']) - let sat p = parser { - let! v = item + let sat p = parser { + let! v = item if (p v) then return v } let char x = sat ((=) x) - let digit = sat Char.IsDigit + let digit = sat Char.IsDigit let lower = sat Char.IsLower let upper = sat Char.IsUpper let letter = sat Char.IsLetter let nondigit = sat (Char.IsDigit >> not) let whitespace = sat (Char.IsWhiteSpace) - let alphanum = parser { + let alphanum = parser { return! letter - return! digit } + return! digit } let rec word = parser { return [] @@ -103,21 +100,21 @@ module Parser = let! xs = word return x::xs } } - let string (str:string) = + let string (str:string) = let chars = str.ToCharArray() |> List.ofSeq let rec string' = function | [] -> result [] - | x::xs -> parser { + | x::xs -> parser { let! y = char x - let! ys = string' xs + let! ys = string' xs return y::ys } string' chars let rec many p = parser { - return! parser { + return! parser { let! it = p let! res = many p - return it::res } + return it::res } return [] } let rec some p = parser { @@ -125,14 +122,19 @@ module Parser = let! rest = many p return first::rest } - let rec map f p = parser { - let! v = p + let rec map f p = parser { + let! v = p return f v } - let apply (P p) (str:seq) = + let optional p = parser { + return! parser { let! v = p in return Some v } + return None } + + let apply (P p) (str:seq) = let res = str |> LazyList.ofSeq |> p res |> List.map fst + // -------------------------------------------------------------------------------------- // Parsing utilities for IntelliSense // -------------------------------------------------------------------------------------- @@ -142,30 +144,20 @@ module Parser = module Parsing = open Parser - module PrettyNaming = - let IsIdentifierPartCharacter (c:char) = - let cat = System.Char.GetUnicodeCategory(c) - cat = UnicodeCategory.UppercaseLetter || - cat = UnicodeCategory.LowercaseLetter || - cat = UnicodeCategory.TitlecaseLetter || - cat = UnicodeCategory.ModifierLetter || - cat = UnicodeCategory.OtherLetter || - cat = UnicodeCategory.LetterNumber || - cat = UnicodeCategory.DecimalDigitNumber || - cat = UnicodeCategory.ConnectorPunctuation || - cat = UnicodeCategory.NonSpacingMark || - cat = UnicodeCategory.SpacingCombiningMark || c = '\'' - - /// Parses F# short-identifier (i.e. not including '.'); also ignores active patterns let parseIdent = many (sat PrettyNaming.IsIdentifierPartCharacter) |> map String.ofSeq + let fsharpIdentCharacter = sat PrettyNaming.IsIdentifierPartCharacter /// Parse F# short-identifier and reverse the resulting string - let parseBackIdent = - many (sat PrettyNaming.IsIdentifierPartCharacter) |> map String.ofReversedSeq - - /// Parse remainder of a logn identifier before '.' (e.g. "Name.space.") + let parseBackIdent = + parser { + let! x = optional (string "``") + let! res = many (if x.IsSome then (whitespace <|> fsharpIdentCharacter) else fsharpIdentCharacter) |> map String.ofReversedSeq + let! _ = optional (string "``") + return res } + + /// Parse remainder of a long identifier before '.' (e.g. "Name.space.") /// (designed to look backwards - reverses the results after parsing) let rec parseBackLongIdentRest = parser { return! parser { @@ -173,16 +165,18 @@ module Parsing = let! ident = parseBackIdent let! rest = parseBackLongIdentRest return ident::rest } - return [] } - - /// Parse long identifier with residue (backwards) (e.g. "Console.Wri") + return [] } + + /// Parse long identifier with residue (backwards) (e.g. "Debug.Wri") /// and returns it as a tuple (reverses the results after parsing) let parseBackIdentWithResidue = parser { - let! residue = many alphanum |> map String.ofReversedSeq + let! residue = many fsharpIdentCharacter + let residue = String.ofReversedSeq residue + let! _ = optional (string "``") return! parser { let! long = parseBackLongIdentRest return residue, long |> List.rev } - return residue, [] } + return residue, [] } /// Parse long identifier and return it as a list (backwards, reversed) let parseBackLongIdent = parser { @@ -192,13 +186,20 @@ module Parsing = return ident::rest |> List.rev } return [] } + let parseBackTriggerThenLongIdent = parser { + let! _ = (char '(' <|> char '<') + let! _ = many whitespace + return! parseBackLongIdent + } + /// Create sequence that reads the string backwards - let createBackStringReader (str:string) from = seq { + let createBackStringReader (str:string) from = seq { for i in (min from (str.Length - 1)) .. -1 .. 0 do yield str.[i] } /// Create sequence that reads the string forwards - let createForwardStringReader (str:string) from = seq { + let createForwardStringReader (str:string) from = seq { for i in (max 0 from) .. (str.Length - 1) do yield str.[i] } /// Returns first result returned by the parser let getFirst p s = apply p s |> List.head + let tryGetFirst p s = match apply p s with h::_ -> Some h | [] -> None diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index aa46bc8b..e2ef5baa 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -234,24 +234,33 @@ type internal IntelliSenseAgent() = with :? System.TimeoutException as e -> None) info - member private x.FindLongIdents(lineStr, column) = - // Parsing - find the identifier around the current location - // (we look for full identifier in the backward direction, but only - // for a short identifier forward - this means that when you hover - // 'B' in 'A.B.C', you will get intellisense for 'A.B' module) - let lookBack = Parsing.createBackStringReader lineStr column - let lookForw = Parsing.createForwardStringReader lineStr (column + 1) - let backIdent = Parsing.getFirst Parsing.parseBackLongIdent lookBack - let nextIdent = Parsing.getFirst Parsing.parseIdent lookForw - - let identIsland = - match List.rev backIdent with - | last::prev -> (last + nextIdent)::prev |> List.rev - | [] -> [] - - match identIsland with - | [ "" ] -> None - | _ -> Some identIsland + member private x.FindLongIdents(lineStr, col) = + // Parsing - find the identifier around the current location + // (we look for full identifier in the backward direction, but only + // for a short identifier forward - this means that when you hover + // 'B' in 'A.B.C', you will get intellisense for 'A.B' module) + let lookBack = createBackStringReader lineStr (col-1) + let lookForw = createForwardStringReader lineStr col + + let backIdentOpt = tryGetFirst parseBackLongIdent lookBack + match backIdentOpt with + | None -> None + | Some backIdent -> + let nextIdentOpt = tryGetFirst parseIdent lookForw + match nextIdentOpt with + | None -> None + | Some nextIdent -> + + let currentIdent, identIsland = + match List.rev backIdent with + | last::prev -> + let current = last + nextIdent + current, current::prev |> List.rev + | [] -> "", [] + + match identIsland with + | [] | [ "" ] -> None + | _ -> Some identIsland /// Gets ToolTip for the specified location (and prints it to the output) member x.GetToolTip(opts, ((line, column) as pos), lineStr, time) : Option = From 9cb626f2a6c8a837460edc7c2a877ac685d800d4 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Thu, 31 Oct 2013 21:41:36 +0000 Subject: [PATCH 16/24] Add guard on overlay display --- emacs/fsharp-mode-completion.el | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/emacs/fsharp-mode-completion.el b/emacs/fsharp-mode-completion.el index 23810c30..d538c537 100644 --- a/emacs/fsharp-mode-completion.el +++ b/emacs/fsharp-mode-completion.el @@ -348,7 +348,7 @@ The current buffer must be an F# file that exists on disk." ;;; ---------------------------------------------------------------------------- ;;; Errors and Overlays -(defstruct fsharp-error start end face text) +(defstruct fsharp-error start end face text file) (defvar fsharp-ac-errors) (make-local-variable 'fsharp-ac-errors) @@ -386,11 +386,13 @@ possibly many lines of description.") 'fsharp-error-face 'fsharp-warning-face)) (msg (gethash "Message" err)) + (file (gethash "FileName" err)) ) (add-to-list 'parsed (make-fsharp-error :start beg :end end :face face - :text msg))))))) + :text msg + :file file))))))) (defun fsharp-ac/show-error-overlay (err) "Draw overlays in the current buffer to represent fsharp-error ERR." @@ -402,11 +404,14 @@ possibly many lines of description.") (end (fsharp-error-end err)) (face (fsharp-error-face err)) (txt (fsharp-error-text err)) + (file (fsharp-error-text err)) (ofaces (mapcar (lambda (o) (overlay-get o 'face)) (overlays-in beg end))) ) - (unless (and (eq face 'fsharp-warning-face) - (memq 'fsharp-error-face ofaces)) + (unless (or (string= (expand-file-name buffer-file-name) + (expand-file-name file)) + (and (eq face 'fsharp-warning-face) + (memq 'fsharp-error-face ofaces))) (when (and (eq face 'fsharp-error-face) (memq 'fsharp-warning-face ofaces)) From 5200d1d43e8bc2317ee611c784fc2bb8297efcc5 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Thu, 31 Oct 2013 21:52:31 +0000 Subject: [PATCH 17/24] Test output update --- .../test/integration/OutOfRange/output.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/FSharp.AutoComplete/test/integration/OutOfRange/output.txt b/FSharp.AutoComplete/test/integration/OutOfRange/output.txt index 395691da..2c619329 100644 --- a/FSharp.AutoComplete/test/integration/OutOfRange/output.txt +++ b/FSharp.AutoComplete/test/integration/OutOfRange/output.txt @@ -249,7 +249,10 @@ module XA from Script <> -INFO: No tooltip information +DATA: tooltip +module XA + +from Script <> INFO: No tooltip information <> @@ -258,7 +261,8 @@ ERROR: Position is out of range DATA: finddecl /test/integration/OutOfRange/Script.fsx:2:7 <> -ERROR: Could not find declaration +DATA: finddecl +/test/integration/OutOfRange/Script.fsx:2:7 <> ERROR: Could not find declaration <> From 71471e75126034e5383e660a252db3356a0036ad Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Thu, 31 Oct 2013 23:58:04 +0000 Subject: [PATCH 18/24] Trial fix for issue 233 --- FSharp.AutoComplete/Parser.fs | 0 emacs/fsharp-mode-completion.el | 26 ++++++++++++++++++-------- 2 files changed, 18 insertions(+), 8 deletions(-) mode change 100755 => 100644 FSharp.AutoComplete/Parser.fs diff --git a/FSharp.AutoComplete/Parser.fs b/FSharp.AutoComplete/Parser.fs old mode 100755 new mode 100644 diff --git a/emacs/fsharp-mode-completion.el b/emacs/fsharp-mode-completion.el index d538c537..bd9d16f3 100644 --- a/emacs/fsharp-mode-completion.el +++ b/emacs/fsharp-mode-completion.el @@ -238,8 +238,11 @@ display in a help buffer instead.") )) (defun fsharp-ac-document (item) - (let* ((prop (gethash item fsharp-ac-current-helptext)) + (let* ((ticks (s-match "^``\\(.*\\)``$" item)) + (key (if ticks (cadr ticks) item)) + (prop (gethash key fsharp-ac-current-helptext)) (help (if prop prop "Loading documentation..."))) + (message "key was '%s'" key) (pos-tip-fill-string help popup-tip-max-width))) (defun fsharp-ac-candidate () @@ -353,12 +356,6 @@ The current buffer must be an F# file that exists on disk." (defvar fsharp-ac-errors) (make-local-variable 'fsharp-ac-errors) -(defconst fsharp-ac-error-regexp - "\\[\\([0-9]+\\):\\([0-9]+\\)-\\([0-9]+\\):\\([0-9]+\\)\\] \\(ERROR\\|WARNING\\) \\(.*\\(?:\n[^[].*\\)*\\)" - "Regexp to match errors that come from fsautocomplete. Each -starts with a character range for position and is followed by -possibly many lines of description.") - (defun fsharp-ac-request-errors () (when (fsharp-ac-can-make-request) (fsharp-ac-parse-current-buffer) @@ -528,6 +525,17 @@ around to the start of the buffer." (setq msg (fsharp-ac--get-msg proc))))) + +(defun fsharp-ac--isIdChar (c) + (let ((gc (get-char-code-property c 'general-category))) + (or + (-any? (lambda (x) (string= gc x)) '("Lu" "Ll" "Lt" "Lm" "Lo" "Nl" "Nd" "Pc" "Mn" "Mc")) + (eq c 39)))) + +(defun fsharp-ac--isNormalId (s) + (-all? (lambda (x) x) (mapcar 'isIdChar s))) + + (defun fsharp-ac-handle-completion (data) (case fsharp-ac-status (preempted @@ -536,7 +544,9 @@ around to the start of the buffer." (ac-update)) (otherwise - (setq fsharp-ac-current-candidate data + (setq fsharp-ac-current-candidate (-map (lambda (s) (if (fsharp-ac--isNormalId s) s + (s-append "``" (s-prepend "``" s)))) + data) fsharp-ac-status 'acknowledged) (fsharp-ac--ac-start :force-init t) (ac-update) From 3165f4d993110f77dfb99cce274c9538567277e8 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Sun, 3 Nov 2013 18:55:34 +0000 Subject: [PATCH 19/24] Correct typo --- emacs/fsharp-mode-completion.el | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/emacs/fsharp-mode-completion.el b/emacs/fsharp-mode-completion.el index bd9d16f3..e2ffe78a 100644 --- a/emacs/fsharp-mode-completion.el +++ b/emacs/fsharp-mode-completion.el @@ -109,6 +109,16 @@ display in a help buffer instead.") (with-current-buffer (find-file-noselect file) (fsharp-ac-parse-current-buffer))) + +(defun fsharp-ac--isIdChar (c) + (let ((gc (get-char-code-property c 'general-category))) + (or + (-any? (lambda (x) (string= gc x)) '("Lu" "Ll" "Lt" "Lm" "Lo" "Nl" "Nd" "Pc" "Mn" "Mc")) + (eq c 39)))) + +(defun fsharp-ac--isNormalId (s) + (-all? (lambda (x) x) (mapcar 'fsharp-ac--isIdChar s))) + ;;; ---------------------------------------------------------------------------- ;;; File Parsing and loading @@ -242,7 +252,6 @@ display in a help buffer instead.") (key (if ticks (cadr ticks) item)) (prop (gethash key fsharp-ac-current-helptext)) (help (if prop prop "Loading documentation..."))) - (message "key was '%s'" key) (pos-tip-fill-string help popup-tip-max-width))) (defun fsharp-ac-candidate () @@ -525,17 +534,6 @@ around to the start of the buffer." (setq msg (fsharp-ac--get-msg proc))))) - -(defun fsharp-ac--isIdChar (c) - (let ((gc (get-char-code-property c 'general-category))) - (or - (-any? (lambda (x) (string= gc x)) '("Lu" "Ll" "Lt" "Lm" "Lo" "Nl" "Nd" "Pc" "Mn" "Mc")) - (eq c 39)))) - -(defun fsharp-ac--isNormalId (s) - (-all? (lambda (x) x) (mapcar 'isIdChar s))) - - (defun fsharp-ac-handle-completion (data) (case fsharp-ac-status (preempted From 4ab8eb2e8f5722fc9ebc6e5e80122f17b4ecfb44 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Mon, 4 Nov 2013 19:07:04 +0000 Subject: [PATCH 20/24] Stub process functions properly in emacs unit test --- emacs/test/fsharp-mode-completion-tests.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/emacs/test/fsharp-mode-completion-tests.el b/emacs/test/fsharp-mode-completion-tests.el index 78be48cf..5555dc25 100644 --- a/emacs/test/fsharp-mode-completion-tests.el +++ b/emacs/test/fsharp-mode-completion-tests.el @@ -46,12 +46,14 @@ "A list of errors containing a square bracket to check the parsing") (check "parses errors from given string" - (using-file (concat fs-file-dir "Program.fs") + (stubbing-process-functions + (using-file + (concat fs-file-dir "Program.fs") (let ((json-array-type 'list) (json-object-type 'hash-table) (json-key-type 'string)) (should= 3 (length (fsharp-ac-parse-errors - (gethash "Data" (json-read-from-string err-brace-str)))))))) + (gethash "Data" (json-read-from-string err-brace-str))))))))) (defmacro check-filter (desc &rest body) "Test properties of filtered output from the ac-process." From 0a59ed8dfd91f494314b8cf68e5387aeba541093 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Mon, 4 Nov 2013 23:19:59 +0000 Subject: [PATCH 21/24] Remove "preempted" state and return correct helptext --- FSharp.AutoComplete/Program.fs | 18 +++++++++--------- emacs/fsharp-mode-completion.el | 33 +++++++++++---------------------- 2 files changed, 20 insertions(+), 31 deletions(-) diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index e2ef5baa..bf4c9ec3 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -216,8 +216,9 @@ type internal IntelliSenseAgent() = // Otherwise try to get type information & run the request agent.PostAndReply(fun r -> GetTypeCheckInfo(opts, time, r)) - /// Invokes dot-completion request and writes information to the standard output - member x.DoCompletion(opts : RequestOptions, ((line, column) as pos), lineStr, time) : Option = + /// Invokes dot-completion request. Returns possible completions + /// and current residue. + member x.DoCompletion(opts : RequestOptions, ((line, column) as pos), lineStr, time) : Option = let info = x.GetTypeCheckInfo(opts, time) Option.bind (fun (info: TypeCheckResults) -> // Get the long identifier before the current location @@ -230,7 +231,7 @@ type internal IntelliSenseAgent() = // Get items & generate output try Some (info.GetDeclarations(None, pos, lineStr, (longName, residue), fun (_,_) -> false) - |> Async.RunSynchronously) + |> Async.RunSynchronously, residue) with :? System.TimeoutException as e -> None) info @@ -578,7 +579,7 @@ module internal Main = let decls = agent.DoCompletion(opts, pos, state.Files.[file].[line], timeout) match decls with - | Some decls -> + | Some (decls, residue) -> match state.OutputMode with | Text -> printfn "DATA: completion" @@ -586,14 +587,13 @@ module internal Main = printfn "<>" | Json -> - let ds = List.sortBy (fun (d: Declaration) -> d.Name) [ for d in decls.Items do yield d ] let helptext = - match ds with - | [] -> Map.empty - | d::_ -> let tip = TipFormatter.formatTip d.DescriptionText - Map.add d.Name tip Map.empty + match List.tryFind (fun (d: Declaration) -> d.Name.StartsWith residue) ds with + | None -> Map.empty + | Some d -> let tip = TipFormatter.formatTip d.DescriptionText + Map.add d.Name tip Map.empty prAsJson { Kind = "helptext"; Data = helptext } diff --git a/emacs/fsharp-mode-completion.el b/emacs/fsharp-mode-completion.el index e2ffe78a..0a61a9ca 100644 --- a/emacs/fsharp-mode-completion.el +++ b/emacs/fsharp-mode-completion.el @@ -64,7 +64,7 @@ display in a help buffer instead.") ;;; Both in seconds. Note that background process uses ms. (defvar fsharp-ac-blocking-timeout 0.4) -(defvar fsharp-ac-idle-timeout 2) +(defvar fsharp-ac-idle-timeout 1) ;;; ---------------------------------------------------------------------------- @@ -273,10 +273,7 @@ display in a help buffer instead.") (acknowledged (setq fsharp-ac-status 'idle) - fsharp-ac-current-candidate) - - (preempted - nil))) + fsharp-ac-current-candidate))) (defun fsharp-ac-prefix () (or (ac-prefix-symbol) @@ -351,11 +348,10 @@ The current buffer must be an F# file that exists on disk." (defun fsharp-ac/complete-at-point () (interactive) - (if (and (fsharp-ac-can-make-request) + (when (and (fsharp-ac-can-make-request) (eq fsharp-ac-status 'idle) fsharp-ac-intellisense-enabled) - (fsharp-ac--ac-start) - (setq fsharp-ac-status 'preempted))) + (fsharp-ac--ac-start))) ;;; ---------------------------------------------------------------------------- ;;; Errors and Overlays @@ -535,20 +531,13 @@ around to the start of the buffer." (setq msg (fsharp-ac--get-msg proc))))) (defun fsharp-ac-handle-completion (data) - (case fsharp-ac-status - (preempted - (setq fsharp-ac-status 'idle) - (fsharp-ac--ac-start) - (ac-update)) - - (otherwise - (setq fsharp-ac-current-candidate (-map (lambda (s) (if (fsharp-ac--isNormalId s) s - (s-append "``" (s-prepend "``" s)))) - data) - fsharp-ac-status 'acknowledged) - (fsharp-ac--ac-start :force-init t) - (ac-update) - (setq fsharp-ac-status 'idle)))) + (setq fsharp-ac-current-candidate (-map (lambda (s) (if (fsharp-ac--isNormalId s) s + (s-append "``" (s-prepend "``" s)))) + data) + fsharp-ac-status 'acknowledged) + (fsharp-ac--ac-start :force-init t) + (ac-update) + (setq fsharp-ac-status 'idle)) (defun fsharp-ac-handle-doctext (data) (setq fsharp-ac-current-helptext data)) From 99755c25891c7f8b931e3cd895b4a02acc540115 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Mon, 4 Nov 2013 23:25:20 +0000 Subject: [PATCH 22/24] Add a "rescue" to idle timer to reenable completion --- emacs/fsharp-mode-completion.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/emacs/fsharp-mode-completion.el b/emacs/fsharp-mode-completion.el index 0a61a9ca..d9a0af68 100644 --- a/emacs/fsharp-mode-completion.el +++ b/emacs/fsharp-mode-completion.el @@ -364,7 +364,10 @@ The current buffer must be an F# file that exists on disk." (defun fsharp-ac-request-errors () (when (fsharp-ac-can-make-request) (fsharp-ac-parse-current-buffer) - (log-psendstr fsharp-ac-completion-process "errors\n"))) + (log-psendstr fsharp-ac-completion-process "errors\n")) + ; Perform some emergency fixup if things got out of sync + (when (not ac-completing) + (setq fsharp-ac-status 'idle))) (defun fsharp-ac-line-column-to-pos (line col) (save-excursion From bcb09f5b17c8222e07949313be7a39d1c7ce51a7 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Mon, 4 Nov 2013 23:32:45 +0000 Subject: [PATCH 23/24] Bump version numbers --- FSharp.AutoComplete/Options.fs | 2 +- FSharp.AutoComplete/Program.fs | 1 - emacs/fsharp-mode.el | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/FSharp.AutoComplete/Options.fs b/FSharp.AutoComplete/Options.fs index 6223e175..63afc304 100644 --- a/FSharp.AutoComplete/Options.fs +++ b/FSharp.AutoComplete/Options.fs @@ -6,7 +6,7 @@ namespace FSharp.InteractiveAutocomplete open System module Version = - let string = "FSharp.AutoComplete 0.6" + let string = "FSharp.AutoComplete 0.7" module Options = diff --git a/FSharp.AutoComplete/Program.fs b/FSharp.AutoComplete/Program.fs index bf4c9ec3..c058c041 100644 --- a/FSharp.AutoComplete/Program.fs +++ b/FSharp.AutoComplete/Program.fs @@ -5,7 +5,6 @@ namespace FSharp.InteractiveAutocomplete open System open System.IO -//open System.Collections.Generic open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.SourceCodeServices diff --git a/emacs/fsharp-mode.el b/emacs/fsharp-mode.el index 833aad4d..989faf89 100644 --- a/emacs/fsharp-mode.el +++ b/emacs/fsharp-mode.el @@ -29,7 +29,7 @@ (require 'fsharp-doc) (require 'inf-fsharp-mode) -(defconst fsharp-mode-version 0.9 +(defconst fsharp-mode-version 1.0 "Version of this fsharp-mode") ;;; Compilation From 13e505d50469b38c79e453a2c5311fd5dd586160 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Tue, 5 Nov 2013 18:16:22 +0000 Subject: [PATCH 24/24] Bump version numbers and change dependency order --- emacs/fsharp-mode-pkg.el | 8 ++++---- emacs/fsharp-mode.el | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/emacs/fsharp-mode-pkg.el b/emacs/fsharp-mode-pkg.el index 58e1687b..c38e7463 100644 --- a/emacs/fsharp-mode-pkg.el +++ b/emacs/fsharp-mode-pkg.el @@ -1,9 +1,9 @@ (define-package "fsharp-mode" - "0.9" + "1.0" "F# mode for Emacs" - '((popup "0.5") + '((auto-complete "1.4") + (popup "0.5") (pos-tip "0.4.5") (s "1.3.1") - (dash "1.1.0") - (auto-complete "1.4"))) + (dash "1.1.0"))) diff --git a/emacs/fsharp-mode.el b/emacs/fsharp-mode.el index 989faf89..da70eb04 100644 --- a/emacs/fsharp-mode.el +++ b/emacs/fsharp-mode.el @@ -6,7 +6,7 @@ ;; 2010-2011 Laurent Le Brun ;; Maintainer: Robin Neatherway ;; Keywords: languages -;; Version: 0.9 +;; Version: 1.0 ;; This file is not part of GNU Emacs.