blob: c431b8a3ce81ad2ac931c19eea515dfd0e6a47f2 [file] [log] [blame]
/*
* Copyright (C) 2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
CodeMirror.defineMode("regex", function() {
function characterSetTokenizer(stream, state) {
let context = state.currentContext();
if (context.negatedCharacterSet === undefined) {
context.negatedCharacterSet = stream.eat("^");
if (context.negatedCharacterSet)
return "regex-character-set-negate";
}
while (stream.peek()) {
if (stream.eat("\\"))
return consumeEscapeSequence(stream);
if (stream.eat("]")) {
state.tokenize = tokenBase;
return state.popContext();
}
stream.next();
}
return "error";
}
function consumeEscapeSequence(stream) {
if (stream.match(/[bBdDwWsStrnvf0]/))
return "regex-special";
if (stream.eat("x")) {
if (stream.match(/[0-9a-fA-F]{2}/))
return "regex-escape";
return "error";
}
if (stream.eat("u")) {
if (stream.match(/[0-9a-fA-F]{4}/))
return "regex-escape-2";
return "error";
}
if (stream.eat("c")) {
if (stream.match(/[A-Z]/))
return "regex-escape-3";
return "error";
}
if (stream.match(/[1-9]/))
return "regex-backreference";
if (stream.next())
return "regex-literal";
return "error";
}
function tokenBase(stream, state) {
let ch = stream.next();
if (ch === "\\")
return consumeEscapeSequence(stream);
// Match start of capturing/noncapturing group, or positive/negative lookaheads.
if (ch === "(") {
let style;
if (stream.match(/\?[=!]/))
style = "regex-lookahead";
else {
stream.match(/\?:/);
style = "regex-group";
}
return state.pushContext(stream, style, ")");
}
let context = state.currentContext();
if (context && context.type === ch)
return state.popContext();
// Match quantifiers: *, +, ?, {n}, {n,}, and {n,m}
if (/[*+?]/.test(ch))
return "regex-quantifier";
if (ch === "{") {
if (stream.match(/\d+}/))
return "regex-quantifier";
let matches = stream.match(/(\d+),(\d+)?}/);
if (!matches)
return "error";
let minimum = parseInt(matches[1]);
let maximum = parseInt(matches[2]);
if (minimum > maximum)
return "error";
return "regex-quantifier";
}
// Match character sets.
if (ch === "[") {
state.tokenize = characterSetTokenizer;
return state.pushContext(stream, "regex-character-set");
}
// Match miscelleneous special characters.
if (/[.^$|]/.test(ch))
return "regex-special";
return null;
}
return {
startState: function() {
let contextStack = [];
return {
currentContext: function() {
return contextStack.length ? contextStack[contextStack.length - 1] : null;
},
pushContext: function(stream, data, type) {
let context = {data, type};
contextStack.push(context);
return data;
},
popContext: function() {
console.assert(contextStack.length, "Tried to pop empty context stack.");
return contextStack.pop().data;
},
tokenize: tokenBase,
};
},
token: function(stream, state) {
return state.tokenize(stream, state);
}
};
});
CodeMirror.defineMIME("text/x-regex", "regex");