editor.xsh

Interactive XSharper script editor

<?xml version="1.0" encoding="utf-8"?>
<xsharper xmlns="http://www.xsharper.com/schemas/1.0" ID="Editor" unknownSwitches="true">
    <versionInfo title="XSharper Editor" value="Edit any XSharper script in interactive mode." Version="0.1.0.0" Copyright="(C) 2009 DeltaX Inc." />
    <usage options="ifNoArguments default" />
    <param name="filename" required="true" value="Script file to edit" />
    <param name="args" required="false" value="Command line arguments for the edited script" count="multiple" last="true" />

<?_ Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    
    EditorForm f1=new EditorForm();
    f1.Filename=c.GetString("filename");
    
    StringBuilder sb=new StringBuilder();
    if (c.IsSet("args"))
        f1.Args=XS.Utils.QuoteArgs(c.GetStringArray("args"));
    Application.Run(f1);

?>

<reference name="System.Windows.Forms" />
<reference name="System.Drawing" />
<?header using System.Windows.Forms;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Drawing;
    using System.IO;
    using System.Text;
    using System.Windows.Forms;
    using XSharper.Core;
    using System.Xml;
    using System.Runtime.InteropServices;
    using System.Text.RegularExpressions;   
    using System.Threading;
    
    public  class EditorForm : Form
    {
        private System.ComponentModel.IContainer components = null;
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.Icon=Icon.ExtractAssociatedIcon(Process.GetCurrentProcess().MainModule.FileName);
            this.splitContainer1 = new System.Windows.Forms.SplitContainer();
            this.edCode = new MyTextBox();
            this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
            this.reOut = new OutputRichTextBox();
            this.edArgs = new MyTextBox();
            this.btnRun = new System.Windows.Forms.Button();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.edDir = new MyTextBox();
            this.chkDebug = new System.Windows.Forms.CheckBox();
            this.splitContainer1.Panel1.SuspendLayout();
            this.splitContainer1.Panel2.SuspendLayout();
            this.splitContainer1.SuspendLayout();
            this.tableLayoutPanel1.SuspendLayout();
            this.SuspendLayout();
            // 
            // splitContainer1
            // 
            this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.splitContainer1.Location = new System.Drawing.Point(0, 0);
            this.splitContainer1.Name = "splitContainer1";
            this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
            // 
            // splitContainer1.Panel1
            // 
            this.splitContainer1.Panel1.Controls.Add(this.edCode);
            // 
            // splitContainer1.Panel2
            // 
            this.splitContainer1.Panel2.Controls.Add(this.tableLayoutPanel1);
            this.splitContainer1.Panel2.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
            this.splitContainer1.Size = new System.Drawing.Size(979, 501);
            this.splitContainer1.SplitterDistance = 326;
            this.splitContainer1.TabIndex = 0;
            // 
            // edCode
            // 
            this.edCode.AcceptsReturn = true;
            this.edCode.AcceptsTab = true;
            this.edCode.Dock = System.Windows.Forms.DockStyle.Fill;
            this.edCode.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.edCode.Location = new System.Drawing.Point(0, 0);
            this.edCode.MaxLength = 999999999;
            this.edCode.Multiline = true;
            this.edCode.Name = "edCode";
            this.edCode.ScrollBars = System.Windows.Forms.ScrollBars.Both;
            this.edCode.Size = new System.Drawing.Size(979, 326);
            this.edCode.TabIndex = 0;
            this.edCode.WordWrap = false;
            // 
            // tableLayoutPanel1
            // 
            this.tableLayoutPanel1.ColumnCount = 3;
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
            this.tableLayoutPanel1.Controls.Add(this.edDir, 1, 1);
            this.tableLayoutPanel1.Controls.Add(this.reOut, 0, 2);
            this.tableLayoutPanel1.Controls.Add(this.edArgs, 1, 0);
            this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
            this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1);
            this.tableLayoutPanel1.Controls.Add(this.btnRun, 2, 1);
            this.tableLayoutPanel1.Controls.Add(this.chkDebug, 2, 0);
            this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
            this.tableLayoutPanel1.Name = "tableLayoutPanel1";
            this.tableLayoutPanel1.RightToLeft = System.Windows.Forms.RightToLeft.No;
            this.tableLayoutPanel1.RowCount = 3;
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F));
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
            this.tableLayoutPanel1.Size = new System.Drawing.Size(979, 171);
            this.tableLayoutPanel1.TabIndex = 0;
            // 
            // reOut
            // 
            this.reOut.BackColor = System.Drawing.Color.Black;
            this.tableLayoutPanel1.SetColumnSpan(this.reOut, 3);
            this.reOut.Dock = System.Windows.Forms.DockStyle.Fill;
            this.reOut.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
            this.reOut.ForeColor = System.Drawing.Color.White;
            this.reOut.Location = new System.Drawing.Point(3, 56);
            this.reOut.Name = "reOut";
            this.reOut.ReadOnly = true;
            this.reOut.RightToLeft = System.Windows.Forms.RightToLeft.No;
            this.reOut.Size = new System.Drawing.Size(973, 112);
            this.reOut.TabIndex = 7;
            this.reOut.Text = "";
            this.reOut.ShortcutsEnabled = true;
            // 
            // edArgs
            // 
            this.edArgs.Dock = System.Windows.Forms.DockStyle.Fill;
            this.edArgs.Location = new System.Drawing.Point(109, 3);
            this.edArgs.Name = "edArgs";
            this.edArgs.Size = new System.Drawing.Size(786, 20);
            this.edArgs.TabIndex = 2;
            // 
            // btnRun
            // 
            this.btnRun.Dock = System.Windows.Forms.DockStyle.Fill;
            this.btnRun.Location = new System.Drawing.Point(901, 29);
            this.btnRun.Name = "btnRun";
            this.btnRun.Size = new System.Drawing.Size(75, 21);
            this.btnRun.TabIndex = 6;
            this.btnRun.Text = "&Run";
            this.btnRun.UseVisualStyleBackColor = true;
            this.btnRun.Click += new System.EventHandler(this.btnRun_Click);
            // 
            // label1
            // 
            this.label1.Location = new System.Drawing.Point(3, 0);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(95, 26);
            this.label1.TabIndex = 1;
            this.label1.Text = "&Arguments:";
            this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
            // 
            // label2
            // 
            this.label2.Location = new System.Drawing.Point(3, 26);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(100, 23);
            this.label2.TabIndex = 4;
            this.label2.Text = "&Starting directory:";
            this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
            // 
            // edDir
            // 
            this.edDir.Dock = System.Windows.Forms.DockStyle.Fill;
            this.edDir.Location = new System.Drawing.Point(109, 29);
            this.edDir.Name = "edDir";
            this.edDir.Size = new System.Drawing.Size(786, 20);
            this.edDir.TabIndex = 5;
            // 
            // chkDebug
            // 
            this.chkDebug.AutoSize = true;
            this.chkDebug.Dock = System.Windows.Forms.DockStyle.Fill;
            this.chkDebug.Location = new System.Drawing.Point(901, 3);
            this.chkDebug.Name = "chkDebug";
            this.chkDebug.Size = new System.Drawing.Size(75, 20);
            this.chkDebug.TabIndex = 3;
            this.chkDebug.Text = "&Debug";
            this.chkDebug.UseVisualStyleBackColor = true;
            // 
            // EditorForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(979, 501);
            this.Controls.Add(this.splitContainer1);
            this.Name = "EditorForm";
            this.Text = "";
            this.Load += new System.EventHandler(this.EditorForm_Load);
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.EditorForm_FormClosing);
            this.splitContainer1.Panel1.ResumeLayout(false);
            this.splitContainer1.Panel1.PerformLayout();
            this.splitContainer1.Panel2.ResumeLayout(false);
            this.splitContainer1.ResumeLayout(false);
            this.tableLayoutPanel1.ResumeLayout(false);
            this.tableLayoutPanel1.PerformLayout();
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.SplitContainer splitContainer1;
        private System.Windows.Forms.TextBox edCode;
        private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
        private OutputRichTextBox reOut;
        private System.Windows.Forms.Button btnRun;
        private System.Windows.Forms.Label label1;
        private MyTextBox edArgs;
        private MyTextBox edDir;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.CheckBox chkDebug;
        
        private const int EM_SETTABSTOPS = 0x00CB;
        
        public string Filename;
        public string Args;

        
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessage(IntPtr h,int msg,int wParam,int[] lParam);
        private ManualResetEvent _running = new ManualResetEvent(false);
        private ManualResetEvent _stopEvent = new ManualResetEvent(false);
        private bool _closeOnStop = false;

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern int SendMessage(IntPtr h, int msg, int wParam, int lParam);


        
        public EditorForm()
        {
            InitializeComponent();
        }

        
        private void EditorForm_Load(object sender, EventArgs e)
        {
            // Change font & tab stops
            reOut.LoadFonts();
            edCode.Font = reOut.Font;
            SendMessage(edCode.Handle, EM_SETTABSTOPS, 1, new int[] {  2*4 });

            this.Text=Path.GetFullPath(Filename);
            if (File.Exists(Filename))
                this.Icon=Icon.ExtractAssociatedIcon(Filename);

           
            ScriptContext ctx=new ScriptContext(null);
            if (File.Exists(Filename))
            {
                edCode.Text=ctx.ReadText(Filename);
            }
            else
            {
                this.Text+=" (new)";
                edCode.Text=@"<?xml version=""1.0"" encoding=""utf-8""?"+">"+@"
<xsharper xmlns=""http://www.xsharper.com/schemas/1.0"">
</xsharper>";
            }   
            edCode.SelectionStart = 0;
            edCode.SelectionLength = 0;
            
            edArgs.Text = Args;
            edDir.Text = Directory.GetCurrentDirectory();
        }
        private bool Save(bool close)
        {
            try {
                if (edCode.Modified)
                {
                    using (StreamWriter sw = new StreamWriter(Filename, false))
                    {
                        sw.Write(edCode.Text);
                    }
                    this.Text=Path.GetFullPath(Filename);
                    edCode.Modified=false;
                }
            }
            catch (Exception ex)
            {
                
                MessageBoxButtons btn=MessageBoxButtons.OK;
                string message=ex.Message;
                if (close)
                {
                    message+=Environment.NewLine+Environment.NewLine+"Close without saving?";
                    btn=MessageBoxButtons.YesNo;
                    
                }
                DialogResult res=MessageBox.Show(message, "Error when saving script",  btn, MessageBoxIcon.Error, close?MessageBoxDefaultButton.Button2:MessageBoxDefaultButton.Button1);
                if (close && res!=DialogResult.Yes)
                    return false;
                return true;
            }
            return true;
        }

        ScriptContext ctx = new ScriptContext(System.Reflection.Assembly.GetEntryAssembly());
        private void btnRun_Click(object sender, EventArgs e)
        {
            Save(false);            
            if (_running.WaitOne(0,false))
            {
                _stopEvent.Set();
                return;
            }
            Stopwatch sw = Stopwatch.StartNew();
            object ret = "";
            string oldText = btnRun.Text;
            string oldDir = Directory.GetCurrentDirectory();
            try
            {
                _stopEvent.Reset();
                _running.Set();
                btnRun.Text = "&Cancel";
                btnRun.Update();
                reOut.Clear();

                Directory.SetCurrentDirectory(edDir.Text);
                ctx.MinOutputType = chkDebug.Checked ? OutputType.Debug : OutputType.Info;
                ctx.Output= OnOutput;

                Cursor = Cursors.WaitCursor;
                
                ctx.Progress = OnProgress;

                // Run it!
                XmlReaderSettings rs=new XmlReaderSettings();
                rs.IgnoreWhitespace = false;
                rs.ConformanceLevel = ConformanceLevel.Fragment;
                
                string text=edCode.SelectedText;
                if (text.Length==0)
                {
                    text=edCode.Text;
                    ctx.Clear();
                }
                Script s = ctx.LoadScript(XmlReader.Create(new StringReader(text),rs), Filename);
                ctx.Initialize(s);
                using (XS.ConsoleRedirector r=new XS.ConsoleRedirector(ctx)) 
                {
                    ctx.In=TextReader.Null;
                    ret=ctx.ExecuteScript(s, XS.Utils.SplitArgs(edArgs.Text), CallIsolation.None);
                }
            }
            catch(Exception ex)
            {
                ctx.WriteException(ex);
                ret = "-1";
            }
            
            Directory.SetCurrentDirectory(oldDir);
            _running.Reset();
            btnRun.Text = oldText;
            ctx.Info.WriteLine("--- Completed in "+sw.Elapsed+" with return value="+ret+" ---");
            reOut.ScrollToBottom();
            Cursor = Cursors.Arrow;
            if (_closeOnStop)
                Close();

        }
        
        private void OnProgress(object sender1, OperationProgressEventArgs e1)
        {
            Application.DoEvents();
            if (_stopEvent.WaitOne(0,false))
            {
                e1.Cancel = true;
                _stopEvent.Reset();
            }
        }
        private void OnOutput(object sender1, OutputEventArgs e1)
        {
            reOut.Output(e1.OutputType,e1.Text);
        }
        
        

        private void EditorForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!Save(true))
            {
                e.Cancel=true;
                return;
            }
            if (_running.WaitOne(0,false))
            {
                Text = "Cancelling script...";
                _closeOnStop = true;
                e.Cancel = true;
                
                return;
            }
        }
        
        public class MyTextBox : System.Windows.Forms.TextBox
        {
            protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e)
            {
                if (e.Control && (e.KeyCode == System.Windows.Forms.Keys.A))
                {
                    this.SelectAll();
                    e.SuppressKeyPress = true;
                    e.Handled = true;
                }
                else
                    base.OnKeyDown(e);
            }
        }

    }
    
    // Extended richedit box, that supports different fonts and backspace character
    public  class OutputRichTextBox : RichTextBox
    {
        private Font _font,_fontBold;

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        private static extern int SendMessage(IntPtr h, int msg, int wParam, int lParam);
        
        private Font createFont(bool bold)
        {
            Font f=new Font("Consolas", 9, bold?FontStyle.Bold:FontStyle.Regular);
            if (f.Name != "Consolas")
                f = new Font("Courier New", 9, bold?FontStyle.Bold:FontStyle.Regular);

            return f;
        }
        
        public void LoadFonts() 
        {
            _font=createFont(false);
            _fontBold=createFont(true);
            this.Font = _font;
        }
        public void ScrollToBottom()
        {
            SendMessage(this.Handle, 0x115, 7, 0);
        }
        
        public void Output(OutputType otype, string text)
        {        
            this.Select(this.TextLength,0);
            StringBuilder sb=new StringBuilder();
            foreach (char ch in text)
            {
                if (ch==(char)8)
                {
                    if (sb.Length>0)
                        this.AppendText(sb.ToString());
                    while (this.TextLength>0)
                    {
                        this.Select(this.TextLength-1,1);                   
                        string s=this.SelectedText;
                        if (s=="\n" || s=="\r" || s=="")
                        {
                            this.Select(this.TextLength,0);
                            break;
                        }
                        this.Select(this.TextLength-1,1);                   
                        this.ReadOnly=false;
                        this.SelectedText=string.Empty;
                        this.ReadOnly=true;
                    }
                    sb.Length=0;
                }
                else
                {
                    if (sb.Length==0)
                    {
                        switch (otype)
                        {
                            case OutputType.Debug:  this.SelectionColor = Color.Cyan;   break;
                            case OutputType.Error:  this.SelectionColor = Color.Yellow; break;
                            case OutputType.Info:   this.SelectionColor = Color.LightGreen; break;
                            case OutputType.Bold:   this.SelectionColor = Color.White; break;
                            default:                this.SelectionColor = Color.LightGray; break;
                        }
                        if (otype==OutputType.Bold)
                        {
                            if (this.SelectionFont.Bold!=true)
                                this.SelectionFont=_fontBold;
                        }
                        else if (this.SelectionFont.Bold!=false)
                            this.SelectionFont=_font;
                    }
                    sb.Append(ch);
                    if (sb.Length>5000)
                    {
                        this.AppendText(sb.ToString());
                        sb.Length=0;
                    }
                        
                }
            }
            this.AppendText(sb.ToString());

            ScrollToBottom();
            Application.DoEvents();
        }
    }
?>
</xsharper>