From f659c77931be6e34cd9df096646e144cbc050ba3 Mon Sep 17 00:00:00 2001 From: AWolf81 Date: Wed, 19 Feb 2020 22:53:10 +0100 Subject: [PATCH 1/5] add special link handling --- browser/main/Main.js | 7 + .../codemirror/addon/hyperlink/hyperlink.js | 128 +++++++++++++++--- 2 files changed, 116 insertions(+), 19 deletions(-) diff --git a/browser/main/Main.js b/browser/main/Main.js index fae596552..d4484f1bb 100644 --- a/browser/main/Main.js +++ b/browser/main/Main.js @@ -181,6 +181,7 @@ class Main extends React.Component { 'menubar:togglemenubar', this.toggleMenuBarVisible.bind(this) ) + eventEmitter.on('dispatch:push', this.changeRoutePush.bind(this)) } componentWillUnmount() { @@ -189,6 +190,12 @@ class Main extends React.Component { 'menubar:togglemenubar', this.toggleMenuBarVisible.bind(this) ) + eventEmitter.off('dispatch:push', this.changeRoutePush.bind(this)) + } + + changeRoutePush(event, destination) { + const { dispatch } = this.props + dispatch(push(destination)) } toggleMenuBarVisible() { diff --git a/extra_scripts/codemirror/addon/hyperlink/hyperlink.js b/extra_scripts/codemirror/addon/hyperlink/hyperlink.js index a0ae55e9f..3562ede5b 100755 --- a/extra_scripts/codemirror/addon/hyperlink/hyperlink.js +++ b/extra_scripts/codemirror/addon/hyperlink/hyperlink.js @@ -1,22 +1,31 @@ -(function (mod) { - if (typeof exports === 'object' && typeof module === 'object') { // Common JS +;(function(mod) { + if (typeof exports === 'object' && typeof module === 'object') { + // Common JS mod(require('../codemirror/lib/codemirror')) - } else if (typeof define === 'function' && define.amd) { // AMD + } else if (typeof define === 'function' && define.amd) { + // AMD define(['../codemirror/lib/codemirror'], mod) - } else { // Plain browser env + } else { + // Plain browser env mod(CodeMirror) } -})(function (CodeMirror) { +})(function(CodeMirror) { 'use strict' const shell = require('electron').shell + const remote = require('electron').remote + const eventEmitter = { + emit: function() { + remote.getCurrentWindow().webContents.send.apply(null, arguments) + } + } const yOffset = 2 const macOS = global.process.platform === 'darwin' const modifier = macOS ? 'metaKey' : 'ctrlKey' class HyperLink { - constructor (cm) { + constructor(cm) { this.cm = cm this.lineDiv = cm.display.lineDiv @@ -28,11 +37,16 @@ this.tooltip = document.createElement('div') this.tooltipContent = document.createElement('div') this.tooltipIndicator = document.createElement('div') - this.tooltip.setAttribute('class', 'CodeMirror-hover CodeMirror-matchingbracket CodeMirror-selected') + this.tooltip.setAttribute( + 'class', + 'CodeMirror-hover CodeMirror-matchingbracket CodeMirror-selected' + ) this.tooltip.setAttribute('cm-ignore-events', 'true') this.tooltip.appendChild(this.tooltipContent) this.tooltip.appendChild(this.tooltipIndicator) - this.tooltipContent.textContent = `${macOS ? 'Cmd(⌘)' : 'Ctrl(^)'} + click to follow link` + this.tooltipContent.textContent = `${ + macOS ? 'Cmd(⌘)' : 'Ctrl(^)' + } + click to follow link` this.lineDiv.addEventListener('mousedown', this.onMouseDown) this.lineDiv.addEventListener('mouseenter', this.onMouseEnter, { @@ -47,7 +61,7 @@ passive: true }) } - getUrl (el) { + getUrl(el) { const className = el.className.split(' ') if (className.indexOf('cm-url') !== -1) { @@ -60,26 +74,99 @@ return null } - onMouseDown (e) { + specialLinkHandler(e, rawHref, linkHash) { + const isStartWithHash = rawHref[0] === '#' + + const extractIdRegex = /file:\/\/.*main.?\w*.html#/ // file://path/to/main(.development.)html + const regexNoteInternalLink = new RegExp(`${extractIdRegex.source}(.+)`) + if (isStartWithHash || regexNoteInternalLink.test(rawHref)) { + const posOfHash = linkHash.indexOf('#') + if (posOfHash > -1) { + const extractedId = linkHash.slice(posOfHash + 1) + const targetId = mdurl.encode(extractedId) + const targetElement = document.getElementById(targetId) // this.getWindow().document.getElementById(targetId) + + if (targetElement != null) { + this.scrollTo(0, targetElement.offsetTop) + } + return + } + } + + // this will match the new uuid v4 hash and the old hash + // e.g. + // :note:1c211eb7dcb463de6490 and + // :note:7dd23275-f2b4-49cb-9e93-3454daf1af9c + const regexIsNoteLink = /^:note:([a-zA-Z0-9-]{20,36})$/ + if (regexIsNoteLink.test(linkHash)) { + eventEmitter.emit('list:jump', linkHash.replace(':note:', '')) + return + } + + const regexIsLine = /^:line:[0-9]/ + if (regexIsLine.test(linkHash)) { + const numberPattern = /\d+/g + + const lineNumber = parseInt(linkHash.match(numberPattern)[0]) + eventEmitter.emit('line:jump', lineNumber) + return + } + + // this will match the old link format storage.key-note.key + // e.g. + // 877f99c3268608328037-1c211eb7dcb463de6490 + const regexIsLegacyNoteLink = /^(.{20})-(.{20})$/ + if (regexIsLegacyNoteLink.test(linkHash)) { + eventEmitter.emit('list:jump', linkHash.split('-')[1]) + return + } + + const regexIsTagLink = /^:tag:#([\w]+)$/ + if (regexIsTagLink.test(rawHref)) { + const tag = rawHref.match(regexIsTagLink)[1] + eventEmitter.emit('dispatch:push', `/tags/${encodeURIComponent(tag)}`) + return + } + } + onMouseDown(e) { const { target } = e if (!e[modifier]) { return } + const rawHref = e.target.innerText.trim().slice(1, -1) // get link text from markdown text + + if (!rawHref) return // not checked href because parser will create file://... string for [empty link]() + + const parser = document.createElement('a') + parser.href = rawHref + const { href, hash } = parser + + if (!rawHref) return // not checked href because parser will create file://... string for [empty link]() + + const linkHash = hash === '' ? rawHref : hash // needed because we're having special link formats that are removed by parser e.g. :line:10 + + this.specialLinkHandler(target, rawHref, linkHash) + const url = this.getUrl(target) + + // all special cases handled --> other case if (url) { e.preventDefault() shell.openExternal(url) } } - onMouseEnter (e) { + onMouseEnter(e) { const { target } = e const url = this.getUrl(target) if (url) { if (e[modifier]) { - target.classList.add('CodeMirror-activeline-background', 'CodeMirror-hyperlink') + target.classList.add( + 'CodeMirror-activeline-background', + 'CodeMirror-hyperlink' + ) } else { target.classList.add('CodeMirror-activeline-background') } @@ -87,14 +174,17 @@ this.showInfo(target) } } - onMouseLeave (e) { + onMouseLeave(e) { if (this.tooltip.parentElement === this.lineDiv) { - e.target.classList.remove('CodeMirror-activeline-background', 'CodeMirror-hyperlink') + e.target.classList.remove( + 'CodeMirror-activeline-background', + 'CodeMirror-hyperlink' + ) this.lineDiv.removeChild(this.tooltip) } } - onMouseMove (e) { + onMouseMove(e) { if (this.tooltip.parentElement === this.lineDiv) { if (e[modifier]) { e.target.classList.add('CodeMirror-hyperlink') @@ -103,25 +193,25 @@ } } } - showInfo (relatedTo) { + showInfo(relatedTo) { const b1 = relatedTo.getBoundingClientRect() const b2 = this.lineDiv.getBoundingClientRect() const tdiv = this.tooltip - tdiv.style.left = (b1.left - b2.left) + 'px' + tdiv.style.left = b1.left - b2.left + 'px' this.lineDiv.appendChild(tdiv) const b3 = tdiv.getBoundingClientRect() const top = b1.top - b2.top - b3.height - yOffset if (top < 0) { - tdiv.style.top = (b1.top - b2.top + b1.height + yOffset) + 'px' + tdiv.style.top = b1.top - b2.top + b1.height + yOffset + 'px' } else { tdiv.style.top = top + 'px' } } } - CodeMirror.defineOption('hyperlink', true, (cm) => { + CodeMirror.defineOption('hyperlink', true, cm => { const addon = new HyperLink(cm) }) }) From b15512668d363d96b445bf8fd08b845ddc19657e Mon Sep 17 00:00:00 2001 From: AWolf81 Date: Tue, 3 Mar 2020 07:29:22 +0100 Subject: [PATCH 2/5] address requested changes - tag link & redundant line --- .../codemirror/addon/hyperlink/hyperlink.js | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/extra_scripts/codemirror/addon/hyperlink/hyperlink.js b/extra_scripts/codemirror/addon/hyperlink/hyperlink.js index 3562ede5b..542f7126f 100755 --- a/extra_scripts/codemirror/addon/hyperlink/hyperlink.js +++ b/extra_scripts/codemirror/addon/hyperlink/hyperlink.js @@ -1,4 +1,4 @@ -;(function(mod) { +;(function (mod) { if (typeof exports === 'object' && typeof module === 'object') { // Common JS mod(require('../codemirror/lib/codemirror')) @@ -9,13 +9,13 @@ // Plain browser env mod(CodeMirror) } -})(function(CodeMirror) { +})(function (CodeMirror) { 'use strict' const shell = require('electron').shell const remote = require('electron').remote const eventEmitter = { - emit: function() { + emit: function () { remote.getCurrentWindow().webContents.send.apply(null, arguments) } } @@ -25,7 +25,7 @@ const modifier = macOS ? 'metaKey' : 'ctrlKey' class HyperLink { - constructor(cm) { + constructor (cm) { this.cm = cm this.lineDiv = cm.display.lineDiv @@ -61,7 +61,7 @@ passive: true }) } - getUrl(el) { + getUrl (el) { const className = el.className.split(' ') if (className.indexOf('cm-url') !== -1) { @@ -74,7 +74,7 @@ return null } - specialLinkHandler(e, rawHref, linkHash) { + specialLinkHandler (e, rawHref, linkHash) { const isStartWithHash = rawHref[0] === '#' const extractIdRegex = /file:\/\/.*main.?\w*.html#/ // file://path/to/main(.development.)html @@ -121,14 +121,14 @@ return } - const regexIsTagLink = /^:tag:#([\w]+)$/ + const regexIsTagLink = /^:tag:([\w]+)$/ if (regexIsTagLink.test(rawHref)) { const tag = rawHref.match(regexIsTagLink)[1] eventEmitter.emit('dispatch:push', `/tags/${encodeURIComponent(tag)}`) return } } - onMouseDown(e) { + onMouseDown (e) { const { target } = e if (!e[modifier]) { return @@ -142,8 +142,6 @@ parser.href = rawHref const { href, hash } = parser - if (!rawHref) return // not checked href because parser will create file://... string for [empty link]() - const linkHash = hash === '' ? rawHref : hash // needed because we're having special link formats that are removed by parser e.g. :line:10 this.specialLinkHandler(target, rawHref, linkHash) @@ -157,7 +155,7 @@ shell.openExternal(url) } } - onMouseEnter(e) { + onMouseEnter (e) { const { target } = e const url = this.getUrl(target) @@ -174,7 +172,7 @@ this.showInfo(target) } } - onMouseLeave(e) { + onMouseLeave (e) { if (this.tooltip.parentElement === this.lineDiv) { e.target.classList.remove( 'CodeMirror-activeline-background', @@ -184,7 +182,7 @@ this.lineDiv.removeChild(this.tooltip) } } - onMouseMove(e) { + onMouseMove (e) { if (this.tooltip.parentElement === this.lineDiv) { if (e[modifier]) { e.target.classList.add('CodeMirror-hyperlink') @@ -193,7 +191,7 @@ } } } - showInfo(relatedTo) { + showInfo (relatedTo) { const b1 = relatedTo.getBoundingClientRect() const b2 = this.lineDiv.getBoundingClientRect() const tdiv = this.tooltip From 32c00050953eb5935641a9b59ca6c4233742fd50 Mon Sep 17 00:00:00 2001 From: AWolf81 Date: Sun, 8 Mar 2020 21:58:27 +0100 Subject: [PATCH 3/5] WIP: Change space before parens. Tag link handling issue present. --- .../codemirror/addon/hyperlink/hyperlink.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/extra_scripts/codemirror/addon/hyperlink/hyperlink.js b/extra_scripts/codemirror/addon/hyperlink/hyperlink.js index 542f7126f..0ac86768a 100755 --- a/extra_scripts/codemirror/addon/hyperlink/hyperlink.js +++ b/extra_scripts/codemirror/addon/hyperlink/hyperlink.js @@ -1,4 +1,4 @@ -;(function (mod) { +;(function(mod) { if (typeof exports === 'object' && typeof module === 'object') { // Common JS mod(require('../codemirror/lib/codemirror')) @@ -9,13 +9,13 @@ // Plain browser env mod(CodeMirror) } -})(function (CodeMirror) { +})(function(CodeMirror) { 'use strict' const shell = require('electron').shell const remote = require('electron').remote const eventEmitter = { - emit: function () { + emit: function() { remote.getCurrentWindow().webContents.send.apply(null, arguments) } } @@ -25,7 +25,7 @@ const modifier = macOS ? 'metaKey' : 'ctrlKey' class HyperLink { - constructor (cm) { + constructor(cm) { this.cm = cm this.lineDiv = cm.display.lineDiv @@ -61,7 +61,7 @@ passive: true }) } - getUrl (el) { + getUrl(el) { const className = el.className.split(' ') if (className.indexOf('cm-url') !== -1) { @@ -74,7 +74,7 @@ return null } - specialLinkHandler (e, rawHref, linkHash) { + specialLinkHandler(e, rawHref, linkHash) { const isStartWithHash = rawHref[0] === '#' const extractIdRegex = /file:\/\/.*main.?\w*.html#/ // file://path/to/main(.development.)html @@ -128,7 +128,7 @@ return } } - onMouseDown (e) { + onMouseDown(e) { const { target } = e if (!e[modifier]) { return @@ -155,7 +155,7 @@ shell.openExternal(url) } } - onMouseEnter (e) { + onMouseEnter(e) { const { target } = e const url = this.getUrl(target) @@ -172,7 +172,7 @@ this.showInfo(target) } } - onMouseLeave (e) { + onMouseLeave(e) { if (this.tooltip.parentElement === this.lineDiv) { e.target.classList.remove( 'CodeMirror-activeline-background', @@ -182,7 +182,7 @@ this.lineDiv.removeChild(this.tooltip) } } - onMouseMove (e) { + onMouseMove(e) { if (this.tooltip.parentElement === this.lineDiv) { if (e[modifier]) { e.target.classList.add('CodeMirror-hyperlink') @@ -191,7 +191,7 @@ } } } - showInfo (relatedTo) { + showInfo(relatedTo) { const b1 = relatedTo.getBoundingClientRect() const b2 = this.lineDiv.getBoundingClientRect() const tdiv = this.tooltip From 905a13abfdb90f3da17ee1f1ee3dbcd1f18ff5cc Mon Sep 17 00:00:00 2001 From: AWolf81 Date: Sun, 8 Mar 2020 22:00:48 +0100 Subject: [PATCH 4/5] Remove duplicated if --- browser/components/MarkdownPreview.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 9e7f72287..569d1dcac 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -1126,8 +1126,6 @@ class MarkdownPreview extends React.Component { const isStartWithHash = rawHref[0] === '#' const { href, hash } = parser - if (!rawHref) return // not checked href because parser will create file://... string for [empty link]() - const linkHash = hash === '' ? rawHref : hash // needed because we're having special link formats that are removed by parser e.g. :line:10 const extractIdRegex = /file:\/\/.*main.?\w*.html#/ // file://path/to/main(.development.)html From 7c707acd80cb8e39a484a3f081e75ee806d46fad Mon Sep 17 00:00:00 2001 From: AWolf81 Date: Fri, 13 Mar 2020 23:26:01 +0100 Subject: [PATCH 5/5] add gfm with modified regex & improve link text handling --- .../codemirror/addon/hyperlink/hyperlink.js | 22 +- extra_scripts/codemirror/mode/bfm/bfm.js | 327 ++++++++++-------- extra_scripts/codemirror/mode/gfm/gfm.js | 157 +++++++++ lib/main.development.html | 2 +- lib/main.production.html | 2 +- 5 files changed, 357 insertions(+), 153 deletions(-) create mode 100644 extra_scripts/codemirror/mode/gfm/gfm.js diff --git a/extra_scripts/codemirror/addon/hyperlink/hyperlink.js b/extra_scripts/codemirror/addon/hyperlink/hyperlink.js index 0ac86768a..4ccbbe011 100755 --- a/extra_scripts/codemirror/addon/hyperlink/hyperlink.js +++ b/extra_scripts/codemirror/addon/hyperlink/hyperlink.js @@ -65,7 +65,16 @@ const className = el.className.split(' ') if (className.indexOf('cm-url') !== -1) { - const match = /^\((.*)\)|\[(.*)\]|(.*)$/.exec(el.textContent) + // multiple cm-url because of search term + const cmUrlSpans = Array.from( + el.parentNode.getElementsByClassName('cm-url') + ) + const textContent = + cmUrlSpans.length > 1 + ? cmUrlSpans.map(span => span.textContent).join('') + : el.textContent + + const match = /^\((.*)\)|\[(.*)\]|(.*)$/.exec(textContent) const url = match[1] || match[2] || match[3] // `:storage` is the value of the variable `STORAGE_FOLDER_PLACEHOLDER` defined in `browser/main/lib/dataApi/attachmentManagement` @@ -134,7 +143,16 @@ return } - const rawHref = e.target.innerText.trim().slice(1, -1) // get link text from markdown text + // Create URL spans array used for special case "search term is hitting a link". + const cmUrlSpans = Array.from( + e.target.parentNode.getElementsByClassName('cm-url') + ) + + const innerText = + cmUrlSpans.length > 1 + ? cmUrlSpans.map(span => span.textContent).join('') + : e.target.innerText + const rawHref = innerText.trim().slice(1, -1) // get link text from markdown text if (!rawHref) return // not checked href because parser will create file://... string for [empty link]() diff --git a/extra_scripts/codemirror/mode/bfm/bfm.js b/extra_scripts/codemirror/mode/bfm/bfm.js index 80f797b9f..d08183cdc 100644 --- a/extra_scripts/codemirror/mode/bfm/bfm.js +++ b/extra_scripts/codemirror/mode/bfm/bfm.js @@ -1,10 +1,20 @@ -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../codemirror/lib/codemirror"), require("../codemirror/mode/gfm/gfm"), require("../codemirror/mode/yaml-frontmatter/yaml-frontmatter")) - else if (typeof define == "function" && define.amd) // AMD - define(["../codemirror/lib/codemirror", "../codemirror/mode/gfm/gfm", "../codemirror/mode/yaml-frontmatter/yaml-frontmatter"], mod) - else // Plain browser env - mod(CodeMirror) +;(function(mod) { + if (typeof exports == 'object' && typeof module == 'object') + // CommonJS + mod( + require('../codemirror/lib/codemirror'), + require('../codemirror/mode/gfm/gfm'), + require('../codemirror/mode/yaml-frontmatter/yaml-frontmatter') + ) + else if (typeof define == 'function' && define.amd) + // AMD + define([ + '../codemirror/lib/codemirror', + '../codemirror/mode/gfm/gfm', + '../codemirror/mode/yaml-frontmatter/yaml-frontmatter' + ], mod) + // Plain browser env + else mod(CodeMirror) })(function(CodeMirror) { 'use strict' @@ -45,189 +55,208 @@ } } - CodeMirror.defineMode('bfm', function (config, baseConfig) { - baseConfig.name = 'yaml-frontmatter' - const baseMode = CodeMirror.getMode(config, baseConfig) + CodeMirror.defineMode( + 'bfm', + function(config, baseConfig) { + baseConfig.name = 'yaml-frontmatter' + const baseMode = CodeMirror.getMode(config, baseConfig) - return { - startState: function() { - return { - baseState: CodeMirror.startState(baseMode), - - basePos: 0, - baseCur: null, - overlayPos: 0, - overlayCur: null, - streamSeen: null, - - fencedEndRE: null, + return { + startState: function() { + return { + baseState: CodeMirror.startState(baseMode), - inTable: false, - rowIndex: 0 - } - }, - copyState: function(s) { - return { - baseState: CodeMirror.copyState(baseMode, s.baseState), + basePos: 0, + baseCur: null, + overlayPos: 0, + overlayCur: null, + streamSeen: null, - basePos: s.basePos, - baseCur: null, - overlayPos: s.overlayPos, - overlayCur: null, + fencedEndRE: null, - fencedMode: s.fencedMode, - fencedState: s.fencedMode ? CodeMirror.copyState(s.fencedMode, s.fencedState) : null, + inTable: false, + rowIndex: 0 + } + }, + copyState: function(s) { + return { + baseState: CodeMirror.copyState(baseMode, s.baseState), - fencedEndRE: s.fencedEndRE, + basePos: s.basePos, + baseCur: null, + overlayPos: s.overlayPos, + overlayCur: null, - inTable: s.inTable, - rowIndex: s.rowIndex - } - }, - token: function(stream, state) { - const initialPos = stream.pos + fencedMode: s.fencedMode, + fencedState: s.fencedMode + ? CodeMirror.copyState(s.fencedMode, s.fencedState) + : null, - if (state.fencedEndRE && stream.match(state.fencedEndRE)) { - state.fencedEndRE = null - state.fencedMode = null - state.fencedState = null + fencedEndRE: s.fencedEndRE, - stream.pos = initialPos - } - else { - if (state.fencedMode) { - return state.fencedMode.token(stream, state.fencedState) + inTable: s.inTable, + rowIndex: s.rowIndex } + }, + token: function(stream, state) { + const initialPos = stream.pos - const match = stream.match(fencedCodeRE, true) - if (match) { - state.fencedEndRE = new RegExp(match[1] + '+ *$') + if (state.fencedEndRE && stream.match(state.fencedEndRE)) { + state.fencedEndRE = null + state.fencedMode = null + state.fencedState = null - state.fencedMode = getMode(match[2], match[3], config, stream.lineOracle.doc.cm) + stream.pos = initialPos + } else { if (state.fencedMode) { - state.fencedState = CodeMirror.startState(state.fencedMode) + return state.fencedMode.token(stream, state.fencedState) } - stream.pos = initialPos + const match = stream.match(fencedCodeRE, true) + if (match) { + state.fencedEndRE = new RegExp(match[1] + '+ *$') + + state.fencedMode = getMode( + match[2], + match[3], + config, + stream.lineOracle.doc.cm + ) + if (state.fencedMode) { + state.fencedState = CodeMirror.startState(state.fencedMode) + } + + stream.pos = initialPos + } } - } - - if (stream != state.streamSeen || Math.min(state.basePos, state.overlayPos) < stream.start) { - state.streamSeen = stream - state.basePos = state.overlayPos = stream.start - } - if (stream.start == state.basePos) { - state.baseCur = baseMode.token(stream, state.baseState) - state.basePos = stream.pos - } - if (stream.start == state.overlayPos) { - stream.pos = stream.start - state.overlayCur = this.overlayToken(stream, state) - state.overlayPos = stream.pos - } - stream.pos = Math.min(state.basePos, state.overlayPos) + if ( + stream != state.streamSeen || + Math.min(state.basePos, state.overlayPos) < stream.start + ) { + state.streamSeen = stream + state.basePos = state.overlayPos = stream.start + } - if (state.overlayCur == null) { - return state.baseCur - } - else if (state.baseCur != null && state.combineTokens) { - return state.baseCur + ' ' + state.overlayCur - } - else { - return state.overlayCur - } - }, - overlayToken: function(stream, state) { - state.combineTokens = false + if (stream.start == state.basePos) { + state.baseCur = baseMode.token(stream, state.baseState) + state.basePos = stream.pos + } + if (stream.start == state.overlayPos) { + stream.pos = stream.start + state.overlayCur = this.overlayToken(stream, state) + state.overlayPos = stream.pos + } + stream.pos = Math.min(state.basePos, state.overlayPos) - if (state.fencedEndRE && stream.match(state.fencedEndRE)) { - state.fencedEndRE = null - state.localMode = null - state.localState = null + if (state.overlayCur == null) { + return state.baseCur + } else if (state.baseCur != null && state.combineTokens) { + return state.baseCur + ' ' + state.overlayCur + } else { + return state.overlayCur + } + }, + overlayToken: function(stream, state) { + state.combineTokens = false - return null - } + if (state.fencedEndRE && stream.match(state.fencedEndRE)) { + state.fencedEndRE = null + state.localMode = null + state.localState = null - if (state.localMode) { - return state.localMode.token(stream, state.localState) || '' - } - - const match = stream.match(fencedCodeRE, true) - if (match) { - state.fencedEndRE = new RegExp(match[1] + '+ *$') + return null + } - state.localMode = getMode(match[2], match[3], config, stream.lineOracle.doc.cm) if (state.localMode) { - state.localState = CodeMirror.startState(state.localMode) + return state.localMode.token(stream, state.localState) || '' } - return null - } + const match = stream.match(fencedCodeRE, true) + if (match) { + state.fencedEndRE = new RegExp(match[1] + '+ *$') - state.combineTokens = true + state.localMode = getMode( + match[2], + match[3], + config, + stream.lineOracle.doc.cm + ) + if (state.localMode) { + state.localState = CodeMirror.startState(state.localMode) + } - if (state.inTable) { - if (stream.match(/^\|/)) { - ++state.rowIndex + return null + } - stream.skipToEnd() + state.combineTokens = true - if (state.rowIndex === 1) { - return 'table table-separator' - } else if (state.rowIndex % 2 === 0) { - return 'table table-row table-row-even' + if (state.inTable) { + if (stream.match(/^\|/)) { + ++state.rowIndex + + stream.skipToEnd() + + if (state.rowIndex === 1) { + return 'table table-separator' + } else if (state.rowIndex % 2 === 0) { + return 'table table-row table-row-even' + } else { + return 'table table-row table-row-odd' + } } else { - return 'table table-row table-row-odd' + state.inTable = false + + stream.skipToEnd() + return null } - } else { - state.inTable = false + } else if (stream.match(/^\|/)) { + state.inTable = true + state.rowIndex = 0 stream.skipToEnd() - return null + return 'table table-header' } - } else if (stream.match(/^\|/)) { - state.inTable = true - state.rowIndex = 0 stream.skipToEnd() - return 'table table-header' - } - - stream.skipToEnd() - return null - }, - electricChars: baseMode.electricChars, - innerMode: function(state) { - if (state.fencedMode) { - return { - mode: state.fencedMode, - state: state.fencedState + return null + }, + electricChars: baseMode.electricChars, + innerMode: function(state) { + if (state.fencedMode) { + return { + mode: state.fencedMode, + state: state.fencedState + } + } else { + return { + mode: baseMode, + state: state.baseState + } } - } else { - return { - mode: baseMode, - state: state.baseState + }, + blankLine: function(state) { + state.inTable = false + + if (state.fencedMode) { + return ( + state.fencedMode.blankLine && + state.fencedMode.blankLine(state.fencedState) + ) + } else { + return baseMode.blankLine(state.baseState) } } - }, - blankLine: function(state) { - state.inTable = false - - if (state.fencedMode) { - return state.fencedMode.blankLine && state.fencedMode.blankLine(state.fencedState) - } else { - return baseMode.blankLine(state.baseState) - } } - } - }, 'yaml-frontmatter') + }, + 'yaml-frontmatter' + ) CodeMirror.defineMIME('text/x-bfm', 'bfm') CodeMirror.modeInfo.push({ - name: "Boost Flavored Markdown", - mime: "text/x-bfm", - mode: "bfm" + name: 'Boost Flavored Markdown', + mime: 'text/x-bfm', + mode: 'bfm' }) -}) \ No newline at end of file +}) diff --git a/extra_scripts/codemirror/mode/gfm/gfm.js b/extra_scripts/codemirror/mode/gfm/gfm.js new file mode 100644 index 000000000..9fed75911 --- /dev/null +++ b/extra_scripts/codemirror/mode/gfm/gfm.js @@ -0,0 +1,157 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +;(function(mod) { + if (typeof exports == 'object' && typeof module == 'object') + // CommonJS + mod( + require('../codemirror/lib/codemirror'), + require('../codemirror/mode/markdown/markdown'), + require('../codemirror/addon/mode/overlay') + ) + else if (typeof define == 'function' && define.amd) + // AMD + define([ + '../codemirror/lib/codemirror', + '../codemirror/mode/markdown/markdown', + '../codemirror/addon/mode/overlay' + ], mod) + // Plain browser env + else mod(CodeMirror) +})(function(CodeMirror) { + 'use strict' + + var urlRE = /^((?:(?:aaas?|about|acap|adiumxtra|af[ps]|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|cap|chrome(?:-extension)?|cid|coap|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-(?:playcontainer|playsingle)|dns|doi|dtn|dvb|ed2k|facetime|feed|file|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|hcp|https?|iax|icap|icon|im|imap|info|ipn|ipp|irc[6s]?|iris(?:\.beep|\.lwz|\.xpc|\.xpcs)?|itms|jar|javascript|jms|keyparc|lastfm|ldaps?|magnet|mailto|maps|market|message|mid|mms|ms-help|msnim|msrps?|mtqp|mumble|mupdate|mvn|news|nfs|nih?|nntp|notes|oid|opaquelocktoken|palm|paparazzi|platform|pop|pres|proxy|psyc|query|res(?:ource)?|rmi|rsync|rtmp|rtsp|secondlife|service|session|sftp|sgn|shttp|sieve|sips?|skype|sm[bs]|snmp|soap\.beeps?|soldat|spotify|ssh|steam|svn|teamspeak|tel(?:net)?|tftp|things|thismessage|tip|tn3270|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|view-source|webcal|wss?|wtai|wyciwyg|xcon(?:-userid)?|xfire|xmlrpc\.beeps?|xmpp|xri|ymsgr|z39\.50[rs]?):(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i + + CodeMirror.defineMode( + 'gfm', + function(config, modeConfig) { + var codeDepth = 0 + function blankLine(state) { + state.code = false + return null + } + var gfmOverlay = { + startState: function() { + return { + code: false, + codeBlock: false, + ateSpace: false + } + }, + copyState: function(s) { + return { + code: s.code, + codeBlock: s.codeBlock, + ateSpace: s.ateSpace + } + }, + token: function(stream, state) { + state.combineTokens = null + + // Hack to prevent formatting override inside code blocks (block and inline) + if (state.codeBlock) { + if (stream.match(/^```+/)) { + state.codeBlock = false + return null + } + stream.skipToEnd() + return null + } + if (stream.sol()) { + state.code = false + } + if (stream.sol() && stream.match(/^```+/)) { + stream.skipToEnd() + state.codeBlock = true + return null + } + // If this block is changed, it may need to be updated in Markdown mode + if (stream.peek() === '`') { + stream.next() + var before = stream.pos + stream.eatWhile('`') + var difference = 1 + stream.pos - before + if (!state.code) { + codeDepth = difference + state.code = true + } else { + if (difference === codeDepth) { + // Must be exact + state.code = false + } + } + return null + } else if (state.code) { + stream.next() + return null + } + // Check if space. If so, links can be formatted later on + if (stream.eatSpace()) { + state.ateSpace = true + return null + } + if (stream.sol() || state.ateSpace) { + state.ateSpace = false + if (modeConfig.gitHubSpice !== false) { + if ( + stream.match( + /^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?=.{0,6}\d)(?:[a-f0-9]{7,40}\b)/ + ) + ) { + // User/Project@SHA + // User@SHA + // SHA + state.combineTokens = true + return 'link' + } else if ( + stream.match( + /^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/ + ) + ) { + // User/Project#Num + // User#Num + // #Num + state.combineTokens = true + return 'link' + } + } + } + if ( + stream.match(urlRE) && + stream.string.slice(stream.start - 2, stream.start) != '](' && + (stream.start == 0 || + /\W/.test(stream.string.charAt(stream.start - 1))) + ) { + // URLs + // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls + // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine + // And then limited url schemes to the CommonMark list, so foo:bar isn't matched as a URL + state.combineTokens = true + return 'link' + } + stream.next() + return null + }, + blankLine: blankLine + } + + var markdownConfig = { + taskLists: true, + strikethrough: true, + emoji: true + } + for (var attr in modeConfig) { + markdownConfig[attr] = modeConfig[attr] + } + markdownConfig.name = 'markdown' + return CodeMirror.overlayMode( + CodeMirror.getMode(config, markdownConfig), + gfmOverlay + ) + }, + 'markdown' + ) + + CodeMirror.defineMIME('text/x-gfm', 'gfm') +}) diff --git a/lib/main.development.html b/lib/main.development.html index 63e50af18..900c66c75 100644 --- a/lib/main.development.html +++ b/lib/main.development.html @@ -108,12 +108,12 @@ - + diff --git a/lib/main.production.html b/lib/main.production.html index aea19e3c6..05d803450 100644 --- a/lib/main.production.html +++ b/lib/main.production.html @@ -104,12 +104,12 @@ - +