hubFS: THE place for F#

. . . are you on The Hub?
Welcome to hubFS: THE place for F# Sign in | Join | Help
in Search

F# Interactive Windows Application and Some Questions

Last post 09-03-2008, 13:24 by kaveh.shahbazian. 0 replies.
Sort Posts: Previous Next
  •  09-03-2008, 13:24 6846

    F# Interactive Windows Application and Some Questions

    First of all best regards to the F# team for producing CTP! This is a great event! Thanks a million!

    I have implemented a windows application in F# which is a wrapper for fsi.exe (F# REPL or Interactive Console). You can load a fsx or save your working text to a file and it works like F# console in Visual Studio. You select your code and press Alt+Enter and your code will be sent to fsi.exe and the result can be seen in the output window. Of course it is far from being comparable to the Visual Studio Interactive Console (We have intellisense there and much more utilities which are indeed very hard work to accomplish). Yet it is good enough for quick tests. It works well out of visual studio. Here the code and then questions:
    (do not forget to add the necessary refrences if you are going to run this in REPL)

    #light

    open System
    open System.Collections.Generic
    open System.ComponentModel
    open System.Data
    open System.Drawing
    open System.Linq
    open System.Text
    open System.Windows.Forms

    open System.IO
    open System.Diagnostics
    open System.Threading

    type MainForm =
        inherit Form
       
        val mutable components : System.ComponentModel.IContainer
        val mutable rtbInput : System.Windows.Forms.RichTextBox
        val mutable rtbOutput : System.Windows.Forms.RichTextBox
        val mutable ofdOpen : System.Windows.Forms.OpenFileDialog
        val mutable sfdSave : System.Windows.Forms.SaveFileDialog
        val mutable cmsMenu : System.Windows.Forms.ContextMenuStrip
        val mutable saveToolStripMenuItem : System.Windows.Forms.ToolStripMenuItem
        val mutable openToolStripMenuItem : System.Windows.Forms.ToolStripMenuItem
        val mutable exitToolStripMenuItem : System.Windows.Forms.ToolStripMenuItem
       
        val mutable proc : Process
        val mutable appPath : string
        val mutable reader : Thread
        val mutable errReader : Thread
        val mutable stop : ManualResetEvent
       
        override this.Dispose (disposing) =
            if disposing && (this.components <> null) then
                this.components.Dispose ()
            base.Dispose (disposing)
       
        member this.InitializeComponent () =
            this.components <- new System.ComponentModel.Container ()
            this.rtbInput <- new System.Windows.Forms.RichTextBox ()
            this.rtbOutput <- new System.Windows.Forms.RichTextBox ()
            this.ofdOpen <- new System.Windows.Forms.OpenFileDialog ()
            this.sfdSave <- new System.Windows.Forms.SaveFileDialog ()
            this.cmsMenu <- new System.Windows.Forms.ContextMenuStrip (this.components)
            this.saveToolStripMenuItem <- new System.Windows.Forms.ToolStripMenuItem ()
            this.openToolStripMenuItem <- new System.Windows.Forms.ToolStripMenuItem ()
            this.exitToolStripMenuItem <- new System.Windows.Forms.ToolStripMenuItem ()
            this.cmsMenu.SuspendLayout ()
            this.SuspendLayout ()
            //
            // rtbInput
            //
            this.rtbInput.AcceptsTab <- true
            this.rtbInput.ContextMenuStrip <- this.cmsMenu
            this.rtbInput.Dock <- System.Windows.Forms.DockStyle.Top
            this.rtbInput.Font <- new System.Drawing.Font ("Consolas", 11.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (0)))
            this.rtbInput.Location <- new System.Drawing.Point (0, 0)
            this.rtbInput.Name <- "rtbInput"
            this.rtbInput.Size <- new System.Drawing.Size (757, 312)
            this.rtbInput.TabIndex <- 0
            this.rtbInput.Text <- ""
            this.rtbInput.KeyDown.AddHandler(new System.Windows.Forms.KeyEventHandler (fun s e -> this.rtbInput_KeyDown (s, e)))
            this.rtbInput.KeyUp.AddHandler(new System.Windows.Forms.KeyEventHandler (fun s e -> this.rtbInput_KeyUp (s, e)))
            //
            // rtbOutput
            //
            this.rtbOutput.Location <- new System.Drawing.Point (0, 318)
            this.rtbOutput.Name <- "rtbOutput"
            this.rtbOutput.Size <- new System.Drawing.Size (757, 190)
            this.rtbOutput.TabIndex <- 1
            this.rtbOutput.Text <- ""
            this.rtbOutput.TextChanged.AddHandler(new System.EventHandler (fun s e -> this.rtbOutput_TextChanged (s, e)))
            //
            // ofdOpen
            //
            this.ofdOpen.Filter <- "F# script files (*.fsx)|*.fsx|All Files (*.*)|*.*"
            this.ofdOpen.FileOk.AddHandler(new System.ComponentModel.CancelEventHandler (fun s e -> this.ofdOpen_FileOk (s, e)))
            //
            // sfdSave
            //
            this.sfdSave.Filter <- "F# script files (*.fsx)|*.fsx|All Files (*.*)|*.*"
            this.sfdSave.FileOk.AddHandler(new System.ComponentModel.CancelEventHandler (fun s e -> this.sfdSave_FileOk (s, e)))
            //
            // cmsMenu
            //
            this.cmsMenu.Items.AddRange ([| (this.saveToolStripMenuItem :> System.Windows.Forms.ToolStripItem);
                                            (this.openToolStripMenuItem :> System.Windows.Forms.ToolStripItem);
                                            (this.exitToolStripMenuItem :> System.Windows.Forms.ToolStripItem) |])
            this.cmsMenu.Name <- "cmsMenu"
            this.cmsMenu.Size <- new System.Drawing.Size (153, 92)
            //
            // saveToolStripMenuItem
            //
            this.saveToolStripMenuItem.Name <- "saveToolStripMenuItem"
            this.saveToolStripMenuItem.ShortcutKeys <- System.Windows.Forms.Keys.Control ||| System.Windows.Forms.Keys.S
            this.saveToolStripMenuItem.Size <- new System.Drawing.Size (152, 22)
            this.saveToolStripMenuItem.Text <- "Save"
            this.saveToolStripMenuItem.Click.AddHandler(new System.EventHandler (fun s e -> this.saveToolStripMenuItem_Click(s, e)))
            //
            // openToolStripMenuItem
            //
            this.openToolStripMenuItem.Name <- "openToolStripMenuItem"
            this.openToolStripMenuItem.ShortcutKeys <- System.Windows.Forms.Keys.Control ||| System.Windows.Forms.Keys.O
            this.openToolStripMenuItem.Size <- new System.Drawing.Size (152, 22)
            this.openToolStripMenuItem.Text <- "Open"
            this.openToolStripMenuItem.Click.AddHandler(new System.EventHandler (fun s e -> this.openToolStripMenuItem_Click(s, e)))
            //
            // exitToolStripMenuItem
            //
            this.exitToolStripMenuItem.Name <- "exitToolStripMenuItem"
            this.exitToolStripMenuItem.ShortcutKeys <- System.Windows.Forms.Keys.Control ||| System.Windows.Forms.Keys.X
            this.exitToolStripMenuItem.Size <- new System.Drawing.Size (152, 22)
            this.exitToolStripMenuItem.Text <- "Exit"
            this.exitToolStripMenuItem.Click.AddHandler(new System.EventHandler (fun s e -> this.exitToolStripMenuItem_Click(s, e)))
            //
            // MainForm
            //
            this.AutoScaleDimensions <- new System.Drawing.SizeF (6.0f, 13.0f)
            this.AutoScaleMode <- System.Windows.Forms.AutoScaleMode.Font
            this.ClientSize <- new System.Drawing.Size (757, 508)
            this.ContextMenuStrip <- this.cmsMenu
            this.Controls.Add (this.rtbOutput)
            this.Controls.Add (this.rtbInput)
            this.Name <- "MainForm"
            this.Text <- "MainForm"
            this.FormClosing.AddHandler(new System.Windows.Forms.FormClosingEventHandler (fun s e -> this.MainForm_FormClosing(s, e)))
            this.ResizeEnd.AddHandler(new System.EventHandler (fun s e -> this.MainForm_ResizeEnd(s, e)))
            this.cmsMenu.ResumeLayout (false)
            this.ResumeLayout (false)
       
        member this.ErrReaderTrd () =
            let MAX_BUF = 4096
            let mutable buffer = Array.create MAX_BUF '0'
            let mutable bread = 0
            while not (this.stop.WaitOne (1, true)) do
                try
                    bread <- this.proc.StandardError.Read (buffer, 0, MAX_BUF)
                    while (bread > 0) do
                        this.rtbOutput.AppendText (new string (buffer, 0, bread))
                        bread <- this.proc.StandardError.Read (buffer, 0, MAX_BUF)
                with
                | _ -> ()
       
        member this.ReaderTrd () =
            let MAX_BUF = 4096
            let mutable buffer = Array.create MAX_BUF '0'
            let mutable bread = 0
            while not (this.stop.WaitOne (1, true)) do
                try
                    bread <- this.proc.StandardOutput.Read (buffer, 0, MAX_BUF)
                    while (bread > 0) do
                        this.rtbOutput.AppendText (new string (buffer, 0, bread))
                        bread <- this.proc.StandardOutput.Read (buffer, 0, MAX_BUF)
                with
                | _ -> ()
       
        member this.InitializeREPL () =
            let mutable psi = new ProcessStartInfo ()

            psi.FileName <- this.appPath
            psi.CreateNoWindow <- true
            psi.RedirectStandardInput <- true
            psi.RedirectStandardOutput <- true
            psi.RedirectStandardError <- true
            psi.UseShellExecute <- false
            psi.ErrorDialog <- true

            this.proc <- new Process ()
            this.proc.StartInfo <- psi
            this.proc.SynchronizingObject <- this
            this.proc.EnableRaisingEvents <- true
            this.proc.Start () |> ignore

            this.stop <- new ManualResetEvent (false)
            this.reader <- new Thread (new ThreadStart (this.ReaderTrd))
            this.errReader <- new Thread (new ThreadStart (this.ErrReaderTrd))

            this.reader.Start ()
            this.errReader.Start ()
       
        member this.MainForm_FormClosing (sender : System.Object, e : FormClosingEventArgs) =
            this.stop.Set () |> ignore
            Thread.Sleep (200)

            if not (this.reader.Join (200)) then
                this.reader.Abort ()

            if not (this.errReader.Join (200)) then
                this.errReader.Abort ()
            try
                this.proc.Kill ();
            with
            | _ -> ()
       
        member this.rtbOutput_TextChanged (sender : System.Object, e : EventArgs) =
            // still there is no good way to do this
            // I mean to make a RichTextBox auto scroll to bottom
            this.rtbOutput.SelectionStart <- this.rtbOutput.Text.Length + 10;
            this.rtbOutput.SelectionLength <- 0
            this.rtbOutput.ScrollToCaret ()
       
        member this.MainForm_ResizeEnd (sender : System.Object, e : EventArgs) =
            let src = this.Height - 32
            let top = src * 60 / 100
            let bottom = src - top

            this.rtbInput.Height <- top
            this.rtbOutput.Height <- bottom
       
        member this.rtbInput_KeyDown (sender : System.Object, e : KeyEventArgs) =
            if (e.KeyData = Keys.Tab) then
                e.Handled <- true
                this.rtbInput.AppendText ("    ")
       
        member this.rtbInput_KeyUp (sender : System.Object, e : KeyEventArgs) =
            if (e.KeyData = (Keys.Enter ||| Keys.Alt)) then
                this.rtbOutput.AppendText ("\r\n")
                this.proc.StandardInput.Write (this.rtbInput.SelectedText + "\r\n;;\r\n")

        member this.ofdOpen_FileOk (sender : System.Object, e : CancelEventArgs) =
            use sr = new StreamReader (this.ofdOpen.FileName)
            this.rtbInput.AppendText (sr.ReadToEnd ())
            sr.Close ()

        member this.sfdSave_FileOk (sender : System.Object, e : CancelEventArgs) =
            use sw = new StreamWriter (this.sfdSave.FileName)
            sw.Write (this.rtbInput.Text)
            sw.Flush ()
            sw.Close ()
       
        member this.saveToolStripMenuItem_Click (sender : System.Object, e : EventArgs) =
            this.sfdSave.ShowDialog () |> ignore
       
        member this.openToolStripMenuItem_Click (sender : System.Object, e : EventArgs) =
            this.ofdOpen.ShowDialog () |> ignore
       
        member this.exitToolStripMenuItem_Click (sender : System.Object, e : EventArgs) =
            this.Close () |> ignore
       
        new () as this =
            { components = null;
              rtbInput = null;
              rtbOutput = null;
              ofdOpen = null;
              sfdSave = null;
              cmsMenu = null;
              saveToolStripMenuItem = null;
              openToolStripMenuItem = null;
              exitToolStripMenuItem = null;
              proc = null;
              appPath = null;
              reader = null;
              errReader = null;
              stop = null; }
            then
              this.appPath <- @"C:\Program Files\FSharp-1.9.6.0\bin\fsi.exe"
              this.InitializeComponent()
              this.InitializeREPL()

    [<STAThread>]
    let main() =
        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(false)
        Thread.Sleep(400)
        Application.Run(new MainForm())

    [<STAThread>]
    main()

    Now the questions:
    1 - When I am developing in Visual Studio, this code does not work from within VS. Is this something about fsi.exe and if not can anyone spot the problem?
    2 - Debugging in reader threads almost does not work. It hits the line and when I press next step it continues to run.

    I had some other problems that I don't remember yet I have enjoyed CTP indeed!
    (There is another F# try of mine from old days at http://cs.hubfs.net/forums/thread/1495.aspx; that feelings came to life again!)

    Best Regards

View as RSS news feed in XML
Powered by Community Server, by Telligent Systems