对于一个规则引擎中的脚本代码编辑器是非常关键的,因为UI控件直接使用对象是规则维护者,关系到用户体验,在选用脚本编辑器的功能时除了满足代码的编辑的基本编辑要求外,功能还需要包含;语法着色,错误提示,代码格式化,代码折叠,自动代码提示,自动补全代码等功能.(目前技术无法做到JS加断点;边调试边运行)
经过众多开源产品的代码编辑器对比与功能测试,ICSharpCode.TextEditor 是JS脚本编辑器最佳选择.
一.语法着色
JS语法着色XML,对JS关键字进行着色,并可以扩展自指定义关键字着色,
应用场影:规则引擎中新增加函数或变量等关键字时, 为用户体验更好,可增加自定义关键字着色;
例:BoardThick 是关键字,那么在JS代码中,以高亮着色加以区分
<?xml version="1.0"?> <!-- syntaxdefinition for JavaScript 2.0 by Svante Lidman --> <SyntaxDefinition name ="JavaScript" extensions = ".js"> <Properties> <Property name="LineComment" value="//"/> </Properties> <Digits name ="Digits" bold ="false" italic ="false" color ="DarkBlue"/> <RuleSets> <RuleSet ignorecase = "false"> <Delimiters>=!><+-/*%&|^~.}{,;][?:</Delimiters> <Span name ="LineComment" bold ="false" italic ="false" color ="DarkSlateGray" stopateol ="true"> <Begin>//</Begin> </Span> <Span name ="BlockComment" bold ="false" italic ="false" color ="DarkSlateGray" stopateol ="false"> <Begin>/*</Begin> <End>*/</End> </Span> <Span name ="String" bold ="false" italic ="false" color ="Sienna" stopateol ="false" escapecharacter="\"> <Begin>"</Begin> <End>"</End> </Span> <Span name = "Character" bold = "false" italic = "false" color = "Sienna" stopateol = "true" escapecharacter="\"> <Begin>'</Begin> <End>'</End> </Span> <KeyWords name ="JavaScriptKeyWords" bold="false" italic = "false" color = "Blue"> <Key word = "break" /> <Key word = "continue" /> <Key word = "delete" /> <Key word = "else" /> <Key word = "for" /> <Key word = "function" /> <Key word = "if" /> <Key word = "in" /> <Key word = "new" /> <Key word = "return" /> <Key word = "this" /> <Key word = "typeof" /> <Key word = "var" /> <Key word = "void" /> <Key word = "while" /> <Key word = "with" /> <!--ECMAScript keywords--> <!-- Reserved for future use (some are already used in some Javascript Engines) --> <Key word = "abstract" /> <Key word = "boolean" /> <Key word = "byte" /> <Key word = "case" /> <Key word = "catch" /> <Key word = "char" /> <Key word = "class" /> <Key word = "const" /> <Key word = "debugger" /> <Key word = "default" /> <Key word = "do" /> <Key word = "double" /> <Key word = "enum" /> <Key word = "export" /> <Key word = "extends" /> <Key word = "final" /> <Key word = "finally" /> <Key word = "float" /> <Key word = "goto" /> <Key word = "implements" /> <Key word = "import" /> <Key word = "instanceof" /> <Key word = "int" /> <Key word = "interface" /> <Key word = "long" /> <Key word = "native" /> <Key word = "package" /> <Key word = "private" /> <Key word = "protected" /> <Key word = "public" /> <Key word = "short" /> <Key word = "static" /> <Key word = "super" /> <Key word = "switch" /> <Key word = "synchronized" /> <Key word = "throw" /> <Key word = "throws" /> <Key word = "transient" /> <Key word = "try" /> <Key word = "volatile" /> </KeyWords> <KeyWords name ="JavaScriptIntrinsics" bold="false" italic = "false" color = "Blue"> <Key word = "Array" /> <Key word = "Boolean" /> <Key word = "Date" /> <Key word = "Function" /> <Key word = "Global" /> <Key word = "Math" /> <Key word = "Number" /> <Key word = "Object" /> <Key word = "RegExp" /> <Key word = "String" /> </KeyWords> <KeyWords name ="JavaScriptLiterals" bold="false" italic = "false" color = "Blue"> <Key word = "false" /> <Key word = "null" /> <Key word = "true" /> <Key word = "NaN" /> <Key word = "Infinity" /> </KeyWords> <KeyWords name ="JavaScriptLiterals" bold="false" italic = "false" color = "Blue"> <Key word = "" /> </KeyWords> <KeyWords name ="JavaScriptGlobalFunctions" bold="false" italic = "false" color = "Blue"> <Key word = "eval" /> <Key word = "parseInt" /> <Key word = "parseFloat" /> <Key word = "escape" /> <Key word = "unescape" /> <Key word = "isNaN" /> <Key word = "isFinite" /> </KeyWords> <KeyWords name ="JavaScriptUserFunctions" bold="false" italic = "false" color = "Blue"> <Key word = "console" /> <Key word = "JSON" /> <Key word = "console.log" /> <Key word = "console.alert" /> <Key word = "console.prompt" /> <Key word = "console.print" /> </KeyWords> </RuleSet> </RuleSets> </SyntaxDefinition>
再来一个C#语法着色 XML供参考
<?xml version="1.0"?> <!-- syntaxdefinition for C# 2000 by Mike Krueger --> <SyntaxDefinition name = "C#" extensions = ".cs"> <Environment> <Custom name="TypeReference" bold="false" italic="false" color="#04ABAB" /> <Custom name="UnknownEntity" bold="false" italic="false" color="#AB0404" /> </Environment> <Properties> <Property name="LineComment" value="//"/> </Properties> <Digits name = "Digits" bold = "false" italic = "false" color = "DarkBlue"/> <RuleSets> <RuleSet ignorecase="false"> <Delimiters>&<>~!%^*()-+=|\#/{}[]:;"' , .?</Delimiters> <Span name = "PreprocessorDirectives" rule = "PreprocessorSet" bold="false" italic="false" color="Green" stopateol = "true"> <Begin>#</Begin> </Span> <Span name = "DocLineComment" rule = "DocCommentSet" bold = "false" italic = "false" color = "Green" stopateol = "true" noescapesequences="true"> <Begin bold = "false" italic = "false" color = "Gray">///@!/@</Begin> </Span> <Span name = "LineComment" rule = "CommentMarkerSet" bold = "false" italic = "false" color = "Green" stopateol = "true"> <Begin>//@!/@</Begin> </Span> <Span name = "LineComment2" rule = "CommentMarkerSet" bold = "false" italic = "false" color = "Green" stopateol = "true"> <Begin></Begin> </Span> <Span name = "BlockComment" rule = "CommentMarkerSet" bold = "false" italic = "false" color = "Green" stopateol = "false"> <Begin>/*</Begin> <End>*/</End> </Span> <Span name = "String" bold = "false" italic = "false" color = "Blue" stopateol = "true" escapecharacter="\"> <Begin>"</Begin> <End>"</End> </Span> <Span name = "MultiLineString" bold = "false" italic = "false" color = "Blue" stopateol = "false" escapecharacter='"'> <Begin>@@"</Begin> <End>"</End> </Span> <Span name = "Char" bold = "false" italic = "false" color = "Magenta" stopateol = "true" escapecharacter="\"> <Begin>'</Begin> <End>'</End> </Span> <MarkPrevious bold = "true" italic = "false" color = "MidnightBlue">(</MarkPrevious> <KeyWords name = "Punctuation" bold = "false" italic = "false" color = "DarkGreen"> <Key word = "?" /> <Key word = "," /> <Key word = "." /> <Key word = ";" /> <Key word = "(" /> <Key word = ")" /> <Key word = "[" /> <Key word = "]" /> <Key word = "{" /> <Key word = "}" /> <Key word = "+" /> <Key word = "-" /> <Key word = "/" /> <Key word = "%" /> <Key word = "*" /> <Key word = "<" /> <Key word = ">" /> <Key word = "^" /> <Key word = "=" /> <Key word = "~" /> <Key word = "!" /> <Key word = "|" /> <Key word = "&" /> </KeyWords> <KeyWords name = "AccessKeywords" bold="true" italic="false" color="Black"> <Key word = "this" /> <Key word = "base" /> </KeyWords> <KeyWords name = "OperatorKeywords" bold="true" italic="false" color="DarkCyan"> <Key word = "as" /> <Key word = "is" /> <Key word = "new" /> <Key word = "sizeof" /> <Key word = "typeof" /> <Key word = "true" /> <Key word = "false" /> <Key word = "stackalloc" /> </KeyWords> <KeyWords name = "SelectionStatements" bold="true" italic="false" color="Blue"> <Key word = "else" /> <Key word = "if" /> <Key word = "switch" /> <Key word = "case" /> <Key word = "default" /> </KeyWords> <KeyWords name = "IterationStatements" bold="true" italic="false" color="Blue"> <Key word = "do" /> <Key word = "for" /> <Key word = "foreach" /> <Key word = "in" /> <Key word = "while" /> </KeyWords> <KeyWords name = "JumpStatements" bold="false" italic="false" color="Navy"> <Key word = "break" /> <Key word = "continue" /> <Key word = "goto" /> <Key word = "return" /> </KeyWords> <KeyWords name = "ContextKeywords" bold="false" italic="false" color="Navy"> <Key word = "yield" /> <Key word = "partial" /> <Key word = "global" /> <Key word = "where" /> <Key word = "select" /> <Key word = "group" /> <Key word = "by" /> <Key word = "into" /> <Key word = "from" /> <Key word = "ascending" /> <Key word = "descending" /> <Key word = "orderby" /> <Key word = "let" /> <Key word = "join" /> <Key word = "on" /> <Key word = "equals" /> <Key word = "var" /> </KeyWords> <KeyWords name = "ExceptionHandlingStatements" bold="true" italic="false" color="Teal"> <Key word = "try" /> <Key word = "throw" /> <Key word = "catch" /> <Key word = "finally" /> </KeyWords> <KeyWords name = "CheckedUncheckedStatements" bold="true" italic="false" color="DarkGray"> <Key word = "checked" /> <Key word = "unchecked" /> </KeyWords> <KeyWords name = "UnsafeFixedStatements" bold="false" italic="false" color="Olive"> <Key word = "fixed" /> <Key word = "unsafe" /> </KeyWords> <KeyWords name = "ValueTypes" bold="true" italic="false" color="Red"> <Key word = "bool" /> <Key word = "byte" /> <Key word = "char" /> <Key word = "decimal" /> <Key word = "double" /> <Key word = "enum" /> <Key word = "float" /> <Key word = "int" /> <Key word = "long" /> <Key word = "sbyte" /> <Key word = "short" /> <Key word = "struct" /> <Key word = "uint" /> <Key word = "ushort" /> <Key word = "ulong" /> </KeyWords> <KeyWords name = "ReferenceTypes" bold="false" italic="false" color="Red"> <Key word = "class" /> <Key word = "interface" /> <Key word = "delegate" /> <Key word = "object" /> <Key word = "string" /> </KeyWords> <KeyWords name = "Void" bold="false" italic="false" color="Red"> <Key word = "void" /> </KeyWords> <KeyWords name = "ConversionKeyWords" bold="true" italic="false" color="Pink"> <Key word = "explicit" /> <Key word = "implicit" /> <Key word = "operator" /> </KeyWords> <KeyWords name = "MethodParameters" bold="true" italic="false" color="DeepPink"> <Key word = "params" /> <Key word = "ref" /> <Key word = "out" /> </KeyWords> <KeyWords name = "Modifiers" bold="false" italic="false" color="Brown"> <Key word = "abstract" /> <Key word = "const" /> <Key word = "event" /> <Key word = "extern" /> <Key word = "override" /> <Key word = "readonly" /> <Key word = "sealed" /> <Key word = "static" /> <Key word = "virtual" /> <Key word = "volatile" /> </KeyWords> <KeyWords name = "AccessModifiers" bold="true" italic="false" color="Blue"> <Key word = "public" /> <Key word = "protected" /> <Key word = "private" /> <Key word = "internal" /> </KeyWords> <KeyWords name = "NameSpaces" bold="true" italic="false" color="Green"> <Key word = "namespace" /> <Key word = "using" /> </KeyWords> <KeyWords name = "LockKeyWord" bold="false" italic="false" color="DarkViolet"> <Key word = "lock" /> </KeyWords> <KeyWords name = "GetSet" bold="false" italic="false" color="SaddleBrown"> <Key word = "get" /> <Key word = "set" /> <Key word = "add" /> <Key word = "remove" /> </KeyWords> <KeyWords name = "Literals" bold="true" italic="false" color="Black"> <Key word = "null" /> <Key word = "value" /> </KeyWords> </RuleSet> <RuleSet name = "CommentMarkerSet" ignorecase = "false"> <Delimiters><>~!@%^*()-+=|\#/{}[]:;"' , .?</Delimiters> <KeyWords name = "ErrorWords" bold="true" italic="false" color="Red"> <Key word = "TODO" /> <Key word = "FIXME" /> </KeyWords> <KeyWords name = "WarningWords" bold="true" italic="false" color="#EEE0E000"> <Key word = "HACK" /> <Key word = "UNDONE" /> </KeyWords> </RuleSet> <RuleSet name = "DocCommentSet" ignorecase = "false"> <Delimiters><>~!@%^*()-+=|\#/{}[]:;"' , .?</Delimiters> <Span name = "XmlTag" rule = "XmlDocSet" bold = "false" italic = "false" color = "Gray" stopateol = "true"> <Begin><</Begin> <End>></End> </Span> <KeyWords name = "ErrorWords" bold="true" italic="false" color="Red"> <Key word = "TODO" /> <Key word = "FIXME" /> </KeyWords> <KeyWords name = "WarningWords" bold="true" italic="false" color="#EEE0E000"> <Key word = "HACK" /> <Key word = "UNDONE" /> </KeyWords> </RuleSet> <RuleSet name = "PreprocessorSet" ignorecase="false"> <Delimiters>&<>~!%^*()-+=|\#/{}[]:;"' , .?</Delimiters> <KeyWords name = "PreprocessorDirectives" bold="true" italic="false" color="Green"> <Key word = "if" /> <Key word = "else" /> <Key word = "elif" /> <Key word = "endif" /> <Key word = "define" /> <Key word = "undef" /> <Key word = "warning" /> <Key word = "error" /> <Key word = "line" /> <Key word = "region" /> <Key word = "endregion" /> <Key word = "pragma" /> </KeyWords> </RuleSet> <RuleSet name = "XmlDocSet" ignorecase = "false"> <Delimiters><>~!@%^*()-+=|\#/{}[]:;"' , .?</Delimiters> <Span name = "String" bold = "true" italic = "false" color = "Silver" stopateol = "true"> <Begin>"</Begin> <End>"</End> </Span> <KeyWords name = "Punctuation" bold = "true" italic = "false" color = "Gray"> <Key word = "/" /> <Key word = "|" /> <Key word = "=" /> </KeyWords> <KeyWords name = "SpecialComment" bold="true" italic="false" color="Gray"> <Key word = "c" /> <Key word = "code" /> <Key word = "example" /> <Key word = "exception" /> <Key word = "list" /> <Key word = "para" /> <Key word = "param" /> <Key word = "paramref" /> <Key word = "permission" /> <Key word = "remarks" /> <Key word = "returns" /> <Key word = "see" /> <Key word = "seealso" /> <Key word = "summary" /> <Key word = "value" /> <Key word = "inheritdoc" /> <Key word = "type" /> <Key word = "name" /> <Key word = "cref" /> <Key word = "item" /> <Key word = "term" /> <Key word = "description" /> <Key word = "listheader" /> </KeyWords> </RuleSet> </RuleSets> </SyntaxDefinition>
二.错误提示
在测试JS代码时,点击运行JS,当报错时直接定位执行JS代码的异常代码.
string result = ""; string resultType = ""; try { var resultJS = context.Run(jsCode); if (resultJS == null) { result = "null"; resultType = "null"; } else { resultType = resultJS.GetType().ToString(); result = Newtonsoft.Json.JsonConvert.SerializeObject(resultJS); } } catch (Exception ex) { string CodeLine = ""; if (ex.Data.Contains("V8SourceLine")) { var ErrCode = ex.Data["V8SourceLine"]; if (ErrCode != null) { CodeLine = CodeLine + "\r\n错误定位代码\r\n" + ErrCode.ToString().Trim(); ; } } MessageBox.Show("报错内容:\r\n" + ex.Message + CodeLine, "JavaScript提示", MessageBoxButtons.OK, MessageBoxIcon.Error); }
三.代码格式化
ICSharpCode.TextEditor 控件中没有现成的,这里跟据{}扩号,多层嵌套加首空格的方式进行代码格式化功能
/// <summary> /// 格式化JS代码 /// </summary> /// <param name="code"></param> /// <returns></returns> public static string FormatJSCode(string code) { //去除空白行 code = RemoveEmptyLines(code); StringBuilder sb = new StringBuilder(); int count = 2; int times = 0; string[] lines = code.Split('\n'); foreach (var line in lines) { if (line.TrimStart().StartsWith("{") || line.TrimEnd().EndsWith("{")) { sb.Append(Indent(count * times) + line.TrimStart() + "\r\n"); times++; } else if (line.TrimStart().StartsWith("}")) { times--; if (times <= 0) { times = 0; } sb.Append(Indent(count * times) + line.TrimStart() + "\r\n"); } else { sb.Append(Indent(count * times) + line.TrimStart() + "\r\n"); } } return sb.ToString(); }
四.补全扩号{} ()[]
当用户敲左扩号时,自动补充右扩号,并将光标点移到2个扩号中间.
private void txtContent_TextChanged(object sender, EventArgs e) { txtContent.Document.FoldingManager.UpdateFoldings(null, null); if (txtContent.Text.Length <= oldJScodeLength) return; var Line = this.txtContent.ActiveTextAreaControl.Caret.Line; var offset = this.txtContent.ActiveTextAreaControl.Caret.Offset; if (offset == 0) return; var LineSegment = txtContent.ActiveTextAreaControl.TextArea.Document.GetLineSegment(Line); if (offset == LineSegment.Length) return; try { var charT = txtContent.ActiveTextAreaControl.TextArea.Document.GetText(offset, 1); var charE = ""; switch (charT) { case "{" : charE = "}"; break; case "(": charE = ")"; break; case "[": charE = "]"; break; } if (!string.IsNullOrEmpty(charE)) { txtContent.ActiveTextAreaControl.SelectionManager.RemoveSelectedText(); txtContent.ActiveTextAreaControl.Caret.Column = txtContent.ActiveTextAreaControl.Caret.Column + 1; txtContent.ActiveTextAreaControl.TextArea.InsertChar(charE[0]); txtContent.ActiveTextAreaControl.Caret.Column = txtContent.ActiveTextAreaControl.Caret.Column - 2; this.txtContent.ActiveTextAreaControl.TextArea.ScrollToCaret(); } } catch (Exception) { } }
五. 当前光标信息
当前光标所在行,列,所选字符数,基本功能扩展,
private void JSPositionChanged(object sender, EventArgs e) { ICSharpCode.TextEditor.Caret CaretPosition = (ICSharpCode.TextEditor.Caret)sender; toolStripStatusLabel1.Text = $@"行:{CaretPosition.Line + 1} 列:{CaretPosition.Column + 1} 偏移:{CaretPosition.Offset + 1}"; toolStripStatusLabel3.Text = CaretPosition.CaretMode.ToString(); } private void JSSelectionChanged(object sender, EventArgs e) { SelectionManager selContent = (SelectionManager)sender; toolStripStatusLabel2.Text = $@"选中字符数:{selContent.SelectedText.Length}"; btnRunJSselect.Enabled = selContent.SelectedText.Length > 0; }