monaco-editor-core
Version:
A browser based code editor
144 lines (143 loc) • 4.37 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function isFuzzyActionArr(what) {
return (Array.isArray(what));
}
export function isFuzzyAction(what) {
return !isFuzzyActionArr(what);
}
export function isString(what) {
return (typeof what === 'string');
}
export function isIAction(what) {
return !isString(what);
}
// Small helper functions
/**
* Is a string null, undefined, or empty?
*/
export function empty(s) {
return (s ? false : true);
}
/**
* Puts a string to lower case if 'ignoreCase' is set.
*/
export function fixCase(lexer, str) {
return (lexer.ignoreCase && str ? str.toLowerCase() : str);
}
/**
* Ensures there are no bad characters in a CSS token class.
*/
export function sanitize(s) {
return s.replace(/[&<>'"_]/g, '-'); // used on all output token CSS classes
}
// Logging
/**
* Logs a message.
*/
export function log(lexer, msg) {
console.log(`${lexer.languageId}: ${msg}`);
}
// Throwing errors
export function createError(lexer, msg) {
return new Error(`${lexer.languageId}: ${msg}`);
}
// Helper functions for rule finding and substitution
/**
* substituteMatches is used on lexer strings and can substitutes predefined patterns:
* $$ => $
* $# => id
* $n => matched entry n
* @attr => contents of lexer[attr]
*
* See documentation for more info
*/
export function substituteMatches(lexer, str, id, matches, state) {
const re = /\$((\$)|(#)|(\d\d?)|[sS](\d\d?)|@(\w+))/g;
let stateMatches = null;
return str.replace(re, function (full, sub, dollar, hash, n, s, attr, ofs, total) {
if (!empty(dollar)) {
return '$'; // $$
}
if (!empty(hash)) {
return fixCase(lexer, id); // default $#
}
if (!empty(n) && n < matches.length) {
return fixCase(lexer, matches[n]); // $n
}
if (!empty(attr) && lexer && typeof (lexer[attr]) === 'string') {
return lexer[attr]; //@attribute
}
if (stateMatches === null) { // split state on demand
stateMatches = state.split('.');
stateMatches.unshift(state);
}
if (!empty(s) && s < stateMatches.length) {
return fixCase(lexer, stateMatches[s]); //$Sn
}
return '';
});
}
/**
* substituteMatchesRe is used on lexer regex rules and can substitutes predefined patterns:
* $Sn => n'th part of state
*
*/
export function substituteMatchesRe(lexer, str, state) {
const re = /\$[sS](\d\d?)/g;
let stateMatches = null;
return str.replace(re, function (full, s) {
if (stateMatches === null) { // split state on demand
stateMatches = state.split('.');
stateMatches.unshift(state);
}
if (!empty(s) && s < stateMatches.length) {
return fixCase(lexer, stateMatches[s]); //$Sn
}
return '';
});
}
/**
* Find the tokenizer rules for a specific state (i.e. next action)
*/
export function findRules(lexer, inState) {
let state = inState;
while (state && state.length > 0) {
const rules = lexer.tokenizer[state];
if (rules) {
return rules;
}
const idx = state.lastIndexOf('.');
if (idx < 0) {
state = null; // no further parent
}
else {
state = state.substr(0, idx);
}
}
return null;
}
/**
* Is a certain state defined? In contrast to 'findRules' this works on a ILexerMin.
* This is used during compilation where we may know the defined states
* but not yet whether the corresponding rules are correct.
*/
export function stateExists(lexer, inState) {
let state = inState;
while (state && state.length > 0) {
const exist = lexer.stateNames[state];
if (exist) {
return true;
}
const idx = state.lastIndexOf('.');
if (idx < 0) {
state = null; // no further parent
}
else {
state = state.substr(0, idx);
}
}
return false;
}