mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
Updated markdown regexes to better handle underscores and links
This commit is contained in:
@@ -10,9 +10,21 @@ class MattermostInlineLexer extends marked.InlineLexer {
|
||||
constructor(links, options) {
|
||||
super(links, options);
|
||||
|
||||
// modified version of the regex that doesn't break up words in snake_case
|
||||
// the original is /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
|
||||
this.rules.text = /^[\s\S]+?(?=__|\b_|[\\<!\[*`]| {2,}\n|$)/;
|
||||
this.rules = Object.assign({}, this.rules);
|
||||
|
||||
// modified version of the regex that doesn't break up words in snake_case,
|
||||
// allows for links starting with www, and allows links succounded by parentheses
|
||||
// the original is /^[\s\S]+?(?=[\\<!\[_*`~]|https?:\/\/| {2,}\n|$)/
|
||||
this.rules.text = /^[\s\S]+?(?=[^\w\/]_|[\\<!\[*`~]|https?:\/\/|www\.|\(| {2,}\n|$)/;
|
||||
|
||||
// modified version of the regex that allows links starting with www and those surrounded
|
||||
// by parentheses
|
||||
// the original is /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/
|
||||
this.rules.url = /^(\(?(?:https?:\/\/|www\.)[^\s<.][^\s<]*[^<.,:;"'\]\s])/;
|
||||
|
||||
// modified version of the regex that allows <links> starting with www.
|
||||
// the original is /^<([^ >]+(@|:\/)[^ >]+)>/
|
||||
this.rules.autolink = /^<((?:[^ >]+(@|:\/)|www\.)[^ >]+)>/;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +68,20 @@ class MattermostMarkdownRenderer extends marked.Renderer {
|
||||
|
||||
link(href, title, text) {
|
||||
let outHref = href;
|
||||
let outText = text;
|
||||
let prefix = '';
|
||||
let suffix = '';
|
||||
|
||||
if (!(/^(mailto|https?|ftp)/.test(outHref))) {
|
||||
// some links like https://en.wikipedia.org/wiki/Rendering_(computer_graphics) contain brackets
|
||||
// and we try our best to differentiate those from ones just wrapped in brackets when autolinking
|
||||
if (outHref.startsWith('(') && outHref.endsWith(')') && text === outHref) {
|
||||
prefix = '(';
|
||||
suffix = ')';
|
||||
outText = text.substring(1, text.length - 1);
|
||||
outHref = outHref.substring(1, outHref.length - 1);
|
||||
}
|
||||
|
||||
if (!(/[a-z+.-]+:/i).test(outHref)) {
|
||||
outHref = `http://${outHref}`;
|
||||
}
|
||||
|
||||
@@ -72,26 +96,17 @@ class MattermostMarkdownRenderer extends marked.Renderer {
|
||||
output += ' target="_blank">';
|
||||
}
|
||||
|
||||
output += text + '</a>';
|
||||
output += outText + '</a>';
|
||||
|
||||
return output;
|
||||
return prefix + output + suffix;
|
||||
}
|
||||
|
||||
paragraph(text) {
|
||||
let outText = text;
|
||||
|
||||
// required so markdown does not strip '_' from @user_names
|
||||
outText = TextFormatting.doFormatMentions(text);
|
||||
|
||||
if (!('emoticons' in this.options) || this.options.emoticon) {
|
||||
outText = TextFormatting.doFormatEmoticons(outText);
|
||||
}
|
||||
|
||||
if (this.formattingOptions.singleline) {
|
||||
return `<p class="markdown__paragraph-inline">${outText}</p>`;
|
||||
return `<p class="markdown__paragraph-inline">${text}</p>`;
|
||||
}
|
||||
|
||||
return super.paragraph(outText);
|
||||
return super.paragraph(text);
|
||||
}
|
||||
|
||||
table(header, body) {
|
||||
@@ -106,7 +121,8 @@ class MattermostMarkdownRenderer extends marked.Renderer {
|
||||
export function format(text, options) {
|
||||
const markdownOptions = {
|
||||
renderer: new MattermostMarkdownRenderer(null, options),
|
||||
sanitize: true
|
||||
sanitize: true,
|
||||
gfm: true
|
||||
};
|
||||
|
||||
const tokens = marked.lexer(text, markdownOptions);
|
||||
|
||||
@@ -43,7 +43,7 @@ export function doFormatText(text, options) {
|
||||
|
||||
// replace important words and phrases with tokens
|
||||
output = autolinkAtMentions(output, tokens);
|
||||
output = autolinkUrls(output, tokens);
|
||||
output = autolinkEmails(output, tokens);
|
||||
output = autolinkHashtags(output, tokens);
|
||||
|
||||
if (!('emoticons' in options) || options.emoticon) {
|
||||
@@ -93,28 +93,21 @@ export function sanitizeHtml(text) {
|
||||
return output;
|
||||
}
|
||||
|
||||
// Convert URLs into tokens
|
||||
function autolinkUrls(text, tokens) {
|
||||
function replaceUrlWithToken(autolinker, match) {
|
||||
// Convert emails into tokens
|
||||
function autolinkEmails(text, tokens) {
|
||||
function replaceEmailWithToken(autolinker, match) {
|
||||
const linkText = match.getMatchedText();
|
||||
let url = linkText;
|
||||
|
||||
if (match.getType() === 'email') {
|
||||
url = `mailto:${url}`;
|
||||
} else if (!(/^(mailto|https?|ftp)/.test(url))) {
|
||||
url = `http://${url}`;
|
||||
}
|
||||
|
||||
const index = tokens.size;
|
||||
const alias = `MM_LINK${index}`;
|
||||
|
||||
var target = 'target="_blank"';
|
||||
if (url.lastIndexOf(Utils.getTeamURLFromAddressBar(), 0) === 0) {
|
||||
target = '';
|
||||
}
|
||||
const alias = `MM_EMAIL${index}`;
|
||||
|
||||
tokens.set(alias, {
|
||||
value: `<a class="theme" ${target} href="${url}">${linkText}</a>`,
|
||||
value: `<a class="theme" href="${url}">${linkText}</a>`,
|
||||
originalText: linkText
|
||||
});
|
||||
|
||||
@@ -123,12 +116,12 @@ function autolinkUrls(text, tokens) {
|
||||
|
||||
// we can't just use a static autolinker because we need to set replaceFn
|
||||
const autolinker = new Autolinker({
|
||||
urls: true,
|
||||
urls: false,
|
||||
email: true,
|
||||
phone: false,
|
||||
twitter: false,
|
||||
hashtag: false,
|
||||
replaceFn: replaceUrlWithToken
|
||||
replaceFn: replaceEmailWithToken
|
||||
});
|
||||
|
||||
return autolinker.link(text);
|
||||
|
||||
Reference in New Issue
Block a user