Merge branch 'asciidoc' into 5.1-candidate

This commit is contained in:
ansuz 2022-08-30 13:58:08 +05:30
commit 97bc45c3d7
6 changed files with 701 additions and 0 deletions

View File

@ -7,6 +7,7 @@ define([
var list = Modes.list = [
"APL apl .apl",
"ASCII-Armor asciiarmor .asc",
"AsciiDoc asciidoc .adoc",
"ASN.1 asn.1 .asn1",
"Asterisk asterisk",
"Brainfuck brainfuck .b",

View File

@ -20,6 +20,7 @@ define([
'netflux-client': '/bower_components/netflux-websocket/netflux-client',
'chainpad-netflux': '/bower_components/chainpad-netflux/chainpad-netflux',
'chainpad-listmap': '/bower_components/chainpad-listmap/chainpad-listmap',
'cm-extra': '/lib/codemirror-extra-modes'
},
map: {
'*': {

View File

@ -272,6 +272,13 @@ define([
if (/text\/x/.test(mode)) {
CMeditor.autoLoadMode(editor, 'clike');
editor.setOption('mode', mode);
} else if (mode === 'asciidoc') {
CMeditor.autoLoadMode(editor, mode, {
path: function () {
return 'cm-extra/asciidoc/asciidoc';
}
});
editor.setOption('mode', mode);
} else {
if (mode !== "text") {
CMeditor.autoLoadMode(editor, mode);

View File

@ -11,4 +11,5 @@ This file is intended to be used as a log of what third-party source we have ven
* [mermaid 9.0.0](https://github.com/mermaid-js/mermaid/releases/tag/8.13.4) extends our markdown integration to support a variety of diagram types
* [Fabricjs 4.6.0](https://github.com/fabricjs/fabric.js) and [Fabric-history](https://github.com/lyzerk/fabric-history) for the whiteboard app
* [Requirejs optional module plugin](https://stackoverflow.com/a/27422370)
* [asciidoc.js 2.0.0](https://github.com/asciidoctor/codemirror-asciidoc/releases/tag/2.0.0) with slight changes to match the format of other codemirror modes

View File

@ -0,0 +1,25 @@
Copyright (c) 2010, Ajax.org B.V.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of Ajax.org B.V. nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 AJAX.ORG B.V. 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.

View File

@ -0,0 +1,666 @@
// Parts from Ace; see <https://raw.githubusercontent.com/ajaxorg/ace/master/LICENSE>
// Ace highlight rules function imported below.
(function(mod) {
"use strict";
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("cm/lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["cm/lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
CodeMirror.defineMode("asciidoc", function() {
var HighlightRules = function () {
var identifierRe = "[a-zA-Z\u00a1-\uffff]+\\b";
this.$rules = {
"start": [
{token: "empty", regex: /$/},
{token: "literal", regex: /^\.{4,}\s*$/, next: "listingBlock"},
{token: "literal", regex: /^-{4,}\s*$/, next: "literalBlock"},
{token: "literal", regex: /^\+{4,}\s*$/, next: "passthroughBlock"},
{token: "keyword", regex: /^={4,}\s*$/},
{token: "text", regex: /^\s*$/},
// immediately return to the start mode without matching anything
{token: "empty", regex: "", next: "dissallowDelimitedBlock"}
],
"dissallowDelimitedBlock": [
{include: "paragraphEnd"},
{token: "comment", regex: '^//.+$'},
{token: "keyword", regex: "^(?:NOTE|TIP|IMPORTANT|WARNING|CAUTION):\\s"},
{include: "listStart"},
{token: "literal", regex: /^\s+.+$/, next: "indentedBlock"},
{token: "empty", regex: "", next: "text"}
],
"paragraphEnd": [
{token: "doc.comment", regex: /^\/{4,}\s*$/, next: "commentBlock"},
{token: "tableBlock", regex: /^\s*[|!]=+\s*$/, next: "tableBlock"},
// open block, ruler
{token: "keyword", regex: /^(?:--|''')\s*$/, next: "start"},
{token: "option", regex: /^\[.*\]\s*$/, next: "start"},
{token: "pageBreak", regex: /^>{3,}$/, next: "start"},
{token: "literal", regex: /^\.{4,}\s*$/, next: "listingBlock"},
{token: "titleUnderline", regex: /^(?:={2,}|-{2,}|~{2,}|\^{2,}|\+{2,})\s*$/, next: "start"},
{token: "singleLineTitle", regex: /^={1,6}\s+\S.*$/, next: "start"},
{token: "otherBlock", regex: /^(?:\*{2,}|_{2,})\s*$/, next: "start"},
// .optional title
{token: "optionalTitle", regex: /^\.[^.\s].+$/, next: "start"}
],
"listStart": [
{
token: "keyword",
regex: /^\s*(?:\d+\.|[a-zA-Z]\.|[ixvmIXVM]+\)|\*{1,5}|-|\.{1,5})\s/,
next: "listText"
},
{token: "meta.tag", regex: /^.+(?::{2,4}|;;)(?: |$)/, next: "listText"},
// continuation
{token: "keyword", regex: /^\+\s*$/, next: "start"}
],
"text": [
{
token: ["link", "link"],
regex: /((?:https?:\/\/|ftp:\/\/|file:\/\/|mailto:|callto:)[^\s\[]+)(\[.*?\])/
},
{token: ["link", "link"], regex: /(?:https?:\/\/|ftp:\/\/|file:\/\/|mailto:|callto:)[^\s\[]+/},
{token: "link", regex: /\b[\w\.\/\-]+@[\w\.\/\-]+\b/},
{include: "macros"},
{include: "paragraphEnd"},
{token: "literal", regex: /\+{3,}/, next: "smallPassthrough"},
{
token: "escape",
regex: /\((?:C|TM|R)\)|\.{3}|->|<-|=>|<=|&#(?:\d+|x[a-fA-F\d]+);|(?: |^)--(?=\s+\S)/
},
{token: "escape", regex: /\\[_*'`+#]|\\{2}[_*'`+#]{2}/},
{token: "keyword", regex: /\s\+$/},
// any word
{token: "text", regex: identifierRe},
{
token: ["keyword", "string", "keyword"],
regex: /(<<[\w\d\-$]+,)(.*?)(>>|$)/
},
{token: "keyword", regex: /<<[\w\d\-$]+,?|>>/},
{token: "constant.character", regex: /\({2,3}.*?\){2,3}/},
// List of callouts
{token: "support.function.list.callout", regex: /^(?:<\d+>|\d+>|>) /, next: "text"},
// Anchor
{token: "keyword", regex: /\[\[.+?\]\]/},
// bibliography
{token: "support", regex: /^\[{3}[\w\d =\-]+\]{3}/},
{include: "quotes"},
// text block end
{token: "empty", regex: /^\s*$/, next: "start"}
],
"listText": [
{include: "listStart"},
{include: "text"}
],
"indentedBlock": [
{token: "literal", regex: /^[\s\w].+$/, next: "indentedBlock"},
{token: "literal", regex: "", next: "start"}
],
"listingBlock": [
{token: "literal", regex: /^\.{4,}\s*$/, next: "dissallowDelimitedBlock"},
{token: "constant.numeric", regex: '<\\d+>'},
{token: "literal", regex: '[^<]+'},
{token: "literal", regex: '<'}
],
"literalBlock": [
{token: "literal", regex: /^-{4,}\s*$/, next: "dissallowDelimitedBlock"},
{token: "constant.numeric", regex: '<\\d+>'},
{token: "literal", regex: '[^<]+'},
{token: "literal", regex: '<'}
],
"passthroughBlock": [
{token: "literal", regex: /^\+{4,}\s*$/, next: "dissallowDelimitedBlock"},
{token: "literal", regex: identifierRe + "|\\d+"},
{include: "macros"},
{token: "literal", regex: "."}
],
"smallPassthrough": [
{token: "literal", regex: /[+]{3,}/, next: "dissallowDelimitedBlock"},
{token: "literal", regex: /^\s*$/, next: "dissallowDelimitedBlock"},
{token: "literal", regex: identifierRe + "|\\d+"},
{include: "macros"}
],
"commentBlock": [
{token: "doc.comment", regex: /^\/{4,}\s*$/, next: "dissallowDelimitedBlock"},
{token: "doc.comment", regex: '^.*$'}
],
"tableBlock": [
{token: "tableBlock", regex: /^\s*\|={3,}\s*$/, next: "dissallowDelimitedBlock"},
{token: "tableBlock", regex: /^\s*!={3,}\s*$/, next: "innerTableBlock"},
{token: "tableBlock", regex: /\|/},
{include: "text", noEscape: true}
],
"innerTableBlock": [
{token: "tableBlock", regex: /^\s*!={3,}\s*$/, next: "tableBlock"},
{token: "tableBlock", regex: /^\s*|={3,}\s*$/, next: "dissallowDelimitedBlock"},
{token: "tableBlock", regex: /\!/}
],
"macros": [
{token: "macro", regex: /{[\w\-$]+}/},
{
token: ["text", "string", "text", "constant.character", "text"],
regex: /({)([\w\-$]+)(:)?(.+)?(})/
},
{
token: ["text", "markup.list.macro", "keyword", "string"],
regex: /(\w+)(footnote(?:ref)?::?)([^\s\[]+)?(\[.*?\])?/
},
{
token: ["markup.list.macro", "keyword", "string"],
regex: /([a-zA-Z\-][\w\.\/\-]*::?)([^\s\[]+)(\[.*?\])?/
},
{token: ["markup.list.macro", "keyword"], regex: /([a-zA-Z\-][\w\.\/\-]+::?)(\[.*?\])/},
{token: "keyword", regex: /^:.+?:(?= |$)/}
],
"quotes": [
{token: "string.italic", regex: /__[^_\s].*?__/},
{token: "string.italic", regex: quoteRule("_")},
{token: "keyword.bold", regex: /\*\*[^*\s].*?\*\*/},
{token: "keyword.bold", regex: quoteRule("\\*")},
{token: "literal", regex: /\+\+[^+\s].*?\+\+/},
{token: "literal", regex: quoteRule("\\+")},
{token: "literal", regex: /\$\$.+?\$\$/},
{token: "literal", regex: quoteRule("\\$")},
{token: "literal", regex: /``[^`\s].*?``/},
{token: "literal", regex: quoteRule("`")},
{token: "keyword", regex: /\^[^\^].*?\^/},
{token: "keyword", regex: quoteRule("\\^")},
{token: "keyword", regex: /~[^~].*?~/},
{token: "keyword", regex: quoteRule("~")},
{token: "keyword", regex: /##?/},
{token: "keyword", regex: /(?:\B|^)``|\b''/}
]
};
function quoteRule(ch) {
var prefix = /\w/.test(ch) ? "\\b" : "(?:\\B|^)";
return prefix + ch + "[^" + ch + "].*?" + ch + "(?![\\w*])";
}
//addQuoteBlock("text")
var tokenMap = {
macro: "constant.character",
tableBlock: "doc.comment",
titleUnderline: "markup.heading",
singleLineTitle: "markup.heading",
pageBreak: "string",
option: "string.regexp",
otherBlock: "markup.list",
literal: "support.function",
optionalTitle: "constant.numeric",
escape: "constant.language.escape",
link: "markup.underline.list"
};
for (var state in this.$rules) {
var stateRules = this.$rules[state];
for (var i = stateRules.length; i--;) {
var rule = stateRules[i];
if (rule.include || typeof rule == "string") {
var args = [i, 1].concat(this.$rules[rule.include || rule]);
if (rule.noEscape) {
args = args.filter(function (x) {
return !x.next;
});
}
stateRules.splice.apply(stateRules, args);
} else if (rule.token in tokenMap) {
rule.token = tokenMap[rule.token];
}
}
}
};
// Ace's Syntax Tokenizer.
// tokenizing lines longer than this makes editor very slow
var MAX_TOKEN_COUNT = 1000;
var Tokenizer = function (rules) {
this.states = rules;
this.regExps = {};
this.matchMappings = {};
for (var key in this.states) {
var state = this.states[key];
var ruleRegExps = [];
var matchTotal = 0;
var mapping = this.matchMappings[key] = {defaultToken: "text"};
var flag = "g";
var splitterRurles = [];
for (var i = 0; i < state.length; i++) {
var rule = state[i];
if (rule.defaultToken)
mapping.defaultToken = rule.defaultToken;
if (rule.caseInsensitive)
flag = "gi";
if (rule.regex == null)
continue;
if (rule.regex instanceof RegExp)
rule.regex = rule.regex.toString().slice(1, -1);
// Count number of matching groups. 2 extra groups from the full match
// And the catch-all on the end (used to force a match);
var adjustedregex = rule.regex;
var matchcount = new RegExp("(?:(" + adjustedregex + ")|(.))").exec("a").length - 2;
if (Array.isArray(rule.token)) {
if (rule.token.length == 1 || matchcount == 1) {
rule.token = rule.token[0];
} else if (matchcount - 1 != rule.token.length) {
throw new Error("number of classes and regexp groups in '" +
rule.token + "'\n'" + rule.regex + "' doesn't match\n"
+ (matchcount - 1) + "!=" + rule.token.length);
} else {
rule.tokenArray = rule.token;
rule.token = null;
rule.onMatch = this.$arrayTokens;
}
} else if (typeof rule.token == "function" && !rule.onMatch) {
if (matchcount > 1)
rule.onMatch = this.$applyToken;
else
rule.onMatch = rule.token;
}
if (matchcount > 1) {
if (/\\\d/.test(rule.regex)) {
// Replace any backreferences and offset appropriately.
adjustedregex = rule.regex.replace(/\\([0-9]+)/g, function (match, digit) {
return "\\" + (parseInt(digit, 10) + matchTotal + 1);
});
} else {
matchcount = 1;
adjustedregex = this.removeCapturingGroups(rule.regex);
}
if (!rule.splitRegex && typeof rule.token != "string")
splitterRurles.push(rule); // flag will be known only at the very end
}
mapping[matchTotal] = i;
matchTotal += matchcount;
ruleRegExps.push(adjustedregex);
// makes property access faster
if (!rule.onMatch)
rule.onMatch = null;
}
splitterRurles.forEach(function (rule) {
rule.splitRegex = this.createSplitterRegexp(rule.regex, flag);
}, this);
this.regExps[key] = new RegExp("(" + ruleRegExps.join(")|(") + ")|($)", flag);
}
};
(function () {
this.$setMaxTokenCount = function (m) {
MAX_TOKEN_COUNT = m | 0;
};
this.$applyToken = function (str) {
var values = this.splitRegex.exec(str).slice(1);
var types = this.token.apply(this, values);
// required for compatibility with old modes
if (typeof types === "string")
return [{type: types, value: str}];
var tokens = [];
for (var i = 0, l = types.length; i < l; i++) {
if (values[i])
tokens[tokens.length] = {
type: types[i],
value: values[i]
};
}
return tokens;
},
this.$arrayTokens = function (str) {
if (!str)
return [];
var values = this.splitRegex.exec(str);
if (!values)
return "text";
var tokens = [];
var types = this.tokenArray;
for (var i = 0, l = types.length; i < l; i++) {
if (values[i + 1])
tokens[tokens.length] = {
type: types[i],
value: values[i + 1]
};
}
return tokens;
};
this.removeCapturingGroups = function (src) {
var r = src.replace(
/\[(?:\\.|[^\]])*?\]|\\.|\(\?[:=!]|(\()/g,
function (x, y) {
return y ? "(?:" : x;
}
);
return r;
};
this.createSplitterRegexp = function (src, flag) {
if (src.indexOf("(?=") != -1) {
var stack = 0;
var inChClass = false;
var lastCapture = {};
src.replace(/(\\.)|(\((?:\?[=!])?)|(\))|([\[\]])/g, function (m, esc, parenOpen, parenClose, square, index) {
if (inChClass) {
inChClass = square != "]";
} else if (square) {
inChClass = true;
} else if (parenClose) {
if (stack == lastCapture.stack) {
lastCapture.end = index + 1;
lastCapture.stack = -1;
}
stack--;
} else if (parenOpen) {
stack++;
if (parenOpen.length != 1) {
lastCapture.stack = stack
lastCapture.start = index;
}
}
return m;
});
if (lastCapture.end != null && /^\)*$/.test(src.substr(lastCapture.end)))
src = src.substring(0, lastCapture.start) + src.substr(lastCapture.end);
}
return new RegExp(src, (flag || "").replace("g", ""));
};
/**
* Returns an object containing two properties: `tokens`, which contains all the tokens; and `state`, the current state.
* @returns {Object}
**/
this.getLineTokens = function (line, startState) {
if (startState && typeof startState != "string") {
var stack = startState.slice(0);
startState = stack[0];
} else
var stack = [];
var currentState = startState || "start";
var state = this.states[currentState];
if (!state) {
currentState = "start";
state = this.states[currentState];
}
var mapping = this.matchMappings[currentState];
var re = this.regExps[currentState];
re.lastIndex = 0;
var match, tokens = [];
var lastIndex = 0;
var token = {type: null, value: ""};
while (match = re.exec(line)) {
var type = mapping.defaultToken;
var rule = null;
var value = match[0];
var index = re.lastIndex;
if (index - value.length > lastIndex) {
var skipped = line.substring(lastIndex, index - value.length);
if (token.type == type) {
token.value += skipped;
} else {
if (token.type)
tokens.push(token);
token = {type: type, value: skipped};
}
}
for (var i = 0; i < match.length - 2; i++) {
if (match[i + 1] === undefined)
continue;
rule = state[mapping[i]];
if (rule.onMatch)
type = rule.onMatch(value, currentState, stack);
else
type = rule.token;
if (rule.next) {
if (typeof rule.next == "string")
currentState = rule.next;
else
currentState = rule.next(currentState, stack);
state = this.states[currentState];
if (!state) {
window.console && console.error && console.error(currentState, "doesn't exist");
currentState = "start";
state = this.states[currentState];
}
mapping = this.matchMappings[currentState];
lastIndex = index;
re = this.regExps[currentState];
re.lastIndex = index;
}
break;
}
if (value) {
if (typeof type == "string") {
if ((!rule || rule.merge !== false) && token.type === type) {
token.value += value;
} else {
if (token.type)
tokens.push(token);
token = {type: type, value: value};
}
} else if (type) {
if (token.type)
tokens.push(token);
token = {type: null, value: ""};
for (var i = 0; i < type.length; i++)
tokens.push(type[i]);
}
}
if (lastIndex == line.length)
break;
lastIndex = index;
if (tokens.length > MAX_TOKEN_COUNT) {
// chrome doens't show contents of text nodes with very long text
while (lastIndex < line.length) {
if (token.type)
tokens.push(token);
token = {
value: line.substring(lastIndex, lastIndex += 2000),
type: "overflow"
};
}
currentState = "start";
stack = [];
break;
}
}
if (token.type)
tokens.push(token);
if (stack.length > 1) {
if (stack[0] !== currentState)
stack.unshift(currentState);
}
return {
tokens: tokens,
state: stack.length ? stack : currentState
};
};
}).call(Tokenizer.prototype);
// Token conversion.
// See <https://github.com/ajaxorg/ace/wiki/Creating-or-Extending-an-Edit-Mode#common-tokens>
// This is not an exact match nor the best match that can be made.
var tokenFromAceToken = {
empty: null,
text: null,
// Keyword
keyword: 'keyword',
control: 'keyword',
operator: 'operator',
// Constants
constant: 'atom',
numeric: 'number',
character: 'atom',
escape: 'atom',
// Variables
variable: 'variable',
parameter: 'variable-3',
language: 'variable-2', // Python's `self` uses that.
// Comments
comment: 'comment',
line: 'comment',
'double-slash': 'comment',
'double-dash': 'comment',
'number-sign': 'comment',
percentage: 'comment',
block: 'comment',
doc: 'comment',
// String
string: 'string',
quoted: 'string',
single: 'string',
double: 'string',
triple: 'string',
unquoted: 'string',
interpolated: 'string',
regexp: 'string-2',
meta: 'keyword',
literal: 'qualifier',
support: 'builtin',
// Markup
markup: 'tag',
underline: 'link',
link: 'link',
strong: 'strong',
heading: 'header',
em: 'em',
list: 'variable-2',
numbered: 'variable-2',
unnumbered: 'variable-2',
quote: 'quote',
raw: 'variable-2', // Markdown's raw block uses that.
// Invalid
invalid: 'error',
illegal: 'invalidchar',
deprecated: 'error'
};
// Takes a list of Ace tokens, returns a (string) CodeMirror token.
var cmTokenFromAceTokens = function (tokens) {
var token = null;
for (var i = 0; i < tokens.length; i++) {
// Find the most specific token.
if (tokenFromAceToken[tokens[i]] !== undefined) {
token = tokenFromAceToken[tokens[i]];
}
}
return token;
};
// Consume a token from plannedTokens.
var consumeToken = function (stream, state) {
var plannedToken = state.plannedTokens.shift();
if (plannedToken === undefined) {
return null;
}
stream.match(plannedToken.value);
var tokens = plannedToken.type.split('.');
return cmTokenFromAceTokens(tokens);
};
var matchToken = function (stream, state) {
// Anormal start: we already have planned tokens to consume.
if (state.plannedTokens.length > 0) {
return consumeToken(stream, state);
}
// Normal start.
var currentState = state.current;
var currentLine = stream.match(/.*$/, false)[0];
var tokenized = tokenizer.getLineTokens(currentLine, currentState);
// We got a {tokens, state} object.
// Each token is a {value, type} object.
state.plannedTokens = tokenized.tokens;
state.current = tokenized.state;
// Consume a token.
return consumeToken(stream, state);
}
// Initialize all state.
var aceHighlightRules = new HighlightRules();
var tokenizer = new Tokenizer(aceHighlightRules.$rules);
var asciidoc = {
startState: function () {
return {
current: 'start',
// List of {value, type}, with type being an Ace token string.
plannedTokens: []
};
},
blankLine: function (state) {
matchToken('', state);
},
token: matchToken
};
return asciidoc;
});
CodeMirror.defineMIME('text/asciidoc', 'asciidoc');
});