Repl.cs :  » Script » IronRuby » Microsoft » Scripting » Silverlight » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Script » IronRuby 
IronRuby » Microsoft » Scripting » Silverlight » Repl.cs
/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Browser;
using System.IO;
using Microsoft.Scripting.Hosting;
using Microsoft.Scripting.Utils;
using System.Windows.Threading;
using Microsoft.Scripting.Silverlight;
#if CLR4
using System.Core;
#endif

namespace Microsoft.Scripting.Silverlight{
    public class Repl {
        
        #region Console Html Template
        private const string _sdlr        = "silverlightDlrRepl";
        private const string _sdlrCode    = "silverlightDlrReplCode";
        private const string _sdlrPrompt  = "silverlightDlrReplPrompt";
        private const string _sdlrLine    = "silverlightDlrReplLine";
        private const string _sdlrOutput  = "silverlightDlrReplOutput";
        private const string _sdlrValue   = "silverlightDlrReplValue";
        private const string _sdlrResult  = "silverlightDlrReplResult";
        private const string _sdlrRunForm = "silverlightDlrReplRunForm";
        private const string _sdlrRun     = "silverlightDlrReplRun";

        // 0 - result id
        // 1 - prompt id/class
        // 2 - run form id
        // 3 - code input id
        // 4 - run id
        private static Func<string> _replHtmlTemplate = delegate() {
            return string.Format(@"
<div id=""{1}"" class=""{0}""></div>
<form id=""{5}"" class=""{4}"" action=""javascript:void(0)""><span id=""{3}"" class=""{2}""></span><input type=""text"" id=""{7}"" class=""{6}"" autocomplete=""off"" /><input type=""submit"" id=""{9}"" class=""{8}"" value=""Run"" /></form>",
                _sdlrResult,  GetId(_sdlrResult),
                _sdlrPrompt,  GetId(_sdlrPrompt),
                _sdlrRunForm, GetId(_sdlrRunForm),
                _sdlrCode,    GetId(_sdlrCode),
                _sdlrRun,     GetId(_sdlrRun)
            );
        };

        private static string GetId(string id) {
            return id + _count;
        }
        #endregion

        #region Private fields
        private string              _code;
        private bool                _multiLine;
        private bool                _multiLinePrompt;
        private bool                _multiLineComplete;
        private ReplOutputBuffer    _outputBuffer;
        private ReplInputBuffer     _inputBuffer;
        private HtmlElement         _silverlightDlrReplCode;
        private HtmlElement         _silverlightDlrReplResult;
        private HtmlElement         _silverlightDlrReplPrompt;
        private ScriptEngine        _engine;
        private ScriptScope         _currentScope;
        private static int          _count;
        private ReplHistory         _history = new ReplHistory();
        private LanguageNeutralFormatter _neutralFormatter;
        #endregion

        #region Public properties
        /// <summary>
        /// The InputBuffer for the Repl
        /// </summary>
        public ReplInputBuffer InputBuffer {
            get { return _inputBuffer; }
        }

        /// <summary>
        /// The OutputBuffer for the Repl
        /// </summary>
        public ReplOutputBuffer OutputBuffer {
            get { return _outputBuffer; }
        }

        /// <summary>
        /// The ScriptEngine to run Repl code against
        /// </summary>
        public ScriptEngine Engine {
            get { return _engine; }
        }

        public ReplHistory History {
            get { return _history; }
        }

        public HtmlElement Input {
            get { return _silverlightDlrReplCode; }
        }

        public IReplFormatter Formatter { get; private set; }
        #endregion

        #region Console management
        /// <summary>
        /// Creates a console and inserts it into the page.
        /// </summary>
        public static Repl Show() {
            if (DynamicApplication.Current == null) {
                throw new Exception("Use the Show(engine, scope) overload, since this is not a dynamic application");
            }

            ScriptEngine engine = null;
            if (DynamicApplication.Current.Engine != null) {
                engine = DynamicApplication.Current.Engine.Engine;    
            }
            if (engine == null) {
                var langCfg = DynamicApplication.Current.LanguagesConfig;
                if (langCfg.LanguagesUsed.Count < 1) {
                    throw new Exception("Use the Show(engine, scope) overload, since there are no languages used");
                }
                foreach(var lang in langCfg.LanguagesUsed) {
                    if (lang.Value) {
                        engine = langCfg.GetEngine(lang.Key);
                        break;
                    }
                }
            }

            ScriptScope scope = DynamicApplication.Current.Engine.EntryPointScope;
            if (scope == null) {
                scope = DynamicApplication.Current.Engine.CreateScope();
            }

            return Show(engine, scope);
        }

        /// <summary>
        /// Creates a console with a language
        /// </summary>
        public static Repl Show(string language) {
            var engine = DynamicApplication.Current.LanguagesConfig.GetEngine(language);
            return Show(engine, DynamicApplication.Current.Engine.EntryPointScope);
        }

        /// <summary>
        /// Creates a console with a engine and scope
        /// </summary>
        public static Repl Show(ScriptEngine engine, ScriptScope scope) {
            ContractUtils.RequiresNotNull(engine, "engine");
            ContractUtils.RequiresNotNull(scope, "scope");

            _count++;

            if (DynamicApplication.Current == null) {
                Window.Show();
            } else {
                Window.Show(Settings.ErrorTargetID);
            }

            HtmlElement replDiv = null;
            var repl = Create(engine, scope, out replDiv);

            Window.Current.AddPanel(engine.Setup.Names[0] + " Console", replDiv);
            Window.Current.Initialize();

            repl.Start();
            return repl;
        }

        /// <summary>
        /// Creates the Repl object and HtmlElement containing the rendered Repl.
        /// </summary>
        public static Repl Create(out HtmlElement element) {
            var dynEngine = DynamicApplication.Current.Engine;
            return Create(dynEngine.Engine, dynEngine.EntryPointScope, out element);
        }

        /// <summary>
        /// Create the Repl object with the ScriptEngine and ScriptScope, and outputs
        /// the HtmlElement containing the rendered Repl.
        /// </summary>
        public static Repl Create(ScriptEngine engine, ScriptScope scope, out HtmlElement replDiv) {
            replDiv = HtmlPage.Document.CreateElement("div");
            replDiv.Id = GetId(_sdlr);
            replDiv.CssClass += _sdlr;
            replDiv.SetProperty("innerHTML", _replHtmlTemplate.Invoke());
            return new Repl(engine, scope);
        }

        private Repl(ScriptEngine engine, ScriptScope scope) {
            _engine = engine;
            _currentScope = scope;
            Formatter = GetReplFormatter(this);
        }

        private IReplFormatter GetReplFormatter(Repl instance) {
            _neutralFormatter = new LanguageNeutralFormatter(this);
            if (_engine.Setup.FileExtensions.Count > 0) {
                string path = string.Format("repl_formatter{0}", _engine.Setup.FileExtensions[0]);
                string code = DynamicApplication.GetResource(path);
                if (code != null) {
                    var scope = _engine.CreateScope();
                    try {
                        _engine.CreateScriptSourceFromString(code, path).Execute(scope);
                        var CreateReplFormatter = scope.GetVariable<Func<Repl, IReplFormatter, IReplFormatter>>("create_repl_formatter");
                        if (CreateReplFormatter != null) {
                            return CreateReplFormatter(this, _neutralFormatter);
                        }
                    } catch (Exception e) {
                        DynamicApplication.Current.HandleException(this, e);
                    }
                }
            }
            return _neutralFormatter;
        }

        /// <summary>
        /// Starts the Repl: creates HTML elements, input/output buffer, make
        /// sure the prompt is cleared and focused, show the prompt, and attach
        /// the keypress event.
        /// </summary>
        public void Start() {
            _silverlightDlrReplCode = HtmlPage.Document.GetElementById(GetId(_sdlrCode));
            _silverlightDlrReplResult = HtmlPage.Document.GetElementById(GetId(_sdlrResult));
            _silverlightDlrReplPrompt = HtmlPage.Document.GetElementById(GetId(_sdlrPrompt));
            ProcessPromptElement(_silverlightDlrReplPrompt);
            _inputBuffer = new ReplInputBuffer(this);
            _outputBuffer = new ReplOutputBuffer(_silverlightDlrReplResult, _sdlrOutput);
            ShowDefaults();
            ShowPrompt();
            _silverlightDlrReplCode.AttachEvent("onkeydown", new EventHandler<HtmlEventArgs>(OnKeyDown));
        }

        private void OnKeyDown(object sender, HtmlEventArgs args) {
            SendCharacterCode(args.CharacterCode, args.CtrlKey, args.ShiftKey);
        }

        /// <summary>
        /// Processes a key press:
        /// - On enter, run the code in the input buffer. Pass the ctrl-key
        ///   to decide whether to force execution, even if the expression is
        ///   incomplete.
        /// - On up arrow, show the previous command.
        /// - On down arrow (and the shift key is not pressed), show the next 
        ///   command.
        /// - Otherwise, just remeber the char for history purposes.
        /// </summary>
        /// <param name="c">int representation of the char to handle</param>
        /// <param name="ctrlKey">Was the ctrl key pressed?</param>
        /// <param name="shiftKey">Was the shift key pressed?</param>
        public void SendCharacterCode(int c, bool ctrlKey, bool shiftKey) {
            switch(c) {
            case 13:
                Store();
                RunCode(ctrlKey);
                break;
            case 38:
                ShowPreviousCommand();
                break;
            case 40:
                if (!shiftKey) {
                    ShowNextCommand();
                }
                break;
            };
        }

        /// <summary>
        /// Reset the REPL
        /// </summary>
        private void Reset() {
            _code = null;
            _multiLine = false;
            _multiLinePrompt = false;
            _multiLineComplete = false;
        }
        #endregion

        #region History

        private string Remember() {
            return _history.ReplaceWriteCommand(Input.GetProperty("value").ToString());
        }

        private void Store() {
            Remember();
            _history.StartNewCommand();
        }

        /// <summary>
        /// Get the next command
        /// </summary>
        public string GetNextCommand() {
            _history.ReadNext();
            return _history.CurrentReadCommand;
        }

        /// <summary>
        /// Get the previous command
        /// </summary>
        public string GetPreviousCommand() {
            Remember();
            _history.ReadPrev();
            return _history.CurrentReadCommand;
        }

        public class ReplHistory {

            private List<string> _commands;
            private int _readPointer;
            private int _writePointer;
            private Func<bool> ResetReadPointerBehavior { get; set; }

            public List<string> Commands { get { return _commands; } }
            
            public ReplHistory() : this(true) {
                Clear();
            }

            public ReplHistory(bool cmdExeBehavior) {
                if (cmdExeBehavior) {
                    ResetReadPointerBehavior = new Func<bool>(() => _readPointer == _writePointer);
                } else {
                    ResetReadPointerBehavior = new Func<bool>(() => true);
                }
            }

            public string CurrentReadCommand {
                get {
                    return GetCommand(_readPointer);
                }
            }

            public string CurrentWriteCommand {
                get {
                    return GetCommand(_writePointer);
                }
            }

            public int ReadPrev() {
                if (_readPointer > 0)
                    _readPointer -= 1;
                return _readPointer;
            }

            public int ReadNext() {
                if (_readPointer < (_commands.Count - 1))
                    _readPointer += 1;
                return _readPointer;
            }

            public string GetCommand(int index) {
                if (_commands.Count == 0) return "";
                return _commands[PointerValue(index)];
            }

            public string SetCommand(int index, string cmd) {
                if (_commands.Count == 0) StartNewCommand();
                return _commands[PointerValue(_writePointer)] = cmd;
            }

            public int ResetReadPointer() {
                return _readPointer = PointerValue(_commands.Count - 1);
            }

            public int ResetWritePointer() {
                return _writePointer = PointerValue(_commands.Count - 1);
            }

            private int PointerValue(int p) {
                if (p <= 0) return 0;
                else if (p > _commands.Count - 1) return _commands.Count - 1;
                return p;
            }

            public string AppendWriteCommand(string cmd) {
                return ReplaceWriteCommand(CurrentWriteCommand + cmd);
            }

            public string AppendWriteCommand(char c) {
                return ReplaceWriteCommand(CurrentWriteCommand + c);
            }

            public string ReplaceWriteCommand(string cmd) {
                return SetCommand(_writePointer, cmd);
            }

            public void StartNewCommand() {
                _commands.Add("");
                if (ResetReadPointerBehavior())
                    ResetReadPointer();
                ResetWritePointer();
            }

            public void Clear() {
                _commands = new List<string>(new string[] { "" });
                _readPointer = 0;
                _writePointer = 0;
            }
        }

        #endregion

        #region Running Code
        /// <summary>
        /// Returns null if "text" is not a complete expression, otherwise
        /// return the "text".
        /// </summary>
        public string TryExpression(string text) {
            var props = _engine.CreateScriptSourceFromString(
                text, SourceCodeKind.Expression
            ).GetCodeProperties();
            string result;
            if (props == ScriptCodeParseResult.Complete || props == ScriptCodeParseResult.Empty) {
                result = text;
            } else {
                result = null;
            }
            return result;
        }

        /// <summary>
        /// Run the REPL input, but do not force the execution.
        /// </summary>
        public void RunCode() {
            RunCode(false);
        }

        /// <summary>
        /// Run single and multiple lines from the REPL input, and render the
        /// result to the REPL.
        /// </summary>
        /// <param name="forceExecute">Forces the statement to execute, regardless of it's validity</param>
        public void RunCode(bool forceExecute) {
            var line = _silverlightDlrReplCode.GetProperty("value").ToString();
            _code = (_code == null ? "" : _code + "\n") + line;

            if (_code != null) {
                var multiLine = _code.Split('\n').Length > 1;
                
                object result = null;
                _outputBuffer.UserOutput = true;
                if (_code != string.Empty && !multiLine) {
                    result = DoSingleLine(forceExecute);
                } else {
                    result = DoMultiLine(forceExecute);
                }
                _outputBuffer.UserOutput = false;

                ShowLineAndResult(line, result);
            }
        }

        /// <summary>
        /// If _code is a valid expression, try running it. Otherwise, run the 
        /// code as a multi-line expression.
        /// </summary>
        /// <param name="forceExecute">Forces the statement to execute, regardless of it's validity</param>
        /// <returns>The result of the _code execution</returns>
        private object DoSingleLine(bool forceExecute) {
            var valid = TryExpression(_code);
            if (valid != null) {
                var source = _engine.CreateScriptSourceFromString(_code, SourceCodeKind.Expression);
                return ExecuteCode(source);
            } else {
                DoMultiLine(forceExecute);
            }
            return null;
        }

        /// <summary>
        /// Runs a multi-line expression. If it's not complete (or not forced
        /// to execute, return null.
        /// </summary>
        /// <param name="forceExecute">Forces the statement to execute, regardless of it's validity</param>
        /// <returns>result of the _code execution</returns>
        private object DoMultiLine(bool forceExecute) {
            if (forceExecute || IsComplete(_code, AllowIncomplete())) {
                _multiLineComplete = true;
                var source = _engine.CreateScriptSourceFromString(_code, SourceCodeKind.InteractiveCode);
                return ExecuteCode(source);
            } else {
                _multiLine = true;
                _multiLineComplete = false;
            }
            return null;
        }

        /// <summary>
        /// Execute code against the currentScope, or create one if it doesn't 
        /// exist. Handles the exception if one occured.
        /// </summary>
        /// <param name="source">ScriptSource to execute</param>
        /// <returns>the result of the execution, or null if an exception occured</returns>
        public object ExecuteCode(ScriptSource source) {
            object result;
            try {
                if (_currentScope == null) {
                    _currentScope = _engine.CreateScope();
                }
                result = source.Compile(new ErrorFormatter.Sink()).Execute(_currentScope);
            } catch (Exception e) {
                HandleException(e);
                result = null;
            }
            return result;
        }

        /// <summary>
        /// Handle the exception by writing the stack trace to the output buffer.
        /// </summary>
        /// <param name="e"></param>
        private void HandleException(Exception e) {
            _outputBuffer.WriteLine(_engine.GetService<ExceptionOperations>().FormatException(e));
        }

        /// <summary>
        /// Is the interactive code complete?
        /// </summary>
        public bool IsComplete(string text, bool allowIncomplete) {
            var props = _engine.CreateScriptSourceFromString(
                text, SourceCodeKind.InteractiveCode
            ).GetCodeProperties();
            var result = (props != ScriptCodeParseResult.Invalid) &&
                (props != ScriptCodeParseResult.IncompleteToken) &&
                (allowIncomplete || (props != ScriptCodeParseResult.IncompleteStatement));
            return result;
        }

        /// <summary>
        /// Allow incomplete if there is more than 0 lines and the last line is blank.
        /// </summary>
        /// <returns></returns>
        private bool AllowIncomplete() {
            var lines = _code.Split('\n');
            if(lines.Length == 0) return false;
            return lines[lines.Length - 1] == string.Empty;
        }
        #endregion

        #region Rendering
        /// <summary>
        /// Given a line of code and it's result, render it to the REPL.
        /// </summary>
        /// <param name="line"></param>
        /// <param name="result"></param>
        internal void ShowLineAndResult(string line, object result) {
            ShowCodeLineInResultDiv(line);

            if (!_multiLine || _multiLineComplete) {
                FlushOutputInResultDiv();
                ShowValueInResultDiv(result);
                ShowPrompt();
                Reset();
            } else {
                ShowSubPrompt();
            }

            ShowDefaults();
        }

        /// <summary>
        /// Defaults of the REPL
        /// </summary>
        internal void ShowDefaults() {
            _silverlightDlrReplCode.SetProperty("value", "");
            _outputBuffer.ScrollToBottom();
            try {
                _silverlightDlrReplPrompt.Focus();
                _silverlightDlrReplCode.Focus();
            } catch {}
        }

        #region Code input
        /// <summary>
        /// Append code to the input.
        /// </summary>
        /// <param name="str"></param>
        public void AppendCode(string str) {
            string toPrepend = HtmlPage.Document.GetElementById(GetId(_sdlrCode)).GetProperty("value").ToString();
            HtmlPage.Document.GetElementById(GetId(_sdlrCode)).SetProperty("value", toPrepend + str);
        }
        #endregion

        #region Prompt
        /// <summary>
        /// Render what happens on multi-line input.
        /// </summary>
        internal void ShowSubPrompt() {
            _outputBuffer.PutTextInElement(SubPromptHtml(), _silverlightDlrReplPrompt);
        }

        /// <summary>
        /// Render what happens on normal input.
        /// </summary>
        internal void ShowPrompt() {
            _outputBuffer.PutTextInElement(PromptHtml(), _silverlightDlrReplPrompt);
        }

        /// <summary>
        /// Normal prompt
        /// </summary>
        internal string PromptHtml() {
            try {
                var html = Formatter.PromptHtml();
                if (html != null) return html;
            } catch (Exception e) {
                DynamicApplication.Current.HandleException(this, e);
            }
            return _neutralFormatter.PromptHtml();
        }

        /// <summary>
        /// Multi-line prompt
        /// </summary>
        internal string SubPromptHtml() {
            try {
                var html = Formatter.SubPromptHtml();
                if (html != null) return html;
            } catch (Exception e) {
                DynamicApplication.Current.HandleException(this, e);
            }
            return _neutralFormatter.SubPromptHtml();
        }
        #endregion

        #region Pushing stuff into Result Div
        /// <summary>
        /// Render code line in the results section of the Repl
        /// </summary>
        /// <param name="codeLine"></param>
        internal void ShowCodeLineInResultDiv(string codeLine) {
            ShowPromptInResultDiv();
            _outputBuffer.ElementClass = _sdlrLine;
            _outputBuffer.ElementName = "div";
            _outputBuffer.Write(codeLine);
            _outputBuffer.Reset();
        }

        /// <summary>
        /// Render the prompt in the results section of the Repl.
        /// </summary>
        internal void ShowPromptInResultDiv() {
            _outputBuffer.ElementProcessor = Formatter.PromptElement;
            _outputBuffer.ElementClass = _sdlrPrompt;
            _outputBuffer.Write(_multiLinePrompt ? SubPromptHtml() : PromptHtml());
            _outputBuffer.Reset();
            if (_multiLine) {
                _multiLinePrompt = true;
            }
        }

        internal void ProcessPromptElement(HtmlElement element) {
            try {
                Formatter.PromptElement(element);
            } catch (Exception e) {
                DynamicApplication.Current.HandleException(this, e);
            }
            _neutralFormatter.PromptElement(element);
        }

        /// <summary>
        /// Render the language-specific result representation in the results
        /// section of the Repl.
        /// </summary>
        /// <param name="result"></param>
        internal void ShowValueInResultDiv(object result) {
            string format = null;
            try {
                format = Formatter.Format(result);
            } catch (Exception e) {
                DynamicApplication.Current.HandleException(this, e);
                format = _neutralFormatter.Format(result);
            }
            _outputBuffer.ElementClass = _sdlrValue;
            _outputBuffer.ElementName = "div";
            if (format != null) {
                _outputBuffer.Write(format);
            }
            _outputBuffer.Reset();
        }

        /// <summary>
        /// Flush the contents of the OutputBuffer.
        /// </summary>
        internal void FlushOutputInResultDiv() {
            _outputBuffer.Flush();
        }
        #endregion

        #region History
        /// <summary>
        /// Render the next command
        /// </summary>
        public void ShowNextCommand() {
            _silverlightDlrReplCode.SetProperty("value", GetNextCommand());
        }

        /// <summary>
        /// Render the previous command
        /// </summary>
        public void ShowPreviousCommand() {
            _silverlightDlrReplCode.SetProperty("value", GetPreviousCommand());
        }
        #endregion

        #endregion
    }

    #region Language Formatting
    public interface IReplFormatter {
        void PromptElement(HtmlElement element);
        string PromptHtml();
        string SubPromptHtml();
        string Format(object obj);
    }

    public class LanguageNeutralFormatter : IReplFormatter {
        public Repl CurrentRepl { get; private set; }

        public LanguageNeutralFormatter(Repl repl) {
            CurrentRepl = repl;
        }

        public void PromptElement(HtmlElement element) {
            // no-op
        }

        public string PromptHtml() {
            return ">>> ";
        }

        public string SubPromptHtml() {
            return "... ";
        }

        public string Format(object obj) {
            return CurrentRepl.Engine.Operations.Format(obj);
        }
    }
    #endregion

    #region Text Buffer
    /// <summary>
    /// Repl's output buffer
    /// </summary>
    public class ReplOutputBuffer : ConsoleWriter {

        public Action<HtmlElement> ElementProcessor;
        public string ElementClass;
        public string ElementName;
        public bool UserOutput;

        private HtmlElement _results;
        private string _outputClass;
        private string _queue;

        public ReplOutputBuffer(HtmlElement results, string outputClass) {
            _results = results;
            _outputClass = outputClass;
            _queue = "";
            Reset();
        }

        public string Queue { get { return _queue; } }

        public override void Write(string str) {
            if (UserOutput) {
                _queue += str;
            } else {
                AppendToResults(str);
            }
        }

        public void flush() {
            Flush();
        }

        public void Reset() {
            ElementProcessor = null;
            ElementClass = null;
            ElementName = "span";
            UserOutput = false;
            ScrollToBottom();
        }

        public override void Flush() {
            if (_queue != string.Empty) {
                AppendToResults(_queue, _outputClass, null);
                _queue = string.Empty;
            }
            ScrollToBottom();
        }

        #region HTML Helpers
        private void AppendToResults(string str) {
            AppendToResults(str, null, null);
        }

        private void AppendToResults(string str, string outputClass, Action<HtmlElement> process) {
            str = str == String.Empty ? " " : str;
            _results.AppendChild(PutTextInNewElement(
                str, ElementName, 
                outputClass ?? ElementClass,
                process ?? ElementProcessor
            ));

        }

        // TODO any library I can use to do this?
        private static string EscapeHtml(string text) {
            return text.Replace("\t", "  ").
                Replace("&", "&amp;").
                Replace(" ", "&nbsp;").
                Replace("<", "&lt;").
                Replace(">", "&gt;").
                Replace("\"", "&quot;").
                Replace(ConsoleWriter.NewLineChar.ToString(), "<br />");
        }

        private HtmlElement PutTextInNewElement(string str, string tagName, string className, Action<HtmlElement> process) {
            var element = HtmlPage.Document.CreateElement(tagName == null ? "span" : tagName);
            if (className != null) {
                element.CssClass = className;
            }
            if (process != null) {
                process(element);
            }
            PutTextInElement(str, element);
            return element;
        }

        internal void PutTextInElement(string str, HtmlElement e) {
            e.SetProperty("innerHTML", EscapeHtml(str));
        }

        internal void AppendTextInElement(string str, HtmlElement e) {
            var toPrepend = e.GetProperty("innerHTML").ToString();
            e.SetProperty("innerHTML", toPrepend + EscapeHtml(str));
        }

        internal void ScrollToBottom() {
            if (_results != null && _results.Parent != null) {
                _results.Parent.SetProperty("scrollTop", _results.Parent.GetProperty("scrollHeight"));
            }
        }
        #endregion
    }

    /// <summary>
    /// Input console buffer 
    /// </summary>
    public class ReplInputBuffer : ConsoleWriter {
        
        private Repl _console;
        
        public ReplInputBuffer(Repl console) {
            _console = console;
        }
        
        public override void Write(string str) {
            string[] lines = str.Split(CoreNewLine);
            if (lines.Length > 1) {
                foreach (var line in lines) {
                    _console.AppendCode(line);
                    _console.RunCode();
                }
            } else if (lines.Length != 0) {
                _console.AppendCode(lines[lines.Length - 1]);
            }
        }
    }

    /// <summary>
    /// Base console writer. Subclasses implement "Write"
    /// </summary>
    public abstract class ConsoleWriter : TextWriter {

        public readonly static char NewLineChar = '\n';
        
        protected Encoding _encoding;
        
        public ConsoleWriter() {
            _encoding = new System.Text.UTF8Encoding();
            CoreNewLine = new char[] { NewLineChar };
        }
        
        public override Encoding Encoding { get { return _encoding; } }
        
        public override void WriteLine(string str) {
            Write(str + "\n");
        }
        
        public abstract override void Write(string str);

        public void write(string str) {
            Write(str);
        }
    }
    #endregion
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.