mirror of
https://github.com/marktext/marktext.git
synced 2025-05-02 11:01:34 +08:00
Add GitLab math block support (#2119)
This commit is contained in:
parent
79c7200406
commit
5bb7856cb4
@ -289,6 +289,11 @@
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"isGitlabCompatibilityEnabled": {
|
||||
"description": "Markdown-Enable GitLab compatibility mode.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"sequenceTheme": {
|
||||
"description": "Markdown--Sequence diagram theme",
|
||||
"enum": [
|
||||
|
@ -282,7 +282,8 @@ export const MUYA_DEFAULT_OPTION = {
|
||||
|
||||
// Markdown extensions
|
||||
superSubScript: false,
|
||||
footnote: false
|
||||
footnote: false,
|
||||
isGitlabCompatibilityEnabled: false
|
||||
}
|
||||
|
||||
// export const DIAGRAM_TEMPLATE = {
|
||||
|
@ -35,6 +35,9 @@ const codeBlockCtrl = ContentState => {
|
||||
|
||||
ContentState.prototype.selectLanguage = function (paragraph, lang) {
|
||||
const block = this.getBlock(paragraph.id)
|
||||
if (lang === 'math' && this.isGitlabCompatibilityEnabled && this.updateMathBlock(block)) {
|
||||
return
|
||||
}
|
||||
this.updateCodeLanguage(block, lang)
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,18 @@ const FUNCTION_TYPE_LANG = {
|
||||
}
|
||||
|
||||
const containerCtrl = ContentState => {
|
||||
ContentState.prototype.createContainerBlock = function (functionType, value = '') {
|
||||
ContentState.prototype.createContainerBlock = function (functionType, value = '', style = undefined) {
|
||||
const figureBlock = this.createBlock('figure', {
|
||||
functionType
|
||||
})
|
||||
|
||||
if (functionType === 'multiplemath') {
|
||||
if (style === undefined) {
|
||||
figureBlock.mathStyle = this.isGitlabCompatibilityEnabled ? 'gitlab' : ''
|
||||
}
|
||||
figureBlock.mathStyle = style
|
||||
}
|
||||
|
||||
const { preBlock, preview } = this.createPreAndPreview(functionType, value)
|
||||
this.appendChild(figureBlock, preBlock)
|
||||
this.appendChild(figureBlock, preview)
|
||||
@ -56,11 +63,18 @@ const containerCtrl = ContentState => {
|
||||
return { preBlock, preview }
|
||||
}
|
||||
|
||||
ContentState.prototype.initContainerBlock = function (functionType, block) { // p block
|
||||
ContentState.prototype.initContainerBlock = function (functionType, block, style = undefined) { // p block
|
||||
block.type = 'figure'
|
||||
block.functionType = functionType
|
||||
block.children = []
|
||||
|
||||
if (functionType === 'multiplemath') {
|
||||
if (style === undefined) {
|
||||
block.mathStyle = this.isGitlabCompatibilityEnabled ? 'gitlab' : ''
|
||||
}
|
||||
block.mathStyle = style
|
||||
}
|
||||
|
||||
const { preBlock, preview } = this.createPreAndPreview(functionType)
|
||||
|
||||
this.appendChild(block, preBlock)
|
||||
@ -84,11 +98,36 @@ const containerCtrl = ContentState => {
|
||||
}
|
||||
|
||||
ContentState.prototype.updateMathBlock = function (block) {
|
||||
const { type } = block
|
||||
if (type !== 'p') return false
|
||||
const { text } = block.children[0]
|
||||
const functionType = 'multiplemath'
|
||||
return text.trim() === '$$' ? this.initContainerBlock(functionType, block) : false
|
||||
const { type } = block
|
||||
|
||||
// TODO(GitLab): Allow "functionType" 'languageInput' to convert an existing
|
||||
// code block into math block.
|
||||
if (type === 'span' && block.functionType === 'paragraphContent') {
|
||||
const isMathBlock = !!block.text.match(/^`{3,}math\s*/)
|
||||
if (isMathBlock) {
|
||||
const result = this.initContainerBlock(functionType, block, 'gitlab')
|
||||
if (result) {
|
||||
// Set cursor at the first line
|
||||
const { key } = result
|
||||
const offset = 0
|
||||
this.cursor = {
|
||||
start: { key, offset },
|
||||
end: { key, offset }
|
||||
}
|
||||
|
||||
// Force render
|
||||
this.partialRender()
|
||||
return result
|
||||
}
|
||||
}
|
||||
return false
|
||||
} else if (type !== 'p') {
|
||||
return false
|
||||
}
|
||||
|
||||
const { text } = block.children[0]
|
||||
return text.trim() === '$$' ? this.initContainerBlock(functionType, block, '') : false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,8 +244,8 @@ const copyCutCtrl = ContentState => {
|
||||
|
||||
const table = this.createTableInFigure({ rows: row, columns: column }, tableContents)
|
||||
this.appendChild(figureBlock, table)
|
||||
const listIndentation = this.listIndentation
|
||||
const markdown = new ExportMarkdown([figureBlock], listIndentation).generate()
|
||||
const { isGitlabCompatibilityEnabled, listIndentation } = this
|
||||
const markdown = new ExportMarkdown([figureBlock], listIndentation, isGitlabCompatibilityEnabled).generate()
|
||||
|
||||
event.clipboardData.setData('text/html', '')
|
||||
event.clipboardData.setData('text/plain', markdown)
|
||||
@ -281,7 +281,9 @@ const copyCutCtrl = ContentState => {
|
||||
case 'copyAsHtml': {
|
||||
event.clipboardData.setData('text/html', '')
|
||||
event.clipboardData.setData('text/plain', getSanitizeHtml(text, {
|
||||
superSubScript: this.muya.options.superSubScript
|
||||
superSubScript: this.muya.options.superSubScript,
|
||||
footnote: this.muya.options.footnote,
|
||||
isGitlabCompatibilityEnabled: this.muya.options.isGitlabCompatibilityEnabled
|
||||
}))
|
||||
break
|
||||
}
|
||||
@ -290,8 +292,8 @@ const copyCutCtrl = ContentState => {
|
||||
const block = typeof copyInfo === 'string' ? this.getBlock(copyInfo) : copyInfo
|
||||
if (!block) return
|
||||
const anchor = this.getAnchor(block)
|
||||
const listIndentation = this.listIndentation
|
||||
const markdown = new ExportMarkdown([anchor], listIndentation).generate()
|
||||
const { isGitlabCompatibilityEnabled, listIndentation } = this
|
||||
const markdown = new ExportMarkdown([anchor], listIndentation, isGitlabCompatibilityEnabled).generate()
|
||||
event.clipboardData.setData('text/html', '')
|
||||
event.clipboardData.setData('text/plain', markdown)
|
||||
break
|
||||
|
@ -321,8 +321,12 @@ const paragraphCtrl = ContentState => {
|
||||
lang
|
||||
})
|
||||
|
||||
const listIndentation = this.listIndentation
|
||||
const markdown = new ExportMarkdown(children.slice(startIndex, endIndex + 1), listIndentation).generate()
|
||||
const { isGitlabCompatibilityEnabled, listIndentation } = this
|
||||
const markdown = new ExportMarkdown(
|
||||
children.slice(startIndex, endIndex + 1),
|
||||
listIndentation,
|
||||
isGitlabCompatibilityEnabled
|
||||
).generate()
|
||||
const codeContent = this.createBlock('span', {
|
||||
text: markdown,
|
||||
lang,
|
||||
|
@ -127,8 +127,8 @@ class Muya {
|
||||
|
||||
getMarkdown () {
|
||||
const blocks = this.contentState.getBlocks()
|
||||
const listIndentation = this.contentState.listIndentation
|
||||
return new ExportMarkdown(blocks, listIndentation).generate()
|
||||
const { isGitlabCompatibilityEnabled, listIndentation } = this.contentState
|
||||
return new ExportMarkdown(blocks, listIndentation, isGitlabCompatibilityEnabled).generate()
|
||||
}
|
||||
|
||||
getHistory () {
|
||||
|
@ -36,6 +36,7 @@ export const block = {
|
||||
// extra
|
||||
frontmatter: /^(?:(?:---\n([\s\S]+?)---)|(?:\+\+\+\n([\s\S]+?)\+\+\+)|(?:;;;\n([\s\S]+?);;;)|(?:\{\n([\s\S]+?)\}))(?:\n{2,}|\n{1,2}$)/,
|
||||
multiplemath: /^\$\$\n([\s\S]+?)\n\$\$(?:\n+|$)/,
|
||||
multiplemathGitlab: /^ {0,3}(`{3,})math\n(?:(|[\s\S]*?)\n)(?: {0,3}\1`* *(?:\n+|$)|$)/, // Math inside a code block (GitLab display math)
|
||||
footnote: /^\[\^([^\^\[\]\s]+?)\]:[\s\S]+?(?=\n *\n {0,3}[^ ]+|$)/
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,12 @@ Lexer.prototype.lex = function (src) {
|
||||
*/
|
||||
|
||||
Lexer.prototype.token = function (src, top) {
|
||||
const { frontMatter, math, footnote } = this.options
|
||||
const {
|
||||
footnote,
|
||||
frontMatter,
|
||||
isGitlabCompatibilityEnabled,
|
||||
math
|
||||
} = this.options
|
||||
src = src.replace(/^ +$/gm, '')
|
||||
|
||||
let loose
|
||||
@ -149,10 +154,25 @@ Lexer.prototype.token = function (src, top) {
|
||||
src = src.substring(cap[0].length)
|
||||
this.tokens.push({
|
||||
type: 'multiplemath',
|
||||
text: cap[1]
|
||||
text: cap[1],
|
||||
mathStyle: ''
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// match GitLab display math blocks (```math)
|
||||
if (isGitlabCompatibilityEnabled) {
|
||||
cap = this.rules.multiplemathGitlab.exec(src)
|
||||
if (cap) {
|
||||
src = src.substring(cap[0].length)
|
||||
this.tokens.push({
|
||||
type: 'multiplemath',
|
||||
text: cap[2] || '',
|
||||
mathStyle: 'gitlab'
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// footnote
|
||||
|
@ -29,5 +29,6 @@ export default {
|
||||
math: true,
|
||||
frontMatter: true,
|
||||
superSubScript: false,
|
||||
footnote: false
|
||||
footnote: false,
|
||||
isGitlabCompatibilityEnabled: false
|
||||
}
|
||||
|
@ -564,6 +564,9 @@
|
||||
},
|
||||
"latex": {
|
||||
"title": "LaTeX",
|
||||
"alias": [
|
||||
"math"
|
||||
],
|
||||
"ext": [
|
||||
"text",
|
||||
"ltx",
|
||||
|
@ -114,6 +114,7 @@ class ExportHtml {
|
||||
let html = marked(this.markdown, {
|
||||
superSubScript: this.muya ? this.muya.options.superSubScript : false,
|
||||
footnote: this.muya ? this.muya.options.footnote : false,
|
||||
isGitlabCompatibilityEnabled: this.muya ? this.muya.options.isGitlabCompatibilityEnabled : false,
|
||||
highlight (code, lang) {
|
||||
// Language may be undefined (GH#591)
|
||||
if (!lang) {
|
||||
|
@ -10,11 +10,12 @@
|
||||
*/
|
||||
|
||||
class ExportMarkdown {
|
||||
constructor (blocks, listIndentation = 1) {
|
||||
constructor (blocks, listIndentation = 1, isGitlabCompatibilityEnabled = false) {
|
||||
this.blocks = blocks
|
||||
this.listType = [] // 'ul' or 'ol'
|
||||
// helper to translate the first tight item in a nested list
|
||||
this.isLooseParentList = true
|
||||
this.isGitlabCompatibilityEnabled = !!isGitlabCompatibilityEnabled
|
||||
|
||||
// set and validate settings
|
||||
this.listIndentation = 'number'
|
||||
@ -229,12 +230,20 @@ class ExportMarkdown {
|
||||
}
|
||||
|
||||
normalizeMultipleMath (block, /* figure */ indent) {
|
||||
const { isGitlabCompatibilityEnabled } = this
|
||||
let startToken = '$$'
|
||||
let endToken = '$$'
|
||||
if (isGitlabCompatibilityEnabled && block.mathStyle === 'gitlab') {
|
||||
startToken = '```math'
|
||||
endToken = '```'
|
||||
}
|
||||
|
||||
const result = []
|
||||
result.push(`${indent}$$\n`)
|
||||
result.push(`${indent}${startToken}\n`)
|
||||
for (const line of block.children[0].children[0].children) {
|
||||
result.push(`${indent}${line.text}\n`)
|
||||
}
|
||||
result.push(`${indent}$$\n`)
|
||||
result.push(`${indent}${endToken}\n`)
|
||||
return result.join('')
|
||||
}
|
||||
|
||||
|
@ -77,9 +77,19 @@ const importRegister = ContentState => {
|
||||
nextSibling: null,
|
||||
children: []
|
||||
}
|
||||
const {
|
||||
footnote,
|
||||
isGitlabCompatibilityEnabled,
|
||||
superSubScript,
|
||||
trimUnnecessaryCodeBlockEmptyLines
|
||||
} = this.muya.options
|
||||
|
||||
const { trimUnnecessaryCodeBlockEmptyLines, footnote } = this.muya.options
|
||||
const tokens = new Lexer({ disableInline: true, footnote }).lex(markdown)
|
||||
const tokens = new Lexer({
|
||||
disableInline: true,
|
||||
footnote,
|
||||
isGitlabCompatibilityEnabled,
|
||||
superSubScript
|
||||
}).lex(markdown)
|
||||
|
||||
let token
|
||||
let block
|
||||
@ -152,7 +162,7 @@ const importRegister = ContentState => {
|
||||
|
||||
case 'multiplemath': {
|
||||
value = token.text
|
||||
block = this.createContainerBlock(token.type, value)
|
||||
block = this.createContainerBlock(token.type, value, token.mathStyle)
|
||||
this.appendChild(parentList[0], block)
|
||||
break
|
||||
}
|
||||
@ -457,8 +467,8 @@ const importRegister = ContentState => {
|
||||
focusBlock.text = focusText.substring(0, focus.offset) + CURSOR_FOCUS_DNA + focusText.substring(focus.offset)
|
||||
}
|
||||
|
||||
const listIndentation = this.listIndentation
|
||||
const markdown = new ExportMarkdown(blocks, listIndentation).generate()
|
||||
const { isGitlabCompatibilityEnabled, listIndentation } = this
|
||||
const markdown = new ExportMarkdown(blocks, listIndentation, isGitlabCompatibilityEnabled).generate()
|
||||
const cursor = markdown.split('\n').reduce((acc, line, index) => {
|
||||
const ach = line.indexOf(CURSOR_ANCHOR_DNA)
|
||||
const fch = line.indexOf(CURSOR_FOCUS_DNA)
|
||||
|
@ -140,6 +140,7 @@ export default {
|
||||
frontmatterType: state => state.preferences.frontmatterType,
|
||||
superSubScript: state => state.preferences.superSubScript,
|
||||
footnote: state => state.preferences.footnote,
|
||||
isGitlabCompatibilityEnabled: state => state.preferences.isGitlabCompatibilityEnabled,
|
||||
lineHeight: state => state.preferences.lineHeight,
|
||||
fontSize: state => state.preferences.fontSize,
|
||||
codeFontSize: state => state.preferences.codeFontSize,
|
||||
@ -284,6 +285,13 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
isGitlabCompatibilityEnabled: function (value, oldValue) {
|
||||
const { editor } = this
|
||||
if (value !== oldValue && editor) {
|
||||
editor.setOptions({ isGitlabCompatibilityEnabled: value }, true)
|
||||
}
|
||||
},
|
||||
|
||||
hideQuickInsertHint: function (value, oldValue) {
|
||||
const { editor } = this
|
||||
if (value !== oldValue && editor) {
|
||||
@ -517,6 +525,7 @@ export default {
|
||||
frontmatterType,
|
||||
superSubScript,
|
||||
footnote,
|
||||
isGitlabCompatibilityEnabled,
|
||||
hideQuickInsertHint,
|
||||
editorLineWidth,
|
||||
theme,
|
||||
@ -564,6 +573,7 @@ export default {
|
||||
frontmatterType,
|
||||
superSubScript,
|
||||
footnote,
|
||||
isGitlabCompatibilityEnabled,
|
||||
hideQuickInsertHint,
|
||||
hideLinkPopup,
|
||||
autoCheck,
|
||||
|
@ -61,6 +61,13 @@
|
||||
more="https://pandoc.org/MANUAL.html#footnotes"
|
||||
></bool>
|
||||
<separator></separator>
|
||||
<h5>Compatibility</h5>
|
||||
<bool
|
||||
description="Enable GitLab compatibility mode."
|
||||
:bool="isGitlabCompatibilityEnabled"
|
||||
:onChange="value => onSelectChange('isGitlabCompatibilityEnabled', value)"
|
||||
></bool>
|
||||
<separator></separator>
|
||||
<h5>Diagram theme</h5>
|
||||
<cus-select
|
||||
description="Sequence diagram theme"
|
||||
@ -114,6 +121,7 @@ export default {
|
||||
frontmatterType: state => state.preferences.frontmatterType,
|
||||
superSubScript: state => state.preferences.superSubScript,
|
||||
footnote: state => state.preferences.footnote,
|
||||
isGitlabCompatibilityEnabled: state => state.preferences.isGitlabCompatibilityEnabled,
|
||||
sequenceTheme: state => state.preferences.sequenceTheme
|
||||
})
|
||||
},
|
||||
|
@ -47,6 +47,7 @@ const state = {
|
||||
frontmatterType: '-',
|
||||
superSubScript: false,
|
||||
footnote: false,
|
||||
isGitlabCompatibilityEnabled: false,
|
||||
sequenceTheme: 'hand',
|
||||
|
||||
theme: 'light',
|
||||
|
@ -43,6 +43,7 @@
|
||||
"frontmatterType": "-",
|
||||
"superSubScript": false,
|
||||
"footnote": false,
|
||||
"isGitlabCompatibilityEnabled": false,
|
||||
"sequenceTheme": "hand",
|
||||
|
||||
"theme": "light",
|
||||
|
Loading…
Reference in New Issue
Block a user