mirror of https://github.com/zulip/zulip.git
110 lines
3.4 KiB
JavaScript
110 lines
3.4 KiB
JavaScript
|
import marked from "../third/marked/lib/marked";
|
||
|
|
||
|
import * as blueslip from "./blueslip";
|
||
|
|
||
|
const linkifier_map = new Map();
|
||
|
export let linkifier_list = [];
|
||
|
|
||
|
function handleLinkifier(pattern, matches) {
|
||
|
let url = linkifier_map.get(pattern);
|
||
|
|
||
|
let current_group = 1;
|
||
|
|
||
|
for (const match of matches) {
|
||
|
const back_ref = "\\" + current_group;
|
||
|
url = url.replace(back_ref, match);
|
||
|
current_group += 1;
|
||
|
}
|
||
|
|
||
|
return url;
|
||
|
}
|
||
|
|
||
|
function python_to_js_linkifier(pattern, url) {
|
||
|
// Converts a python named-group regex to a javascript-compatible numbered
|
||
|
// group regex... with a regex!
|
||
|
const named_group_re = /\(?P<([^>]+?)>/g;
|
||
|
let match = named_group_re.exec(pattern);
|
||
|
let current_group = 1;
|
||
|
while (match) {
|
||
|
const name = match[1];
|
||
|
// Replace named group with regular matching group
|
||
|
pattern = pattern.replace("(?P<" + name + ">", "(");
|
||
|
// Replace named reference in URL to numbered reference
|
||
|
url = url.replace("%(" + name + ")s", "\\" + current_group);
|
||
|
|
||
|
// Reset the RegExp state
|
||
|
named_group_re.lastIndex = 0;
|
||
|
match = named_group_re.exec(pattern);
|
||
|
|
||
|
current_group += 1;
|
||
|
}
|
||
|
// Convert any python in-regex flags to RegExp flags
|
||
|
let js_flags = "g";
|
||
|
const inline_flag_re = /\(\?([Limsux]+)\)/;
|
||
|
match = inline_flag_re.exec(pattern);
|
||
|
|
||
|
// JS regexes only support i (case insensitivity) and m (multiline)
|
||
|
// flags, so keep those and ignore the rest
|
||
|
if (match) {
|
||
|
const py_flags = match[1].split("");
|
||
|
|
||
|
for (const flag of py_flags) {
|
||
|
if ("im".includes(flag)) {
|
||
|
js_flags += flag;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pattern = pattern.replace(inline_flag_re, "");
|
||
|
}
|
||
|
// Ideally we should have been checking that linkifiers
|
||
|
// begin with certain characters but since there is no
|
||
|
// support for negative lookbehind in javascript, we check
|
||
|
// for this condition in `contains_backend_only_syntax()`
|
||
|
// function. If the condition is satisfied then the message
|
||
|
// is rendered locally, otherwise, we return false there and
|
||
|
// message is rendered on the backend which has proper support
|
||
|
// for negative lookbehind.
|
||
|
pattern = pattern + /(?!\w)/.source;
|
||
|
let final_regex = null;
|
||
|
try {
|
||
|
final_regex = new RegExp(pattern, js_flags);
|
||
|
} catch (error) {
|
||
|
// We have an error computing the generated regex syntax.
|
||
|
// We'll ignore this linkifier for now, but log this
|
||
|
// failure for debugging later.
|
||
|
blueslip.error("python_to_js_linkifier: " + error.message);
|
||
|
}
|
||
|
return [final_regex, url];
|
||
|
}
|
||
|
|
||
|
export function update_linkifier_rules(linkifiers) {
|
||
|
// Update the marked parser with our particular set of linkifiers
|
||
|
linkifier_map.clear();
|
||
|
linkifier_list = [];
|
||
|
|
||
|
const marked_rules = [];
|
||
|
|
||
|
for (const linkifier of linkifiers) {
|
||
|
const [regex, final_url] = python_to_js_linkifier(linkifier.pattern, linkifier.url_format);
|
||
|
if (!regex) {
|
||
|
// Skip any linkifiers that could not be converted
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
linkifier_map.set(regex, final_url);
|
||
|
linkifier_list.push({
|
||
|
pattern: regex,
|
||
|
url_format: final_url,
|
||
|
});
|
||
|
marked_rules.push(regex);
|
||
|
}
|
||
|
|
||
|
marked.InlineLexer.rules.zulip.linkifiers = marked_rules;
|
||
|
}
|
||
|
|
||
|
export function initialize(linkifiers) {
|
||
|
update_linkifier_rules(linkifiers);
|
||
|
|
||
|
marked.setOptions({linkifierHandler: handleLinkifier});
|
||
|
}
|