diff --git a/client/homebrew/editor/editor.jsx b/client/homebrew/editor/editor.jsx index ee955c0ca..637394072 100644 --- a/client/homebrew/editor/editor.jsx +++ b/client/homebrew/editor/editor.jsx @@ -160,6 +160,24 @@ const Editor = createClass({ } } + // Superscript + if(line.includes('\^')) { + const regex = /\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^/g; + let match; + while ((match = regex.exec(line)) != null) { + codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) - 1 }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length + 1 }, { className: 'superscript' }); + } + } + + // Subscript + if(line.includes('^^')) { + const regex = /\^\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^\^/g; + let match; + while ((match = regex.exec(line)) != null) { + codeMirror.markText({ line: lineNumber, ch: line.indexOf(match[1]) - 2 }, { line: lineNumber, ch: line.indexOf(match[1]) + match[1].length + 2 }, { className: 'subscript' }); + } + } + // Highlight injectors {style} if(line.includes('{') && line.includes('}')){ const regex = /(?:^|[^{\n])({(?=((?::(?:"[\w,\-()#%. ]*"|[\w\-()#%.]*)|[^"':{}\s]*)*))\2})/gm; diff --git a/client/homebrew/editor/editor.less b/client/homebrew/editor/editor.less index d31ceb97c..b165f91db 100644 --- a/client/homebrew/editor/editor.less +++ b/client/homebrew/editor/editor.less @@ -43,6 +43,18 @@ font-weight : bold; color : green; } + .superscript:not(.cm-comment) { + font-weight : bold; + color : goldenrod; + vertical-align : super; + font-size : 0.9em; + } + .subscript:not(.cm-comment) { + font-weight : bold; + color : rgb(123, 123, 15); + vertical-align : sub; + font-size : 0.9em; + } } .brewJump { diff --git a/shared/naturalcrit/codeEditor/codeEditor.jsx b/shared/naturalcrit/codeEditor/codeEditor.jsx index e0a2220b4..fcfee1dbf 100644 --- a/shared/naturalcrit/codeEditor/codeEditor.jsx +++ b/shared/naturalcrit/codeEditor/codeEditor.jsx @@ -112,6 +112,10 @@ const CodeEditor = createClass({ 'Shift-Tab' : this.dedent, 'Ctrl-B' : this.makeBold, 'Cmd-B' : this.makeBold, + 'Shift-Ctrl-=' : this.makeSuper, + 'Shift-Cmd-=' : this.makeSuper, + 'Ctrl-=' : this.makeSub, + 'Cmd-=' : this.makeSub, 'Ctrl-I' : this.makeItalic, 'Cmd-I' : this.makeItalic, 'Ctrl-U' : this.makeUnderline, @@ -219,6 +223,25 @@ const CodeEditor = createClass({ } }, + makeSuper : function() { + const selection = this.codeMirror.getSelection(), t = selection.slice(0, 1) === '^' && selection.slice(-1) === '^'; + this.codeMirror.replaceSelection(t ? selection.slice(1, -1) : `^${selection}^`, 'around'); + if(selection.length === 0){ + const cursor = this.codeMirror.getCursor(); + this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 1 }); + } + }, + + makeSub : function() { + const selection = this.codeMirror.getSelection(), t = selection.slice(0, 2) === '^^' && selection.slice(-2) === '^^'; + this.codeMirror.replaceSelection(t ? selection.slice(2, -2) : `^^${selection}^^`, 'around'); + if(selection.length === 0){ + const cursor = this.codeMirror.getCursor(); + this.codeMirror.setCursor({ line: cursor.line, ch: cursor.ch - 2 }); + } + }, + + makeNbsp : function() { this.codeMirror.replaceSelection(' ', 'end'); }, diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index 114229887..7185cab8e 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -206,6 +206,34 @@ const mustacheInjectBlock = { } }; +const superSubScripts = { + name : 'superSubScript', + level : 'inline', + start(src) { return src.match(/\^/m)?.index; }, // Hint to Marked.js to stop and check for a match + tokenizer(src, tokens) { + const superRegex = /^\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^/m; + const subRegex = /^\^\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^\^/m; + let isSuper = false; + let match = subRegex.exec(src); + if(!match){ + match = superRegex.exec(src); + if(match) + isSuper = true; + } + if(match?.length) { + return { + type : 'superSubScript', // Should match "name" above + raw : match[0], // Text to consume from the source + tag : isSuper ? 'sup' : 'sub', + tokens : this.lexer.inlineTokens(match[1]) + }; + } + }, + renderer(token) { + return `<${token.tag}>${this.parser.parseInline(token.tokens)}`; + } +}; + const definitionLists = { name : 'definitionLists', level : 'block', @@ -238,7 +266,7 @@ const definitionLists = { } }; -Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionLists] }); +Marked.use({ extensions: [mustacheSpans, mustacheDivs, mustacheInjectInline, definitionLists, superSubScripts] }); Marked.use(mustacheInjectBlock); Marked.use({ renderer: renderer, mangle: false }); Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite());