diff --git a/src/IfSharp.Kernel/DirectivePreprocessor.fs b/src/IfSharp.Kernel/DirectivePreprocessor.fs new file mode 100644 index 0000000..87bf5d6 --- /dev/null +++ b/src/IfSharp.Kernel/DirectivePreprocessor.fs @@ -0,0 +1,27 @@ +namespace IfSharp.Kernel + +module DirectivePreprocessor = + + type Line = + | HelpDirective + | FSIOutputDirective + | NugetDirective + | Other + + let determineLineType (idx, (line:string)) = + match line.ToLower() with + | line when line.StartsWith "#n" -> NugetDirective + | line when line.StartsWith "#help" -> HelpDirective + | line when line.StartsWith "#fsioutput" -> FSIOutputDirective + | _ -> Other + + /// Separates into map of directive types + let partitionLines(lines : string[]) = + lines + |> Seq.mapi (fun (idx) (line) -> (idx, line)) + |> Seq.groupBy determineLineType + |> Map.ofSeq + + /// Parses a directive line. Example: #N "Deedle" + let parseDirectiveLine (prefix : string) (line : string) = + line.Substring(prefix.Length + 1).Trim().Trim('"') diff --git a/src/IfSharp.Kernel/Evaluation.fs b/src/IfSharp.Kernel/Evaluation.fs index 461d7e9..a63ad67 100644 --- a/src/IfSharp.Kernel/Evaluation.fs +++ b/src/IfSharp.Kernel/Evaluation.fs @@ -8,6 +8,7 @@ open Microsoft.FSharp.Compiler.Interactive.Shell [] module Evaluation = + let internal fsiout = ref false let internal sbOut = new StringBuilder() let internal sbErr = new StringBuilder() let internal inStream = new StringReader("") diff --git a/src/IfSharp.Kernel/IfSharp.Kernel.fsproj b/src/IfSharp.Kernel/IfSharp.Kernel.fsproj index 7ff32fc..bdf8d7a 100644 --- a/src/IfSharp.Kernel/IfSharp.Kernel.fsproj +++ b/src/IfSharp.Kernel/IfSharp.Kernel.fsproj @@ -78,6 +78,7 @@ + diff --git a/src/IfSharp.Kernel/Kernel.fs b/src/IfSharp.Kernel/Kernel.fs index 7ca8935..7fdcb9a 100644 --- a/src/IfSharp.Kernel/Kernel.fs +++ b/src/IfSharp.Kernel/Kernel.fs @@ -236,6 +236,27 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = let results = compiler.NuGetManager.Preprocess(code) let newCode = String.Join("\n", results.FilteredLines) + if not (Seq.isEmpty results.HelpLines) then + fsiEval.EvalInteraction("#help") + let ifsharpHelp = + """ IF# notebook directives: + + #fsioutput ["on"|"off"];; Toggle output display on/off + """ + let fsiHelp = sbOut.ToString() + pyout (ifsharpHelp + fsiHelp) + sbOut.Clear() |> ignore + + //This is a persistent toggle, just respect the last one + if not (Seq.isEmpty results.FsiOutputLines) then + let lastFsiOutput = Seq.last results.FsiOutputLines + if lastFsiOutput.ToLower().Contains("on") then + fsiout := true + else if lastFsiOutput.ToLower().Contains("off") then + fsiout := false + else + pyout (sprintf "Unreocognised fsioutput setting: %s" lastFsiOutput) + // do nuget stuff for package in results.Packages do if not (String.IsNullOrWhiteSpace(package.Error)) then @@ -256,6 +277,9 @@ type IfSharpKernel(connectionInformation : ConnectionInformation) = if not <| String.IsNullOrEmpty(newCode) then fsiEval.EvalInteraction(newCode) + + if fsiout.Value then + pyout (sbOut.ToString()) /// Handles an 'execute_request' message let executeRequest(msg : KernelMessage) (content : ExecuteRequest) = diff --git a/src/IfSharp.Kernel/NuGetManager.fs b/src/IfSharp.Kernel/NuGetManager.fs index 5777ccb..8c85207 100644 --- a/src/IfSharp.Kernel/NuGetManager.fs +++ b/src/IfSharp.Kernel/NuGetManager.fs @@ -59,6 +59,8 @@ type CustomErrorInfo = type PreprocessResults = { OriginalLines : string[]; + HelpLines : string[]; + FsiOutputLines : string[]; NuGetLines : string[]; FilteredLines : string[]; Packages : NuGetPackage[]; @@ -87,24 +89,6 @@ type CustomInstallCommand() = let semanticVersion = SemanticVersion(version) packageManager.LocalRepository.FindPackage(packageId, semanticVersion) -module NuGetManagerInternals = - - /// Separates a list of lines between into two partitions, the first list are the directive lines, second list is the other lines - let partitionLines(directive) (lines : string[]) = - lines - |> Seq.mapi (fun (idx) (line) -> (idx, line)) - |> Seq.toList - |> List.partition (fun (idx, line) -> line.StartsWith(directive)) - - /// Separates a list of lines between into two partitions, the first list are the directive lines, second list is the other lines - let partitionSource(directive) (source : string) = - let delimiters = [|"\r\n"; "\n"; "\r";|] - partitionLines directive (source.Split(delimiters, StringSplitOptions.None)) - - /// Parses a directive line. Example: #N "Deedle" - let parseDirectiveLine (prefix : string) (line : string) = - line.Substring(prefix.Length + 1).Trim().Trim('"') - /// The NuGetManager class contains methods for downloading nuget packages and such type NuGetManager (executingDirectory : string) = @@ -231,7 +215,7 @@ type NuGetManager (executingDirectory : string) = /// prerelease should be used or not. member this.ParseNugetLine (line : string) = - let contents = NuGetManagerInternals.parseDirectiveLine "#N" line + let contents = DirectivePreprocessor.parseDirectiveLine "#N" line if contents.Contains("/") then let splits = contents.Split([| '/' |]) if splits.Length > 2 then @@ -246,7 +230,15 @@ type NuGetManager (executingDirectory : string) = // split the source code into lines, then get the nuget lines let lines = source.Split('\n') - let (nugetLines, otherLines) = NuGetManagerInternals.partitionLines "#N" lines + let linesSplit = DirectivePreprocessor.partitionLines lines + + let orEmpty key = let opt = Map.tryFind key linesSplit + if opt.IsSome then opt.Value else Seq.empty + + let helpLines = DirectivePreprocessor.Line.HelpDirective |> orEmpty + let fsiOutputLines = DirectivePreprocessor.Line.FSIOutputDirective |> orEmpty + let nugetLines = DirectivePreprocessor.Line.NugetDirective |> orEmpty + let otherLines = DirectivePreprocessor.Line.Other |> orEmpty // parse the nuget lines and then download the packages let nugetPackages = @@ -258,14 +250,17 @@ type NuGetManager (executingDirectory : string) = // gather errors let errors = nugetPackages - |> Seq.filter (fun (idx, package) -> String.IsNullOrEmpty(package.Error) = false) + |> Seq.filter (fun (_, package) -> String.IsNullOrEmpty(package.Error) = false) |> Seq.map (fun (idx, package) -> CustomErrorInfo.From("", idx, 0, idx, lines.[idx].Length, package.Error, "Error", "preprocess")) |> Seq.toArray { OriginalLines = lines; - NuGetLines = nugetLines |> Seq.map(fun (idx, line) -> line) |> Seq.toArray; - FilteredLines = otherLines |> Seq.map(fun (idx, line) -> line) |> Seq.toArray; - Packages = nugetPackages |> Seq.map(fun (idx, package) -> package) |> Seq.toArray; + HelpLines = helpLines |> Seq.map(fun (_, line) -> line) |> Seq.toArray; + FsiOutputLines = fsiOutputLines |> Seq.map(fun (_, line) -> line) |> Seq.toArray; + NuGetLines = nugetLines |> Seq.map(fun (_, line) -> line) |> Seq.toArray; + + FilteredLines = otherLines |> Seq.map(fun (_, line) -> line) |> Seq.toArray; + Packages = nugetPackages |> Seq.map(fun (_, package) -> package) |> Seq.toArray; Errors = errors; }