CodeMirror : mIRC mode

By FordLawnmower on Feb 23, 2013

This is a mIRC mode for use with {} CodeMirror http://codemirror.net/
Adds syntax highlighting for mSL code.
Screenshot:
Image

//mIRC mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara
CodeMirror.defineMIME("text/mirc", "mirc");
CodeMirror.defineMode("mirc", function() {
  function parseWords(str) {
    var obj = {}, words = str.split(" ");
    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
    return obj;
  }
  var specials = parseWords("$! $$ $& $? $+ $abook $abs $active $activecid " +
    "$activewid $address $addtok $agent $agentname $agentstat $agentver " +
    "$alias $and $anick $ansi2mirc $aop $appactive $appstate $asc $asctime " +
    "$asin $atan $avoice $away $awaymsg $awaytime $banmask $base $bfind " +
    "$binoff $biton $bnick $bvar $bytes $calc $cb $cd $ceil $chan $chanmodes " +
    "$chantypes $chat $chr $cid $clevel $click $cmdbox $cmdline $cnick $color " +
    "$com $comcall $comchan $comerr $compact $compress $comval $cos $count " +
    "$cr $crc $creq $crlf $ctime $ctimer $ctrlenter $date $day $daylight " +
    "$dbuh $dbuw $dccignore $dccport $dde $ddename $debug $decode $decompress " +
    "$deltok $devent $dialog $did $didreg $didtok $didwm $disk $dlevel $dll " +
    "$dllcall $dname $dns $duration $ebeeps $editbox $emailaddr $encode $error " +
    "$eval $event $exist $feof $ferr $fgetc $file $filename $filtered $finddir " +
    "$finddirn $findfile $findfilen $findtok $fline $floor $fopen $fread $fserve " +
    "$fulladdress $fulldate $fullname $fullscreen $get $getdir $getdot $gettok $gmt " +
    "$group $halted $hash $height $hfind $hget $highlight $hnick $hotline " +
    "$hotlinepos $ial $ialchan $ibl $idle $iel $ifmatch $ignore $iif $iil " +
    "$inelipse $ini $inmidi $inpaste $inpoly $input $inrect $inroundrect " +
    "$insong $instok $int $inwave $ip $isalias $isbit $isdde $isdir $isfile " +
    "$isid $islower $istok $isupper $keychar $keyrpt $keyval $knick $lactive " +
    "$lactivecid $lactivewid $left $len $level $lf $line $lines $link $lock " +
    "$lock $locked $log $logstamp $logstampfmt $longfn $longip $lower $ltimer " +
    "$maddress $mask $matchkey $matchtok $md5 $me $menu $menubar $menucontext " +
    "$menutype $mid $middir $mircdir $mircexe $mircini $mklogfn $mnick $mode " +
    "$modefirst $modelast $modespl $mouse $msfile $network $newnick $nick $nofile " +
    "$nopath $noqt $not $notags $notify $null $numeric $numok $oline $onpoly " +
    "$opnick $or $ord $os $passivedcc $pic $play $pnick $port $portable $portfree " +
    "$pos $prefix $prop $protect $puttok $qt $query $rand $r $rawmsg $read $readomo " +
    "$readn $regex $regml $regsub $regsubex $remove $remtok $replace $replacex " +
    "$reptok $result $rgb $right $round $scid $scon $script $scriptdir $scriptline " +
    "$sdir $send $server $serverip $sfile $sha1 $shortfn $show $signal $sin " +
    "$site $sline $snick $snicks $snotify $sock $sockbr $sockerr $sockname " +
    "$sorttok $sound $sqrt $ssl $sreq $sslready $status $strip $str $stripped " +
    "$syle $submenu $switchbar $tan $target $ticks $time $timer $timestamp " +
    "$timestampfmt $timezone $tip $titlebar $toolbar $treebar $trust $ulevel " +
    "$ulist $upper $uptime $url $usermode $v1 $v2 $var $vcmd $vcmdstat $vcmdver " +
    "$version $vnick $vol $wid $width $wildsite $wildtok $window $wrap $xor");
  var keywords = parseWords("abook ajinvite alias aline ame amsg anick aop auser autojoin avoice " +
    "away background ban bcopy beep bread break breplace bset btrunc bunset bwrite " +
    "channel clear clearall cline clipboard close cnick color comclose comopen " +
    "comreg continue copy creq ctcpreply ctcps dcc dccserver dde ddeserver " +
    "debug dec describe dialog did didtok disable disconnect dlevel dline dll " +
    "dns dqwindow drawcopy drawdot drawfill drawline drawpic drawrect drawreplace " +
    "drawrot drawsave drawscroll drawtext ebeeps echo editbox emailaddr enable " +
    "events exit fclose filter findtext finger firewall flash flist flood flush " +
    "flushini font fopen fseek fsend fserve fullname fwrite ghide gload gmove " +
    "gopts goto gplay gpoint gqreq groups gshow gsize gstop gtalk gunload hadd " +
    "halt haltdef hdec hdel help hfree hinc hload hmake hop hsave ial ialclear " +
    "ialmark identd if ignore iline inc invite iuser join kick linesep links list " +
    "load loadbuf localinfo log mdi me menubar mkdir mnick mode msg nick noop notice " +
    "notify omsg onotice part partall pdcc perform play playctrl pop protect pvoice " +
    "qme qmsg query queryn quit raw reload remini remote remove rename renwin " +
    "reseterror resetidle return rlevel rline rmdir run ruser save savebuf saveini " +
    "say scid scon server set showmirc signam sline sockaccept sockclose socklist " +
    "socklisten sockmark sockopen sockpause sockread sockrename sockudp sockwrite " +
    "sound speak splay sreq strip switchbar timer timestamp titlebar tnick tokenize " +
    "toolbar topic tray treebar ulist unload unset unsetall updatenl url uwho " +
    "var vcadd vcmd vcrem vol while whois window winhelp write writeini if isalnum " +
    "isalpha isaop isavoice isban ischan ishop isignore isin isincs isletter islower " +
    "isnotify isnum ison isop isprotect isreg isupper isvoice iswm iswmcs " +
    "elseif else goto menu nicklist status title icon size option text edit " +
    "button check radio box scroll list combo link tab item");
    var functions = parseWords("if elseif else and not or eq ne in ni for foreach while switch");
    var isOperatorChar = /[+\-*&%=<>!?^\/\|]/;
    function chain(stream, state, f) {
      state.tokenize = f;
      return f(stream, state);
    }
    function tokenBase(stream, state) {
      var beforeParams = state.beforeParams;
      state.beforeParams = false;
      var ch = stream.next();
      if (/[\[\]{}\(\),\.]/.test(ch)) {
        if (ch == "(" && beforeParams) state.inParams = true;
        else if (ch == ")") state.inParams = false;
          return null;
      }
      else if (/\d/.test(ch)) {
        stream.eatWhile(/[\w\.]/);
        return "number";
      }
      else if (ch == "\\") {
        stream.eat("\\");
        stream.eat(/./)
        return "number"
      }
      else if (ch == "/" && stream.eat("*")) {
        stream.backUp(2);
        var count =  2;
        while (!stream.sol()) {
          stream.backUp(1);
          var mycurrent = stream.next();
          if (!stream.sol()) { 
            stream.backUp(1);
            count++;            
          }
          if (/\(/.test(mycurrent)) {
            while (count > 2) {
              stream.next();
              count--;
            }
            stream.eat("/");
            stream.eat("*");
            var mycurrent = stream.current();
            return "meta";
          }               
          if (/[)}]/.test(mycurrent)) {
            while (count > 2) {
              stream.next();
              count--;
            }
            stream.eat("/");
            stream.eat("*");
            var mycurrent = stream.current();
            return chain(stream, state, tokenComment);
          }       
        }
        while (count > 2) {
            stream.next();
            count--;
        }
        stream.eat("/");
        stream.eat("*");
        var mycurrent = stream.current();
        return chain(stream, state, tokenComment);
      }
      else if (ch == ";" && stream.match(/ *\( *\(/)) {
        return chain(stream, state, tokenUnparsed);
      }
      else if (ch == ";" && !state.inParams) {
        stream.skipToEnd();
        return "comment";
      }
      else if (ch == '"') {
        stream.eat(/"/);
        return "keyword";
      }
      else if (ch == "$") {
        stream.eatWhile(/[$_a-z0-9A-Z\.:]/);
        if (specials && specials.propertyIsEnumerable(stream.current().toLowerCase())) {
          return "keyword"
        }
        else {
          state.beforeParams = true;        
          return "builtin";
        }
      }
      else if (ch == "%") {
        stream.eatWhile(/[^,^\s^\(^\)]/);
        state.beforeParams = true;      
        return "string";
      }
      else if (isOperatorChar.test(ch)) {
        stream.eatWhile(isOperatorChar);
        return "operator";
      }
      else {
        stream.eatWhile(/[\w\$_{}]/);
        var word = stream.current().toLowerCase();
        if (keywords && keywords.propertyIsEnumerable(word))
          return "keyword";
        if (functions && functions.propertyIsEnumerable(word)) {
          state.beforeParams = true;
          return "keyword";
        }
        return null;
      }
    }
    function tokenString(quote) {
      return function(stream, state) {
      var escaped = false, next, end = false;
      while ((next = stream.next()) != null) {
        if (next == quote && !escaped) {
          end = true;
          break;
        }
        escaped = !escaped && next == "\\";
      }
      if (end) state.tokenize = tokenBase;
        return "string";
      };
    }
    function tokenComment(stream, state) {
      var maybeEnd = false, ch;
      while (ch = stream.next()) {
        if (ch == "/" && maybeEnd) {
          state.tokenize = tokenBase;
          break;
        }
        maybeEnd = (ch == "*");
      }
      return "comment";
    }
    function tokenUnparsed(stream, state) {
      var maybeEnd = 0, ch;
      while (ch = stream.next()) {
        if (ch == ";" && maybeEnd == 2) {
          state.tokenize = tokenBase;
          break;
        }
        if (ch == ")")
          maybeEnd++;
        else if (ch != " ")
          maybeEnd = 0;
      }
      return "meta";
    }
    return {
      startState: function() {
        return {
          tokenize: tokenBase,
          beforeParams: false,
          inParams: false
        };
      },
      token: function(stream, state) {
        if (stream.eatSpace()) return null;
        return state.tokenize(stream, state);
      }
    };
});

Comments

Sign in to comment.
erickl_s   -  Feb 21, 2016

@FordLawnmower for what reason this syntax highlighting doesn't work on brackets editor?
thank's in advance

 Respond  
Hawkee   -  Feb 26, 2013

Great work on this @FordLawnmower. You should push these out to the official CodeMirror repo, https://github.com/marijnh/CodeMirror They might just end up in the next release.

FordLawnmower  -  Feb 26, 2013

I looked at that page before . I couldn't really figure out how to submit anything or even leave a comment.

Hawkee  -  Feb 26, 2013

I'm sure you can figure it out.

FordLawnmower  -  Feb 27, 2013

I added them to a fork, as per instructions on contributing.
https://github.com/FordLawnmower/CodeMirror/

Sign in to comment

Conscious   -  Feb 24, 2013

This is neat, @Hawkee could possibly implement it on here to give a proper highlighting!

Hawkee  -  Feb 24, 2013

Great I'll certainly implement it.

FordLawnmower  -  Feb 24, 2013

I found a problem when there are unbalanced quotes. I updated the code above.

Sign in to comment

FordLawnmower   -  Feb 23, 2013

I was looking at some mIRC scripts and they seemed to have colors but with no real rhyme or reason so I decided to make this.
The scripts I tested on it were much easier to follow with this codemirror.

Hawkee  -  Feb 24, 2013

Perfect, this was a shortcoming of CodeMirror. Thank you for putting this together!

Hawkee  -  Feb 25, 2013

I've implemented it but there seems to be an issue. Most snippets aren't highlighted. Maybe it's a problem with the way it interprets the start and end of scope.

FordLawnmower  -  Feb 25, 2013

@Hawkee I just updated this to the code I'm using now. It should be mostly the same with a few minor changes.
I'm not sure why it doesn't highlight the same as it did in testing, but maybe try the new code.
Is there a way I could test this and get the same result you do here?

Hawkee  -  Feb 25, 2013

It still doesn't seem to be working. You can view the source of this page to see how I'm calling Codemirror. It's a pretty basic call from a textarea. Do you have a working example online?

FordLawnmower  -  Feb 26, 2013

Here is a link to a basic example: http://lawnmower.asuscomm.com/codemirror/mode/mIRC/

FordLawnmower  -  Feb 26, 2013

I think I found the issue. It looks like a case sensitive issue @Hawkee
If I change

CodeMirror.defineMode("mIRC", function() {

to

CodeMirror.defineMode("mirc", function() {

It breaks and doesn't work at all.

So what I've found is that this:

CodeMirror.defineMIME("text/mIRC", "mIRC");

must match this:

CodeMirror.defineMode("mIRC", function() {

The case in the defineMode must be exactly the same as the defineMIME

I also found that the defineMIME should be at the top of the script.
I will update this code now to all lowercase and move the defineMIME to the top

Hawkee  -  Feb 26, 2013

That did it! Thank you. Looks great!

FordLawnmower  -  Feb 26, 2013

Is that the version of the script from the top here, because I still see an unmatched quote issue that I'm not getting on my tests.
You can see it here: http://www.hawkee.com/snippet/9837/

Note I also found that "text/mIRC" has to match the mode: on the html. I changed it above to "text/mirc" to make it uniform.

Edit I'm pretty sure the site is running an older version, because I see several other glitches that I already fixed.

Hawkee  -  Feb 26, 2013

I updated to the latest version. How is that?

FordLawnmower  -  Feb 26, 2013

It looks good, but I found another issue. I added support for mult-line comments, as mIRC does support / /
and lots of people use this type of comment. The problem was, there wasn't a check for / inside a function or if statement.
ie. if (/
iswm %bla)
You can see what it does here: http://www.hawkee.com/snippet/9858/
It basically just comments out the rest of the script and it looks pretty stupid.
Anyway @Hawkee , I updated the script and this issue should be fixed.

Hawkee  -  Feb 26, 2013

Good fix.

FordLawnmower  -  Feb 26, 2013

Cool . It looks good.

FordLawnmower  -  Feb 26, 2013

Updated Found a typo preventing "writeini" from being highlighted. I had "writeint"
You can see it here: http://www.hawkee.com/snippet/8545/

Sign in to comment

Are you sure you want to unfollow this person?
Are you sure you want to delete this?
Click "Unsubscribe" to stop receiving notices pertaining to this post.
Click "Subscribe" to resume notices pertaining to this post.