Modules (181)

LanguageManager

Description

LanguageManager provides access to the languages supported by Brackets

To find out which languages we support by default, have a look at languages.json.

To get access to an existing language, call getLanguage():

var language = LanguageManager.getLanguage("<id>");

To define your own languages, call defineLanguage():

LanguageManager.defineLanguage("haskell", {
    name: "Haskell",
    mode: "haskell",
    fileExtensions: ["hs"],
    blockComment: ["{-", "-}"],
    lineComment: ["--"]
});

To use that language and its related mode, wait for the returned promise to be resolved:

LanguageManager.defineLanguage("haskell", definition).done(function (language) {
    console.log("Language " + language.getName() + " is now available!");
});

The extension can also contain dots:

LanguageManager.defineLanguage("literatecoffeescript", {
    name: "Literate CoffeeScript",
    mode: "coffeescript",
    fileExtensions: ["litcoffee", "coffee.md"]
});

You can also specify file names:

LanguageManager.defineLanguage("makefile", {
    name: "Make",
    mode: ["null", "text/plain"],
    fileNames: ["Makefile"]
});

You can combine file names and extensions, or not define them at all.

You can also refine an existing language:

var language = LanguageManager.getLanguage("haskell");
language.setLineCommentSyntax(["--"]);
language.setBlockCommentSyntax("{-", "-}");
language.addFileExtension("lhs");

Some CodeMirror modes define variations of themselves. They are called MIME modes. To find existing MIME modes, search for "CodeMirror.defineMIME" in thirdparty/CodeMirror/mode For instance, C++, C# and Java all use the clike (C-like) mode with different settings and a different MIME name. You can refine the mode definition by specifying the MIME mode as well:

LanguageManager.defineLanguage("csharp", {
    name: "C#",
    mode: ["clike", "text/x-csharp"],
    ...
});

Defining the base mode is still necessary to know which file to load. However, language.getMode() will return just the MIME mode if one was specified.

If you need to configure a mode, you can just create a new MIME mode and use that:

CodeMirror.defineMIME("text/x-brackets-html", {
    "name": "htmlmixed",
    "scriptTypes": [{"matches": /\/x-handlebars-template|\/x-mustache/i,
                   "mode": null}]
});

LanguageManager.defineLanguage("html", {
    name: "HTML",
    mode: ["htmlmixed", "text/x-brackets-html"],
    ...
});

If a mode is not shipped with our CodeMirror distribution, you need to first load it yourself. If the mode is part of our CodeMirror distribution, it gets loaded automatically.

You can also defines binary file types, i.e. Brackets supports image files by default, such as .jpg, .png etc. Binary files do not require mode because modes are specific to CodeMirror, which only handles text based file types. To register a binary language the isBinary flag must be set, i.e.

LanguageManager.defineLanguage("audio", {
    name: "Audio",
    fileExtensions: ["mp3", "wav", "aif", "aiff", "ogg"],
    isBinary: true
});

LanguageManager dispatches two events:

  • languageAdded -- When any new Language is added. 2nd arg is the new Language.
  • languageModified -- When the attributes of a Language change, or when the Language gains or loses
      file extension / filename mappings. 2nd arg is the modified Language.
    

Dependencies

Functions

Private

_getLanguageForMode

Resolves a CodeMirror mode to a Language object.

mode non-nullable string
CodeMirror mode
Returns: Language
The language for the provided mode or the fallback language
    function _getLanguageForMode(mode) {
        var language = _modeToLanguageMap[mode];
        if (language) {
            return language;
        }

        // In case of unsupported languages
        console.log("Called LanguageManager._getLanguageForMode with a mode for which no language has been registered:", mode);
        return _fallbackLanguage;
    }
Private

_patchCodeMirror

Monkey-patch CodeMirror to prevent modes from being overwritten by extensions. We may rely on the tokens provided by some of these modes.

    function _patchCodeMirror() {
        var _original_CodeMirror_defineMode = CodeMirror.defineMode;
        function _wrapped_CodeMirror_defineMode(name) {
            if (CodeMirror.modes[name]) {
                console.error("There already is a CodeMirror mode with the name \"" + name + "\"");
                return;
            }
            _original_CodeMirror_defineMode.apply(CodeMirror, arguments);
        }
        CodeMirror.defineMode = _wrapped_CodeMirror_defineMode;
    }
Private Public API

_resetPathLanguageOverrides

Resets all the language overrides for file paths. Used by unit tests only.

    function _resetPathLanguageOverrides() {
        _filePathToLanguageMap = {};
    }
Private

_restoreOverriddenDefault

name string
Extension or filename that should be restored
prefState {overridden: string,add: string}
object for the pref that is currently being updated
    function _restoreOverriddenDefault(name, state) {
        if (state.overridden[name]) {
            var language = getLanguage(state.overridden[name]);
            language[state.add](name);
            delete state.overridden[name];
        }
    }
Private

_setLanguageForMode

Adds a global mode-to-language association.

mode non-nullable string
The mode to associate the language with
language non-nullable Language
The language to associate with the mode
    function _setLanguageForMode(mode, language) {
        if (_modeToLanguageMap[mode]) {
            console.warn("CodeMirror mode \"" + mode + "\" is already used by language " + _modeToLanguageMap[mode]._name + " - cannot fully register language " + language._name +
                         " using the same mode. Some features will treat all content with this mode as language " + _modeToLanguageMap[mode]._name);
            return;
        }

        _modeToLanguageMap[mode] = language;
    }
Private

_triggerLanguageAdded

language non-nullable Language
The new language
    function _triggerLanguageAdded(language) {
        // finally, store language to _language map
        _languages[language.getId()] = language;
        exports.trigger("languageAdded", language);
    }
Private

_triggerLanguageModified

language non-nullable Language
The modified language
    function _triggerLanguageModified(language) {
        exports.trigger("languageModified", language);
    }
Private

_updateFromPrefs

    function _updateFromPrefs(pref) {
        var newMapping = PreferencesManager.get(pref),
            newNames = Object.keys(newMapping),
            state = _prefState[pref],
            last = state.last,
            overridden = state.overridden;

        // Look for added and changed names (extensions or filenames)
        newNames.forEach(function (name) {
            var language;
            if (newMapping[name] !== last[name]) {
                if (last[name]) {
                    language = getLanguage(last[name]);
                    if (language) {
                        language[state.remove](name);

                        // If this name that was previously mapped was overriding a default
                        // restore it now.
                        _restoreOverriddenDefault(name, state);
                    }
                }

                language = exports[state.get](name);
                if (language) {
                    language[state.remove](name);

                    // We're removing a name that was defined in Brackets or an extension,
                    // so keep track of how it used to be mapped.
                    if (!overridden[name]) {
                        overridden[name] = language.getId();
                    }
                }
                language = getLanguage(newMapping[name]);
                if (language) {
                    language[state.add](name);
                }
            }
            if(!getLanguage(newMapping[name])) {
                
                // If the language doesn't exist, restore any overrides and remove it
                // from the state.
                if(overridden[name]) {
                    _restoreOverriddenDefault(name, state);
                }
                delete newMapping[name];
            }
        });

        // Look for removed names (extensions or filenames)
        _.difference(Object.keys(last), newNames).forEach(function (name) {
            var language = getLanguage(last[name]);
            if (language) {
                language[state.remove](name);
                _restoreOverriddenDefault(name, state);
            }
        });
        state.last = newMapping;
    }


    EventDispatcher.makeEventDispatcher(exports);

    // Prevent modes from being overwritten by extensions
    _patchCodeMirror();

    // Define a custom MIME mode here instead of putting it directly into languages.json
    // because JSON files can't contain regular expressions. Also, all other modes so
    // far were strings, so we spare us the trouble of allowing more complex mode values.
    CodeMirror.defineMIME("text/x-brackets-html", {
        "name": "htmlmixed",
        "scriptTypes": [
            {
                "matches": /\/x-handlebars|\/x-mustache|\/ng-template$|^text\/html$/i,
                "mode": "htmlmixed"
            },
            {
                "matches": /^text\/(babel|jsx)$/i,
                "mode": "jsx"
            }
        ]
    });

    // Define SVG MIME type so an SVG language can be defined for SVG-specific code hints.
    // Currently, SVG uses XML mode so it has generic XML syntax highlighting. This can
    // be removed when SVG gets its own CodeMirror mode with SVG syntax highlighting.
    CodeMirror.defineMIME("image/svg+xml", "xml");

    // Load the default languages
    _defaultLanguagesJSON = JSON.parse(_defaultLanguagesJSON);
    _ready = Async.doInParallel(Object.keys(_defaultLanguagesJSON), function (key) {
        return defineLanguage(key, _defaultLanguagesJSON[key]);
    }, false);

    // Get the object for HTML
    _ready.always(function () {
        var html = getLanguage("html");

        // The htmlmixed mode uses the xml mode internally for the HTML parts, so we map it to HTML
        html._setLanguageForMode("xml", html);

        // Currently we override the above mentioned "xml" in TokenUtils.getModeAt, instead returning "html".
        // When the CSSInlineEditor and the hint providers are no longer based on modes, this can be changed.
        // But for now, we need to associate this madeup "html" mode with our HTML language object.
        _setLanguageForMode("html", html);

        // Similarly, the php mode uses clike internally for the PHP parts
        var php = getLanguage("php");
        php._setLanguageForMode("clike", php);

        // Similar hack to the above for dealing with SCSS/CSS.
        var scss = getLanguage("scss");
        scss._setLanguageForMode("css", scss);

        // The fallback language for unknown modes and file extensions
        _fallbackLanguage = getLanguage("unknown");

        // There is a circular dependency between FileUtils and LanguageManager which
        // was introduced in 254b01e2f2eebea4416026d0f40d017b8ca6dbc9
        // and may be preventing us from importing PreferencesManager (which also
        // depends on FileUtils) here. Using the async form of require fixes this.
        require(["preferences/PreferencesManager"], function (pm) {
            PreferencesManager = pm;
            pm.definePreference(_EXTENSION_MAP_PREF, "object", {}, {
                description: Strings.DESCRIPTION_LANGUAGE_FILE_EXTENSIONS
            }).on("change", function () {
                _updateFromPrefs(_EXTENSION_MAP_PREF);
            });
            pm.definePreference(_NAME_MAP_PREF, "object", {}, {
                description: Strings.DESCRIPTION_LANGUAGE_FILE_NAMES
            }).on("change", function () {
                _updateFromPrefs(_NAME_MAP_PREF);
            });
            _updateFromPrefs(_EXTENSION_MAP_PREF);
            _updateFromPrefs(_NAME_MAP_PREF);
        });
    });

    // Private for unit tests
    exports._EXTENSION_MAP_PREF         = _EXTENSION_MAP_PREF;
    exports._NAME_MAP_PREF              = _NAME_MAP_PREF;
    exports._resetPathLanguageOverrides = _resetPathLanguageOverrides;

    // Public methods
    exports.ready                       = _ready;
    exports.defineLanguage              = defineLanguage;
    exports.getLanguage                 = getLanguage;
    exports.getLanguageForExtension     = getLanguageForExtension;
    exports.getLanguageForPath          = getLanguageForPath;
    exports.getLanguages                = getLanguages;
    exports.setLanguageOverrideForPath  = setLanguageOverrideForPath;
    exports.getCompoundFileExtension    = getCompoundFileExtension;
});
Private

_validateNonEmptyString

Checks whether value is a non-empty string. Reports an error otherwise. If no deferred is passed, console.error is called. Otherwise the deferred is rejected with the error message.

value *
The value to validate
description non-nullable string
A helpful identifier for value
deferred nullable jQuery.Deferred
A deferred to reject with the error message in case of an error
Returns: boolean
True if the value is a non-empty string, false otherwise
    function _validateNonEmptyString(value, description, deferred) {
        var reportError = deferred ? deferred.reject : console.error;

        // http://stackoverflow.com/questions/1303646/check-whether-variable-is-number-or-string-in-javascript
        if (Object.prototype.toString.call(value) !== "[object String]") {
            reportError(description + " must be a string");
            return false;
        }
        if (value === "") {
            reportError(description + " must not be empty");
            return false;
        }
        return true;
    }
Public API

defineLanguage

Defines a language.

id non-nullable string
Unique identifier for this language: lowercase letters, digits, and _ separators (e.g. "cpp", "foo_bar", "c99")
definition non-nullable Object
An object describing the language
definition.name non-nullable string
Human-readable name of the language, as it's commonly referred to (e.g. "C++")
definition.fileExtensions Array.<string>
List of file extensions used by this language (e.g. ["php", "php3"] or ["coffee.md"] - may contain dots)
definition.fileNames Array.<string>
List of exact file names (e.g. ["Makefile"] or ["package.json]). Higher precedence than file extension.
definition.blockComment Array.<string>
Array with two entries defining the block comment prefix and suffix (e.g. [""])
definition.lineComment (string,Array.<string>)
Line comment prefixes (e.g. "//" or ["//", "#"])
definition.mode (string,Array.<string>)
CodeMirror mode (e.g. "htmlmixed"), optionally with a MIME mode defined by that mode ["clike", "text/x-c++src"] Unless the mode is located in thirdparty/CodeMirror/mode//.js, you need to first load it yourself.
Returns: $.Promise
A promise object that will be resolved with a Language object
    function defineLanguage(id, definition) {
        var result = new $.Deferred();

        if (_pendingLanguages[id]) {
            result.reject("Language \"" + id + "\" is waiting to be resolved.");
            return result.promise();
        }
        if (_languages[id]) {
            result.reject("Language \"" + id + "\" is already defined");
            return result.promise();
        }

        var language       = new Language(),
            name           = definition.name,
            fileExtensions = definition.fileExtensions,
            fileNames      = definition.fileNames,
            blockComment   = definition.blockComment,
            lineComment    = definition.lineComment,
            i,
            l;

        function _finishRegisteringLanguage() {
            if (fileExtensions) {
                for (i = 0, l = fileExtensions.length; i < l; i++) {
                    language.addFileExtension(fileExtensions[i]);
                }
            }
            // register language file names after mode has loaded
            if (fileNames) {
                for (i = 0, l = fileNames.length; i < l; i++) {
                    language.addFileName(fileNames[i]);
                }
            }

            language._setBinary(!!definition.isBinary);

            // store language to language map
            _languages[language.getId()] = language;

            // restore any preferences for non-default languages
            if(PreferencesManager) {
                _updateFromPrefs(_EXTENSION_MAP_PREF);
                _updateFromPrefs(_NAME_MAP_PREF);
            }
        }

        if (!language._setId(id) || !language._setName(name) ||
                (blockComment && !language.setBlockCommentSyntax(blockComment[0], blockComment[1])) ||
                (lineComment && !language.setLineCommentSyntax(lineComment))) {
            result.reject();
            return result.promise();
        }


        if (definition.isBinary) {
            // add file extensions and store language to language map
            _finishRegisteringLanguage();

            result.resolve(language);
            // Not notifying DocumentManager via event LanguageAdded, because DocumentManager
            // does not care about binary files.
        } else {
            // track languages that are currently loading
            _pendingLanguages[id] = language;

            language._loadAndSetMode(definition.mode).done(function () {

                // globally associate mode to language
                _setLanguageForMode(language.getMode(), language);

                // add file extensions and store language to language map
                _finishRegisteringLanguage();

                // fire an event to notify DocumentManager of the new language
                _triggerLanguageAdded(language);

                result.resolve(language);
            }).fail(function (error) {
                console.error(error);
                result.reject(error);
            }).always(function () {
                // delete from pending languages after success and failure
                delete _pendingLanguages[id];
            });
        }

        return result.promise();
    }
Public API

getCompoundFileExtension

Get the file extension (excluding ".") given a path OR a bare filename. Returns "" for names with no extension. If the only . in the file is the first character, returns "" as this is not considered an extension. This method considers known extensions which include . in them.

fullPath string
full path to a file or directory
Returns: string
Returns the extension of a filename or empty string if the argument is a directory or a filename with no extension
    function getCompoundFileExtension(fullPath) {
        var baseName = FileUtils.getBaseName(fullPath),
            parts = baseName.split(".");

        // get rid of file name
        parts.shift();
        if (baseName[0] === ".") {
            // if starts with a `.`, then still consider it as file name
            parts.shift();
        }

        var extension = [parts.pop()], // last part is always an extension
            i = parts.length;
        while (i--) {
            if (getLanguageForExtension(parts[i])) {
                extension.unshift(parts[i]);
            } else {
                break;
            }
        }
        return extension.join(".");
    }
Public API

getLanguage

Resolves a language ID to a Language object. File names have a higher priority than file extensions.

id non-nullable string
Identifier for this language: lowercase letters, digits, and _ separators (e.g. "cpp", "foo_bar", "c99")
Returns: Language
The language with the provided identifier or undefined
    function getLanguage(id) {
        return _languages[id];
    }
Public API

getLanguageForExtension

Resolves a file extension to a Language object. Warning: it is almost always better to use getLanguageForPath(), since Language can depend on file name and even full path. Use this API only if no relevant file/path exists.

extension non-nullable string
Extension that language should be resolved for
Returns: ?Language
The language for the provided extension or null if none exists
    function getLanguageForExtension(extension) {
        return _fileExtensionToLanguageMap[extension.toLowerCase()];
    }
Public API

getLanguageForPath

Resolves a file path to a Language object.

path non-nullable string
Path to the file to find a language for
ignoreOverride nullable boolean
If set to true will cause the lookup to ignore any overrides and return default binding. By default override is not ignored.
Returns: Language
The language for the provided file type or the fallback language
    function getLanguageForPath(path, ignoreOverride) {
        var fileName,
            language = _filePathToLanguageMap[path],
            extension,
            parts;

        // if there's an override, return it
        if (!ignoreOverride && language) {
            return language;
        }

        fileName = FileUtils.getBaseName(path).toLowerCase();
        language = _fileNameToLanguageMap[fileName];

        // If no language was found for the file name, use the file extension instead
        if (!language) {
            // Split the file name into parts:
            //   "foo.coffee.md"   => ["foo", "coffee", "md"]
            //   ".profile.bak"    => ["", "profile", "bak"]
            //   "1. Vacation.txt" => ["1", " Vacation", "txt"]
            parts = fileName.split(".");

            // A leading dot does not indicate a file extension, but marks the file as hidden => remove it
            if (parts[0] === "") {
                // ["", "profile", "bak"] => ["profile", "bak"]
                parts.shift();
            }

            // The first part is assumed to be the title, not the extension => remove it
            //   ["foo", "coffee", "md"]   => ["coffee", "md"]
            //   ["profile", "bak"]        => ["bak"]
            //   ["1", " Vacation", "txt"] => [" Vacation", "txt"]
            parts.shift();

            // Join the remaining parts into a file extension until none are left or a language was found
            while (!language && parts.length) {
                // First iteration:
                //   ["coffee", "md"]     => "coffee.md"
                //   ["bak"]              => "bak"
                //   [" Vacation", "txt"] => " Vacation.txt"
                // Second iteration (assuming no language was found for "coffee.md"):
                //   ["md"]  => "md"
                //   ["txt"] => "txt"
                extension = parts.join(".");
                language  = _fileExtensionToLanguageMap[extension];
                // Remove the first part
                // First iteration:
                //   ["coffee", "md"]     => ["md"]
                //   ["bak"]              => []
                //   [" Vacation", "txt"] => ["txt"]
                // Second iteration:
                //   ["md"]  => []
                //   ["txt"] => []
                parts.shift();
            }
        }

        return language || _fallbackLanguage;
    }
Public API

getLanguages

Returns a map of all the languages currently defined in the LanguageManager. The key to the map is the language id and the value is the language object.

Returns: Object.<string,Language>
A map containing all of the languages currently defined.
    function getLanguages() {
        return $.extend({}, _languages); // copy to prevent modification
    }
Public API

setLanguageOverrideForPath

Adds a language mapping for the specified fullPath. If language is falsy (null or undefined), the mapping is removed. The override is NOT persisted across Brackets sessions.

fullPath non-nullable fullPath
absolute path of the file
language nullable object
language to associate the file with or falsy value to remove any existing override
    function setLanguageOverrideForPath(fullPath, language) {
        var oldLang = getLanguageForPath(fullPath);
        if (!language) {
            delete _filePathToLanguageMap[fullPath];
        } else {
            _filePathToLanguageMap[fullPath] = language;
        }
        var newLang = getLanguageForPath(fullPath);

        // Old language changed since this path is no longer mapped to it
        _triggerLanguageModified(oldLang);
        // New language changed since a path is now mapped to it that wasn't before
        _triggerLanguageModified(newLang);
    }

Classes

Constructor

Language

Model for a language.

    function Language() {
        this._fileExtensions    = [];
        this._fileNames         = [];
        this._modeToLanguageMap = {};
        this._lineCommentSyntax = [];
    }

Properties

Private

_blockCommentSyntax

Block comment syntax

Type
{ prefix: string, suffix: string }
Private
    Language.prototype._blockCommentSyntax = null;
Private

_fileExtensions

File extensions that use this language

Type
Array.<string>
Private
    Language.prototype._fileExtensions = null;
Private

_fileNames

File names for extensionless files that use this language

Type
Array.<string>
Private
    Language.prototype._fileNames = null;
Private

_id

Identifier for this language

Type
string
Private
    Language.prototype._id = null;
Private

_isBinary

Whether or not the language is binary

Type
boolean
Private
    Language.prototype._isBinary = false;
Private

_lineCommentSyntax

Line comment syntax

Type
Array.<string>
Private
    Language.prototype._lineCommentSyntax = null;
Private

_mode

CodeMirror mode for this language

Type
string
Private
    Language.prototype._mode = null;
Private

_modeToLanguageMap

Which language to use for what CodeMirror mode

Type
Object.<string, Language>
Private
    Language.prototype._modeToLanguageMap = null;
Private

_name

Human-readable name of this language

Type
string
Private
    Language.prototype._name = null;

Methods

Private

_loadAndSetMode

Loads a mode and sets it for this language.

mode (string,Array.<string>)
CodeMirror mode (e.g. "htmlmixed"), optionally paired with a MIME mode defined by that mode (e.g. ["clike", "text/x-c++src"]). Unless the mode is located in thirdparty/CodeMirror/mode//.js, you need to first load it yourself.
Returns: $.Promise
A promise object that will be resolved when the mode is loaded and set
    Language.prototype._loadAndSetMode = function (mode) {
        var result      = new $.Deferred(),
            self        = this,
            mimeMode; // Mode can be an array specifying a mode plus a MIME mode defined by that mode ["clike", "text/x-c++src"]

        if (Array.isArray(mode)) {
            if (mode.length !== 2) {
                result.reject("Mode must either be a string or an array containing two strings");
                return result.promise();
            }
            mimeMode = mode[1];
            mode = mode[0];
        }

        // mode must not be empty. Use "null" (the string "null") mode for plain text
        if (!_validateNonEmptyString(mode, "mode", result)) {
            result.reject();
            return result.promise();
        }

        var finish = function () {
            if (!CodeMirror.modes[mode]) {
                result.reject("CodeMirror mode \"" + mode + "\" is not loaded");
                return;
            }

            if (mimeMode) {
                var modeConfig = CodeMirror.mimeModes[mimeMode];

                if (!modeConfig) {
                    result.reject("CodeMirror MIME mode \"" + mimeMode + "\" not found");
                    return;
                }
            }

            // This mode is now only about what to tell CodeMirror
            // The base mode was only necessary to load the proper mode file
            self._mode = mimeMode || mode;
            self._wasModified();

            result.resolve(self);
        };

        if (CodeMirror.modes[mode]) {
            finish();
        } else {
            require(["thirdparty/CodeMirror/mode/" + mode + "/" + mode], finish);
        }

        return result.promise();
    };
Private

_setBinary

Sets whether or not the language is binary

isBinary non-nullable boolean
    Language.prototype._setBinary = function (isBinary) {
        this._isBinary = isBinary;
    };
Private

_setId

Sets the identifier for this language or prints an error to the console.

id non-nullable string
Identifier for this language: lowercase letters, digits, and _ separators (e.g. "cpp", "foo_bar", "c99")
Returns: boolean
Whether the ID was valid and set or not
    Language.prototype._setId = function (id) {
        if (!_validateNonEmptyString(id, "Language ID")) {
            return false;
        }
        // Make sure the ID is a string that can safely be used universally by the computer - as a file name, as an object key, as part of a URL, etc.
        // Hence we use "_" instead of "." since the latter often has special meaning
        if (!id.match(/^[a-z0-9]+(_[a-z0-9]+)*$/)) {
            console.error("Invalid language ID \"" + id + "\": Only groups of lower case letters and numbers are allowed, separated by underscores.");
            return false;
        }

        this._id = id;
        return true;
    };
Private

_setLanguageForMode

Overrides a mode-to-language association for this particular language only or prints an error to the console. Used to disambiguate modes used by multiple languages.

mode non-nullable string
The mode to associate the language with
language non-nullable Language
The language to associate with the mode
Returns: boolean
Whether the mode-to-language association was valid and set or not
    Language.prototype._setLanguageForMode = function (mode, language) {
        if (mode === this._mode && language !== this) {
            console.error("A language must always map its mode to itself");
            return false;
        }

        this._modeToLanguageMap[mode] = language;
        this._wasModified();

        return true;
    };
Private

_setName

Sets the human-readable name of this language or prints an error to the console.

name non-nullable string
Human-readable name of the language, as it's commonly referred to (e.g. "C++")
Returns: boolean
Whether the name was valid and set or not
    Language.prototype._setName = function (name) {
        if (!_validateNonEmptyString(name, "name")) {
            return false;
        }

        this._name = name;
        return true;
    };
Private

_wasModified

Trigger the "languageModified" event if this language is registered already

    Language.prototype._wasModified = function () {
        if (_languages[this._id]) {
            _triggerLanguageModified(this);
        }
    };

addFileExtension

Adds one or more file extensions to this language.

extension non-nullable string, Array.<string>
A file extension (or array thereof) used by this language
    Language.prototype.addFileExtension = function (extension) {
        if (Array.isArray(extension)) {
            extension.forEach(this._addFileExtension.bind(this));
        } else {
            this._addFileExtension(extension);
        }
    };
    Language.prototype._addFileExtension = function (extension) {
        // Remove a leading dot if present
        if (extension.charAt(0) === ".") {
            extension = extension.substr(1);
        }

        // Make checks below case-INsensitive
        extension = extension.toLowerCase();

        if (this._fileExtensions.indexOf(extension) === -1) {
            this._fileExtensions.push(extension);

            var language = _fileExtensionToLanguageMap[extension];
            if (language) {
                console.warn("Cannot register file extension \"" + extension + "\" for " + this._name + ", it already belongs to " + language._name);
            } else {
                _fileExtensionToLanguageMap[extension] = this;
            }

            this._wasModified();
        } else if(!_fileExtensionToLanguageMap[extension]) {
            
            // Language should be in the extension map but isn't
            _fileExtensionToLanguageMap[extension] = this;
            this._wasModified();
        }
    };

addFileName

Adds one or more file names to the language which is used to match files that don't have extensions like "Makefile" for example.

extension non-nullable string, Array.<string>
An extensionless file name (or array thereof) used by this language
    Language.prototype.addFileName = function (name) {
        if (Array.isArray(name)) {
            name.forEach(this._addFileName.bind(this));
        } else {
            this._addFileName(name);
        }
    };
    Language.prototype._addFileName = function (name) {
        // Make checks below case-INsensitive
        name = name.toLowerCase();

        if (this._fileNames.indexOf(name) === -1) {
            this._fileNames.push(name);

            var language = _fileNameToLanguageMap[name];
            if (language) {
                console.warn("Cannot register file name \"" + name + "\" for " + this._name + ", it already belongs to " + language._name);
            } else {
                _fileNameToLanguageMap[name] = this;
            }

            this._wasModified();
        }
    };

getBlockCommentPrefix

Returns the prefix to use for block comments.

Returns: string
The prefix
    Language.prototype.getBlockCommentPrefix = function () {
        return this._blockCommentSyntax && this._blockCommentSyntax.prefix;
    };

getBlockCommentSuffix

Returns the suffix to use for block comments.

Returns: string
The suffix
    Language.prototype.getBlockCommentSuffix = function () {
        return this._blockCommentSyntax && this._blockCommentSyntax.suffix;
    };

getFileExtensions

Returns an array of file extensions for this language.

Returns: Array.<string>
File extensions used by this language
    Language.prototype.getFileExtensions = function () {
        // Use concat to create a copy of this array, preventing external modification
        return this._fileExtensions.concat();
    };

getFileNames

Returns an array of file names for extensionless files that use this language.

Returns: Array.<string>
Extensionless file names used by this language
    Language.prototype.getFileNames = function () {
        // Use concat to create a copy of this array, preventing external modification
        return this._fileNames.concat();
    };

getId

Returns the identifier for this language.

Returns: string
The identifier
    Language.prototype.getId = function () {
        return this._id;
    };

getLanguageForMode

Returns either a language associated with the mode or the fallback language. Used to disambiguate modes used by multiple languages.

mode non-nullable string
The mode to associate the language with
Returns: Language
This language if it uses the mode, or whatever {@link #_getLanguageForMode} returns
    Language.prototype.getLanguageForMode = function (mode) {
        if (mode === this._mode) {
            return this;
        }
        return this._modeToLanguageMap[mode] || _getLanguageForMode(mode);
    };

getLineCommentPrefixes

Returns an array of prefixes to use for line comments.

Returns: Array.<string>
The prefixes
    Language.prototype.getLineCommentPrefixes = function () {
        return this._lineCommentSyntax;
    };

getMode

Returns the CodeMirror mode for this language.

Returns: string
The mode
    Language.prototype.getMode = function () {
        return this._mode;
    };

getName

Returns the human-readable name of this language.

Returns: string
The name
    Language.prototype.getName = function () {
        return this._name;
    };

hasBlockCommentSyntax

Returns whether the block comment syntax is defined for this language.

Returns: boolean
Whether block comments are supported
    Language.prototype.hasBlockCommentSyntax = function () {
        return Boolean(this._blockCommentSyntax);
    };

hasLineCommentSyntax

Returns whether the line comment syntax is defined for this language.

Returns: boolean
Whether line comments are supported
    Language.prototype.hasLineCommentSyntax = function () {
        return this._lineCommentSyntax.length > 0;
    };

isBinary

Indicates whether or not the language is binary (e.g., image or audio).

Returns: boolean
    Language.prototype.isBinary = function () {
        return this._isBinary;
    };

isFallbackLanguage

Determines whether this is the fallback language or not

Returns: boolean
True if this is the fallback language, false otherwise
    Language.prototype.isFallbackLanguage = function () {
        return this === _fallbackLanguage;
    };

removeFileExtension

Unregisters one or more file extensions from this language.

extension non-nullable string, Array.<string>
File extension (or array thereof) to stop using for this language
    Language.prototype.removeFileExtension = function (extension) {
        if (Array.isArray(extension)) {
            extension.forEach(this._removeFileExtension.bind(this));
        } else {
            this._removeFileExtension(extension);
        }
    };
    Language.prototype._removeFileExtension = function (extension) {
        // Remove a leading dot if present
        if (extension.charAt(0) === ".") {
            extension = extension.substr(1);
        }

        // Make checks below case-INsensitive
        extension = extension.toLowerCase();

        var index = this._fileExtensions.indexOf(extension);
        if (index !== -1) {
            this._fileExtensions.splice(index, 1);

            delete _fileExtensionToLanguageMap[extension];

            this._wasModified();
        }
    };

removeFileName

Unregisters one or more file names from this language.

extension non-nullable string, Array.<string>
An extensionless file name (or array thereof) used by this language
    Language.prototype.removeFileName = function (name) {
        if (Array.isArray(name)) {
            name.forEach(this._removeFileName.bind(this));
        } else {
            this._removeFileName(name);
        }
    };
    Language.prototype._removeFileName = function (name) {
        // Make checks below case-INsensitive
        name = name.toLowerCase();

        var index = this._fileNames.indexOf(name);
        if (index !== -1) {
            this._fileNames.splice(index, 1);

            delete _fileNameToLanguageMap[name];

            this._wasModified();
        }
    };

setBlockCommentSyntax

Sets the prefix and suffix to use for blocks comments in this language or prints an error to the console.

prefix non-nullable string
Prefix string to use for block comments (e.g. "")
Returns: boolean
Whether the syntax was valid and set or not
    Language.prototype.setBlockCommentSyntax = function (prefix, suffix) {
        if (!_validateNonEmptyString(prefix, "prefix") || !_validateNonEmptyString(suffix, "suffix")) {
            return false;
        }

        this._blockCommentSyntax = { prefix: prefix, suffix: suffix };
        this._wasModified();

        return true;
    };

setLineCommentSyntax

Sets the prefixes to use for line comments in this language or prints an error to the console.

prefix non-nullable (string, Array.<string>)
Prefix string or an array of prefix strings to use for line comments (e.g. "//" or ["//", "#"])
Returns: boolean
Whether the syntax was valid and set or not
    Language.prototype.setLineCommentSyntax = function (prefix) {
        var prefixes = Array.isArray(prefix) ? prefix : [prefix];
        var i;

        if (prefixes.length) {
            this._lineCommentSyntax = [];
            for (i = 0; i < prefixes.length; i++) {
                _validateNonEmptyString(String(prefixes[i]), Array.isArray(prefix) ? "prefix[" + i + "]" : "prefix");

                this._lineCommentSyntax.push(prefixes[i]);
            }
            this._wasModified();
        } else {
            console.error("The prefix array should not be empty");
        }

        return true;
    };