improve CodeMirror integration
authorDenis Ovsienko <denis@ovsienko.info>
Fri, 26 May 2017 18:07:34 +0000 (19:07 +0100)
committerDenis Ovsienko <denis@ovsienko.info>
Fri, 26 May 2017 18:14:55 +0000 (19:14 +0100)
Reimplement the RackCode language mode in rackcode.js using simpler
JavaScript code and define a standalone "rackcode" theme in rackcode.css
instead of overriding the default theme. In renderRackCodeEditor() pass
more properties to CodeMirror constructor and make minor HTML fixups.
In renderRackCodeViewer() rewrite the code to use CodeMirror in read-only
mode and to scroll to requested line on request. Amend the URL format in
refRCLineno() respectively.

ChangeLog
wwwroot/css/codemirror/rackcode.css
wwwroot/inc/code.php
wwwroot/inc/interface-config.php
wwwroot/js/codemirror/rackcode.js

index 8c923032879faffa98f0f220abbf7e4a1cebe631..8fc77abd231ad4dcde2e64b2caebd08c39ab6e3d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,7 @@
 0.21.0
        update: display MySQL warnings in debug mode
        update: better display objects that have no common name
+       update: highlight RackCode syntax in the Permissions viewer too
 0.20.13 2017-05-12
        update: fix performance for pages with images (GH#190 by Michael A. Mikhailov)
        update: improve SNMP support for Brocade devices (GH#180 by Chris Jones)
index 39e4da8c9150fcdac0523ba96febd81cca71ad5a..d2f53f757bcd4167a67f85544e6e85e40fcee727 100644 (file)
@@ -1,3 +1,7 @@
-.CodeMirror {height: 500px;}
-.cm-s-default .cm-operator {color: blue;}
-.cm-s-default .cm-tag {color: orange;}
+.CodeMirror { height: 500px; }
+.cm-s-rackcode span.cm-comment { font-style: italic; color: gray; }
+.cm-s-rackcode span.cm-operator { color: blue; }
+.cm-s-rackcode span.cm-variable { color: orange; }
+.cm-s-rackcode span.cm-def { color: magenta; }
+.cm-s-rackcode span.cm-keyword { font-weight: bold; }
+.cm-s-rackcode span.cm-atom { font-weight: normal; color: brown; }
index 9a38765017803128e454869c0aa40f11ca3cdeef..bc0add7d12d44107d6c836e66cb857a6c06b75dc 100644 (file)
@@ -452,7 +452,7 @@ class RackCodeParser
 
 function refRCLineno ($ln)
 {
-       return "<a href='index.php?page=perms&tab=default#line${ln}'>line ${ln}</a>";
+       return "<a href='index.php?page=perms&tab=default&line=${ln}'>line ${ln}</a>";
 }
 
 // returns warning message or NULL
index 638e8735a4129c30d2077833a92b44fa644338f8..e59fa42f4fa1aa96e3ceb652ccaecd2b457ab737 100644 (file)
@@ -88,15 +88,34 @@ function renderUserProperties ($user_id)
 
 function renderRackCodeViewer ()
 {
-       $text = loadScript ('RackCode');
        echo '<table width="100%" border=0>';
-       $lineno = 1;
-       foreach (explode ("\n", $text) as $line)
+       addJS ('js/codemirror/codemirror.js');
+       addJS ('js/codemirror/rackcode.js');
+       addCSS ('css/codemirror/codemirror.css');
+       addCSS ('css/codemirror/rackcode.css');
+       if (! array_key_exists ('line', $_REQUEST))
+               $scrollcode = '';
+       else
        {
-               echo "<tr><td class=tdright><a name=line${lineno}>${lineno}</a></td>";
-               echo "<td class=tdleft>${line}</td></tr>";
-               $lineno++;
+               // Line numbers start from 0 in CodeMirror API and from 1 elsewhere.
+               $lineno = genericAssertion ('line', 'uint') - 1;
+               $scrollcode = "rackCodeMirror.addLineClass (${lineno}, 'wrap', 'border_highlight');\n" .
+                       "rackCodeMirror.scrollIntoView ({line: ${lineno}, ch: 0}, 50);\n";
        }
+       addJS (<<<ENDJAVASCRIPT
+$(document).ready(function() {
+       var rackCodeMirror = CodeMirror.fromTextArea(document.getElementById("RCTA"),{
+               mode:'rackcode',
+               theme:'rackcode',
+               readOnly:'nocursor',
+               lineNumbers:true });
+       ${scrollcode}
+});
+ENDJAVASCRIPT
+       , TRUE);
+       echo "<tr><td><textarea rows=40 cols=100 id=RCTA>";
+       echo loadScript ('RackCode') . "</textarea></td></tr>\n";
+       echo '</table>';
 }
 
 function renderRackCodeEditor ()
@@ -138,6 +157,8 @@ $(document).ready(function() {
 
        var rackCodeMirror = CodeMirror.fromTextArea(document.getElementById("RCTA"),{
                mode:'rackcode',
+               theme:'rackcode',
+               autofocus:true,
                lineNumbers:true });
        rackCodeMirror.on("change",function(cm,cmChangeObject){
                $("#RCTA").text(cm.getValue());
@@ -146,12 +167,11 @@ $(document).ready(function() {
 ENDJAVASCRIPT
        , TRUE);
 
-       $text = loadScript ('RackCode');
        printOpFormIntro ('saveRackCode');
-       echo '<table style="width:100%;border:1px;" border=0 align=center>';
+       echo '<table width="100%" border=0>';
        echo "<tr><td><textarea rows=40 cols=100 name=rackcode id=RCTA>";
-       echo $text . "</textarea></td></tr>\n";
-       echo "<tr><td align=center>";
+       echo loadScript ('RackCode') . "</textarea></td></tr>\n";
+       echo "<tr><td class=submit>";
        echo '<div id="ShowMessage"></div>';
        echo "<input type='button' value='Verify' onclick='verify();'>";
        echo "<input type='submit' value='Save' disabled='disabled' id='SaveChanges'>";
index a71e1b82ed2113ceff8f7218f3e64b7f3ddeea1d..505904972be3819ade10eaf6d56cf2f444f2cdeb 100644 (file)
@@ -7,52 +7,37 @@
     mod(CodeMirror);
 })(function(CodeMirror) {
 "use strict";
-CodeMirror.defineMode('rackcode', function() {
-  var allowkeywords = /^(allow)\b/i;
-  var denykeywords = /^(deny)\b/i;
-  var contextkeywords = /^(context|clear|insert|remove|on)\b/i;
-  var operatorkeywords = /^(define|and|or|not|true|false)\b/i;
-
-  return {
-    token: function(stream, state) {
-
-      if (stream.eatSpace())
-        return null;
-
-      var w;
-
-      if (stream.eatWhile(/\w/)) {
-        w = stream.current();
-
-
-               if (allowkeywords.test(w)) {
-            return 'positive';
-               } else if (denykeywords.test(w)) {
-            return 'negative';
-        } else if (operatorkeywords.test(w)) {
-            return 'operator';
-        } else if (contextkeywords.test(w)) {
-            return 'keyword';
-        }
-
-      } else if (stream.eat('#')) {
-        stream.skipToEnd();
-        return 'comment';
-      } else if (stream.eat('{')) {
-        while (w = stream.next()) {
-          if (w == '}')
-            break;
-
-          if (w == '\\')
-            stream.next();
-        }
-        return 'tag';
-      }  else {
-        stream.next();
-      }
-      return null;
-    }
-  };
+CodeMirror.defineMode('rackcode', function()
+{
+       return {
+               token: function (stream)
+               {
+                       const WORDS =
+                       {
+                               'allow':   'keyword positive',
+                               'deny':    'keyword negative',
+                               'define':  'keyword',
+                               'context': 'keyword',
+                               'clear':   'keyword',
+                               'insert':  'keyword',
+                               'remove':  'keyword',
+                               'on':      'keyword',
+                               'true':    'atom',
+                               'false':   'atom',
+                               'and':     'operator',
+                               'or':      'operator',
+                               'not':     'operator',
+                       };
+                       return stream.eatSpace() ? null :
+                               stream.eat ('(') ? 'bracket' :
+                               stream.eat (')') ? 'bracket' :
+                               stream.match (/^#.*$/) ? 'comment' :
+                               stream.match (/^{[^{}]+}/) ? 'variable' : // a tag
+                               stream.match (/^\[[^\[\]]+\]/) ? 'def' : // a predicate
+                               stream.eatWhile (/\S/) ? WORDS[stream.current()] :
+                               null;
+               }
+       };
 });
 
 CodeMirror.defineMIME("text/x-rackcode", "rackcode");