Custom Mode causes error “Uncaught TypeError: Cannot read property 'prototype' of undefined”

703 views
Skip to first unread message

Noah Crowley

unread,
Jan 12, 2014, 12:04:43 AM1/12/14
to ace-d...@googlegroups.com
I'm creating a web application that will require users to be able to write code in my custom scripting language that is currently in development.  I've used Cloud9's Ace editor in projects before.  Since I'm used to Ace and know quite a bit about how to implement it, I decided it'd be a perfect fit to fulfill my needs for my script editor.  However, in order to use a custom scripting language, I needed to create a custom Mode for Ace to use.  Here is my mode, which is stored in a file in the ace_editor directory (same directory as ace.js is in):

    ace.define('ace/mode/customscript', function (require, exports, module) {

        var oop = require("ace/lib/oop");
        var Tokenizer = require("ace/tokenizer").Tokenizer;
        var CustomScriptHighlightRules = require("ace/mode/customscript_highlight_rules").CustomScriptHighlightRules;

        var Mode = function () {
            this.$tokenizer = new Tokenizer(new CustomScriptHighlightRules().getRules());
        };
        oop.inherits(Mode);

        exports.Mode = Mode;
    });

    ace.define('ace/mode/customscript_highlight_rules', function (require, exports, module) {

        var oop = require("ace/lib/oop");

        var keywords = [
            "null",
            "new",
            "task",
            "end",
            "if",
            "else",
            "elseif",
            "and",
            "or",
            "not",
            "then",
            "for",
            "loop",
            "while",
            "times",
            "print",
            "use",
            "use_csharp",
            "extend",
            "as",
            "return",
            "continue",
            "my"
        ];
        var keywordsRegex = "";
        for (var i = 0; i < keywords.length; i++) {
            keywordsRegex = keywordsRegex + keywords[i] + "|";
        }
        keywordsRegex = keywordsRegex.substr(0, keywordsRegex.length - 1);

        var CustomScriptHighlightRules = function() {

            this.$rules = {
                "no_regex": [
                    {
                        token: "comment",
                        regex: "(->)(?s)(.*?)(<-)"
                    },
                    {
                        token: "comment",
                        regex: "--([^\n].*)"
                    },
                    {
                        token: "string",
                        regex: "(\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\")"
                    },
                    {
                        token: "numbers",
                        regex: "(?<!\\w)[0x]?\\d+(?!\\w)"
                    },
                    {
                        token: "booleans",
                        regex: "true|false"
                    },
                    {
                        token: "keywords",
                        regex: keywordsRegex
                    }
                ]
            };

        };

        oop.inherits(CustomScriptHighlightRules);

        exports.CustomScriptHighlightRules = CustomScriptHighlightRules;
    });

And here is the code I'm using to test the editor:

    <!DOCTYPE html>
    <html>
        <head>
            <title>CustomScript Editor</title>
        </head>
        <body>
            <div id="syntax_editor" style="width: 100%; height: 100%">task idk()
        alert("hi")
    end</div>
            <script type="text/javascript">
                $().ready(function () {
                    var editor = ace.edit("syntax_editor");
                    editor.setTheme("ace/theme/twilight");
                    editor.getSession().setMode("ace/mode/customscript");
                    editor.getSession().setUseWrapMode(false);
                    editor.getSession().setUseSoftTabs(true);
                    editor.getSession().setTabSize(4);
                    editor.setShowPrintMargin(false);
                    editor.setFadeFoldWidgets(false);
                    document.getElementById("syntax_editor").style.fontSize = 20 + 'px';
                });
            </script>
        </body>
    </html>

The problem is that I'm getting an error from Ace when I load the page that reads:

    Uncaught TypeError: Cannot read property 'prototype' of undefined

I am using Ace's latest src_noconflict library from GitHub.  What am I doing wrong in my custom mode?

Harutyun Amirjanyan

unread,
Jan 12, 2014, 4:06:06 AM1/12/14
to ace-d...@googlegroups.com
oop.inherits takes 2 arguments
like oop.inherits(JavaScriptHighlightRules, TextHighlightRules);

in mode constructor it's better to do
this.HighlightRules = CustomScriptHighlightRules;
see https://github.com/ajaxorg/ace/blob/master/lib/ace/mode/javascript.js#L45

js regexps do not support lookbehind "(?<!"
and keyword arrays are better handled by createKeywordMapper

see http://jsbin.com/ojijeb/373

Noah Crowley

unread,
Jan 12, 2014, 9:53:30 AM1/12/14
to ace-d...@googlegroups.com
Thank you very much!  That helped tremendoulsy!!  However, I've run into a new error.  Here is what my script looks like now (HTML page remains the same):

ace.define('ace/mode/customscript', function (require, exports, module) {

    var oop = require("ace/lib/oop");
    var TextMode = require("./text").Mode;
    var Tokenizer = require("ace/tokenizer").Tokenizer;
    this.HighlightRules = require("ace/mode/customscript_highlight_rules").CustomScriptHighlightRules;

    var Mode = function () {
        this.$tokenizer = new Tokenizer(new HighlightRules().getRules());
    };
    oop.inherits(Mode, TextMode);

    exports.Mode = Mode;
});

ace.define('ace/mode/customscript_highlight_rules', function (require, exports, module) {

    var oop = require("ace/lib/oop");
    var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
    /*var keywords = [
    keywordsRegex = keywordsRegex.substr(0, keywordsRegex.length - 1);*/

    var CustomScriptHighlightRules = function () {
        
        this.createKeywordMapper({
            "keyword":
                "new task end if else elseif and or not then for loop while times print use use_csharp extend as return continue my",
            "constant.language":
                "true false null"
        }, "text", true, " ");

        this.$rules = {
            "no_regex": [
                {
                    token: "comment",
                    regex: "(->)(.|[\r\n])*(<-)"
                },
                {
                    token: "comment",
                    regex: "--([^\n].*)"
                },
                {
                    token: "string",
                    regex: "(\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\")"
                },
                {
                    token: "numbers",
                    regex: "(\d)?[.]*(\d)*"
                },
                {
                    token: "booleans",
                    regex: "true|false"
                }
            ]
        };

    };

    oop.inherits(CustomScriptHighlightRules, TextHighlightRules);

    exports.CustomScriptHighlightRules = CustomScriptHighlightRules;
});

And here is the error that I am now getting:

Uncaught TypeError: Cannot set property 'lastIndex' of undefined

Harutyun Amirjanyan

unread,
Jan 12, 2014, 12:56:48 PM1/12/14
to ace-d...@googlegroups.com
Please use something like http://plnkr.co/ or jsbin for sharing code,
that makes it much easier to see what is wrong.

The error is because your mode misses initial state "start" and uses
"no_regex" instead,

also regex: "(->)(.|[\r\n])*(<-)" wouldn't work because regexps can
match only one line at a time, so you need to use $rules the way i
showed in http://jsbin.com/ojijeb/373/edit,
see http://jsbin.com/ojijeb/374/edit for a slightly better version
Reply all
Reply to author
Forward
0 new messages