Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/src/packages/frontend/markdown/hashtag-plugin.ts
Views: 894
/*1* LICENSE: MIT (same as upstream)2*/34/*5This is a rewrite of https://github.com/svbergerem/markdown-it-hashtag67LICENSE: MIT (same as the original upstream).89CHANGES:10- typescript11- only one token instead of three, which makes more sense to12me (better for conversion to slate)13*/1415function renderHashtag(tokens, idx): string {16// obviously pretty specific to cocalc...17// Looks like antd tag, but scales.18return `<span style="color:#1b95e0;background-color:#fafafa;border:1px solid #d9d9d9;padding:0 7px;border-radius:5px">#${tokens[idx].content}</span>`;19}2021function isLinkOpen(str) {22return /^<a[>\s]/i.test(str);23}24function isLinkClose(str) {25return /^<\/a\s*>/i.test(str);26}2728export function hashtagPlugin(md): void {29const { arrayReplaceAt, escapeHtml } = md.utils;3031const regex = /(^|\s)#(\w+)/g;3233function hashtag(state) {34const { Token, tokens: blockTokens } = state;3536for (let j = 0, l = blockTokens.length; j < l; j++) {37if (blockTokens[j].type !== "inline") {38continue;39}4041let tokens = blockTokens[j].children;4243let htmlLinkLevel = 0;4445for (let i = tokens.length - 1; i >= 0; i--) {46const currentToken = tokens[i];4748// skip content of markdown links49if (currentToken.type === "link_close") {50i--;51while (52tokens[i].level !== currentToken.level &&53tokens[i].type !== "link_open"54) {55i--;56}57continue;58}5960// skip content of html links61if (currentToken.type === "html_inline") {62// we are going backwards, so isLinkOpen shows end of link63if (isLinkOpen(currentToken.content) && htmlLinkLevel > 0) {64htmlLinkLevel--;65}66if (isLinkClose(currentToken.content)) {67htmlLinkLevel++;68}69}70if (htmlLinkLevel > 0) {71continue;72}7374if (currentToken.type !== "text") {75continue;76}7778// find hashtags79let text = currentToken.content;80const matches = text.match(regex);8182if (matches === null) {83continue;84}8586const nodes: any[] = [];87const { level } = currentToken;8889for (let m = 0; m < matches.length; m++) {90const tagName = matches[m].split("#", 2)[1];9192// find the beginning of the matched text93let pos = text.indexOf(matches[m]);94// find the beginning of the hashtag95pos = text.indexOf("#" + tagName, pos);9697if (pos > 0) {98const token = new Token("text", "", 0);99token.content = text.slice(0, pos);100token.level = level;101nodes.push(token);102}103104const token = new Token("hashtag", "", 0);105token.content = escapeHtml(tagName);106token.level = level;107nodes.push(token);108109text = text.slice(pos + 1 + tagName.length);110}111112if (text.length > 0) {113const token = new Token("text", "", 0);114token.content = text;115token.level = level;116nodes.push(token);117}118119// replace current node120blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, nodes);121}122}123}124125md.core.ruler.after("inline", "hashtag", hashtag);126md.renderer.rules.hashtag = renderHashtag;127}128129130