using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Windows.Forms; using Sunweaver.Parsing; using Sunweaver; using ScintillaNET; namespace Sunweaver.Editor { public partial class DevControl : UserControl { public const double TIME_AFTER_TYPING = 500; public const int ERROR_STYLE = 2; System.Timers.Timer _syntaxCheckTimer; Thread syntaxBackgroundWorker; public delegate void CompileHandler(string dnacode); public delegate void CompileSuccessfulHandler(string dnacode, Chromosome chromosome); public delegate void CodeDirtiedHandler(); public event CompileHandler CompileStarted; public event CompileSuccessfulHandler CompileSuccessful; public event CompileHandler CompileFailed; public event CodeDirtiedHandler CodeDirty; public DevControl() { InitializeComponent(); //make sure line numbers are visible scintilla.Margins[0].Width = 20; defineLanguage(); _syntaxCheckTimer = new System.Timers.Timer(TIME_AFTER_TYPING); _syntaxCheckTimer.Elapsed += delegate(object source, System.Timers.ElapsedEventArgs e) { DoSyntaxCompileCheck(); }; // Just so the events aren't completely empty. CompileSuccessful += delegate(string code, Chromosome chromosome) { statusStrip.Invoke(new MethodInvoker(delegate() { compileStatusLabel.Text = "Good"; compileStatusLabel.BackColor = Color.LightGreen; bpsLabel.Text = "bps: " + chromosome.TotalBasePairs; codulesLabel.Text = "codules: " + chromosome.Codules.Count; charsLabel.Text = "chars: " + code.Length; })); }; CompileFailed += delegate(string code) { statusStrip.Invoke(new MethodInvoker(delegate() { compileStatusLabel.Text = "Errors"; compileStatusLabel.BackColor = Color.LightSalmon; bpsLabel.Text = "bps: ?"; codulesLabel.Text = "codules: ?"; charsLabel.Text = "chars: " + code.Length; })); }; CompileStarted += delegate(string code) { statusStrip.Invoke(new MethodInvoker(delegate() { compileStatusLabel.Text = "Compiling..."; compileStatusLabel.BackColor = Color.FromKnownColor(KnownColor.Control); bpsLabel.Text = "bps: ?"; codulesLabel.Text = "codules: ?"; charsLabel.Text = "chars: " + code.Length; })); }; CodeDirty += delegate() { statusStrip.Invoke(new MethodInvoker(delegate() { compileStatusLabel.Text = "Unanalyzed"; compileStatusLabel.BackColor = Color.FromKnownColor(KnownColor.Control); bpsLabel.Text = "bps: ?"; codulesLabel.Text = "codules: ?"; charsLabel.Text = "chars: ?"; })); }; compileStatusLabel.Text = "Good"; compileStatusLabel.BackColor = Color.LightGreen; codulesLabel.Text = "codules: 1"; bpsLabel.Text = "bps: 0"; charsLabel.Text = "chars: 0"; } private void DoSyntaxCompileCheck() { _syntaxCheckTimer.Stop(); string DNACode = GetCode(); CompileStarted.Invoke(DNACode); if (syntaxBackgroundWorker != null) { // Abort the old DNA syntax check and block until the thread is releasd. syntaxBackgroundWorker.Abort(); syntaxBackgroundWorker.Join(); } syntaxBackgroundWorker = new Thread(delegate() { try { List compilationErrors; var cms = new Chromosome(); try { Parser.ParseDNA(DNACode, ref cms, out compilationErrors); } catch (Exception exc) { if (exc is ThreadAbortException) { throw; } compilationErrors = new List(); compilationErrors.Add(new CompilationMessage { message = exc.Message, token = new Token { column = -1, line = -1, token = "exception compiling DNA", }, }); } clearErrorMessages(); ClearErrorIndicators(); foreach (CompilationMessage cm in compilationErrors) { AddErrorMessage(cm); AddErrorIndicator(cm); } if (compilationErrors.Count == 0) { // This is a synchronous blocking call. // We might want to make this asynchronous at some point. CompileSuccessful.Invoke(DNACode, cms); } else { CompileFailed.Invoke(DNACode); } } catch (ThreadAbortException) { Thread.ResetAbort(); } }); syntaxBackgroundWorker.Start(); } private void defineLanguage() { StringBuilder commandsSB = new StringBuilder(); var commands = String.Join(" ", Sunweaver.DNASystem.Commands.Keys); var sysvars = String.Join(" ", Sunweaver.DNASystem.Sysvars.Keys); // scintilla.Lexing.LineCommentPrefix = "//"; scintilla.SetKeywords(0, commands); if (sysvars.Length > 0) { scintilla.SetKeywords(1, sysvars); } scintilla.Styles[Style.Default].Font = "Courier New"; scintilla.Styles[Style.Default].Size = 10; scintilla.Styles[Style.Cpp.Word].ForeColor = System.Drawing.Color.Blue; scintilla.Styles[Style.Cpp.Word2].ForeColor = System.Drawing.Color.Turquoise; } private void scintilla_KeyToggled(object sender, KeyEventArgs e) { // Reset the syntax check timer _syntaxCheckTimer.Stop(); _syntaxCheckTimer.Start(); CodeDirty.Invoke(); } #region Syntax Errors and Highlighting #region Get code delegation public delegate string GetCodeDelegator(); public string GetCode() { return InvokeGetCodeThreaded(); } public string InvokeGetCodeThreaded() { if (scintilla.InvokeRequired) { return (string)scintilla.Invoke(new GetCodeDelegator(GetCodeThreaded)); } return GetCodeThreaded(); } private string GetCodeThreaded() { return scintilla.Text; } #endregion #region ADD - error message public delegate void AddErrorMessageDelegator(CompilationMessage cm); public void AddErrorMessage(CompilationMessage cm) { InvokeAddErrorMessageThreaded(cm); } public void InvokeAddErrorMessageThreaded(CompilationMessage cm) { if (compilationMessagesList.InvokeRequired) { compilationMessagesList.Invoke(new AddErrorMessageDelegator(AddErrorMessageThreaded), new object[] { cm }); } else { AddErrorMessageThreaded(cm); } } private void AddErrorMessageThreaded(CompilationMessage cm) { ListViewItem item = null; if (cm.message.Contains("__anon")) { string splitString = cm.message.Split(new string[] { "__anon" }, StringSplitOptions.None)[0]; item = new ListViewItem(string.Concat(splitString, "{")); } else { item = new ListViewItem(cm.message); } item.SubItems.Add(cm.token.line.ToString()); item.SubItems.Add(cm.token.column.ToString()); compilationMessagesList.Items.Add(item); } #endregion #region CLEAR - error message public delegate void ClearErrorMessagesDelegator(); public void clearErrorMessages() { InvokeClearErrorMessagesThreaded(); } public void InvokeClearErrorMessagesThreaded() { if (compilationMessagesList.InvokeRequired) { compilationMessagesList.Invoke(new ClearErrorMessagesDelegator(ClearErrorMessagesThreaded)); } else { ClearErrorMessagesThreaded(); } } private void ClearErrorMessagesThreaded() { compilationMessagesList.Items.Clear(); } #endregion #region ADD - error message indicators public delegate void AddErrorIndicatorDelegator(CompilationMessage cm); public void AddErrorIndicator(CompilationMessage cm) { InvokeAddErrorIndicatorThreaded(cm); } public void InvokeAddErrorIndicatorThreaded(CompilationMessage cm) { if (scintilla.InvokeRequired) { scintilla.Invoke(new AddErrorIndicatorDelegator(AddErrorIndicatorThreaded), new object[] { cm }); } else { AddErrorIndicatorThreaded(cm); } } // Chosen arbitrarily const int SquiggleIndicatorIndex = 8; private void AddErrorIndicatorThreaded(CompilationMessage cm) { scintilla.IndicatorCurrent = SquiggleIndicatorIndex; scintilla.Indicators[SquiggleIndicatorIndex].Style = IndicatorStyle.Squiggle; string tokenString = cm.token.token; int start = getPosition(cm.token.line, cm.token.column); if (tokenString.Contains("__anon")) { string splitString = tokenString.Split(new string[] { "__anon" }, StringSplitOptions.None)[0]; Console.WriteLine(splitString); scintilla.IndicatorFillRange(start, splitString.Length); } else { scintilla.IndicatorFillRange(start, tokenString.Length); } } #endregion #region CLEAR - error message indicators public delegate void ClearErrorIndicatorsDelegator(); public void ClearErrorIndicators() { InvokeClearErrorIndicatorsThreaded(); } public void InvokeClearErrorIndicatorsThreaded() { if (scintilla.InvokeRequired) { scintilla.Invoke(new ClearErrorIndicatorsDelegator(ClearErrorIndicatorsThreaded)); } else { ClearErrorIndicatorsThreaded(); } } private void ClearErrorIndicatorsThreaded() { scintilla.IndicatorCurrent = SquiggleIndicatorIndex; scintilla.IndicatorClearRange(0, scintilla.TextLength); } #endregion #endregion //syntax errors #region Get code delegation public delegate void SetTextDelegator(string code); public void SetText(string code) { InvokeSetTextThreaded(code); CodeDirty.Invoke(); DoSyntaxCompileCheck(); } public void InvokeSetTextThreaded(string code) { if (scintilla.InvokeRequired) { scintilla.Invoke(new SetTextDelegator(SetTextThreaded), new object[] {code}); } SetTextThreaded(code); } private void SetTextThreaded(string code) { scintilla.Text = code; } #endregion #region undo/redo public Boolean canUndo() { return scintilla.CanUndo; } public Boolean canRedo() { return scintilla.CanRedo; } public void undo() { scintilla.Undo(); } public void redo() { scintilla.Redo(); } public void ClearUndoStack() { scintilla.EmptyUndoBuffer(); } #endregion private void compilationMessagesList_ItemActivate(object sender, EventArgs e) { int line = Convert.ToInt32(compilationMessagesList.SelectedItems[0].SubItems[1].Text); int column = Convert.ToInt32(compilationMessagesList.SelectedItems[0].SubItems[2].Text); scintilla.CurrentPosition = getPosition(line, column); scintilla.ScrollCaret(); scintilla.Focus(); } private int getPosition(int line, int column) { return scintilla.Lines[line - 1].Position + column - 1; } public void showFind() { //scintilla.FindReplace.ShowFind(); } public void showFindAndReplace() { //scintilla.FindReplace.ShowReplace(); } } }