add list indentation settings (#900)

This commit is contained in:
Felix Häusler 2019-04-10 16:43:21 +02:00 committed by Ran Luo
parent 2e50b62d99
commit 1c4ec7cae4
18 changed files with 347 additions and 20 deletions

View File

@ -8,4 +8,3 @@ end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2

View File

@ -1,7 +1,8 @@
{
"editor.insertSpaces": true,
"editor.tabSize": 2,
"editor.trimAutoWhitespace": true,
// Issues with unit test files
//"editor.trimAutoWhitespace": false,
"files.eol": "\n",
"files.insertFinalNewline": true,

View File

@ -10,6 +10,10 @@
- **codeFontFamily**: The code block font family name.
- **lineHeight**: The line height of the editor.
- **tabSize**: The number of spaces a tab is equal to.
- **listIndentation**: The list indentation of list items (`"dfm"`, `"tab"` or number `1-4`)
- `tab`: Indent subsequent paragraphs by one tab.
- `dfm`: Each subsequent paragraph in a list item must be indented by either 4 spaces or one tab, we are using 4 spaces (used by Bitbucket and Daring Fireball Markdown Spec).
- `number`: Dynamic indent subsequent paragraphs by the given number (1-4) plus list marker width (default).
- **autoPairBracket**: If `true` the editor automatically closes brackets.
- **autoPairMarkdownSyntax**: If `true` the editor automatically closes inline markdown like `*` or `_`.
- **autoPairQuote**: If `true` the editor automatically closes quotes (`'` and `"`).

View File

@ -48,7 +48,6 @@ class Preference {
this.validateSettings(userSetting)
} else {
userSetting = this.loadJson(userDataPath)
this.validateSettings(userSetting)
// Update outdated settings
const requiresUpdate = !hasSameKeys(defaultSettings, userSetting)
@ -65,8 +64,11 @@ class Preference {
userSetting[key] = defaultSettings[key]
}
}
this.validateSettings(userSetting)
this.writeJson(userSetting, false)
.catch(log)
} else {
this.validateSettings(userSetting)
}
}
@ -75,7 +77,6 @@ class Preference {
userSetting = defaultSettings
this.validateSettings(userSetting)
}
this.cache = userSetting
}
@ -104,7 +105,7 @@ class Preference {
writeJson (json, async = true) {
const { userDataPath } = this
return new Promise((resolve, reject) => {
const content = fs.readFileSync(userDataPath, 'utf-8')
const content = fs.readFileSync(this.staticPath, 'utf-8')
const tokens = content.split('```')
const newContent = tokens[0] +
'```json\n' +
@ -139,14 +140,35 @@ class Preference {
brokenSettings = true
settings.theme = 'light'
}
if (!settings.bulletListMarker ||
(settings.bulletListMarker && !/^(?:\+|-|\*)$/.test(settings.bulletListMarker))) {
brokenSettings = true
settings.bulletListMarker = '-'
}
if (!settings.titleBarStyle || !/^(?:native|csd|custom)$/.test(settings.titleBarStyle)) {
settings.titleBarStyle = 'csd'
}
if (!settings.tabSize || typeof settings.tabSize !== 'number') {
settings.tabSize = 4
} else if (settings.tabSize < 1) {
settings.tabSize = 1
} else if (settings.tabSize > 4) {
settings.tabSize = 4
}
if (!settings.listIndentation) {
settings.listIndentation = 1
} else if (typeof settings.listIndentation === 'number') {
if (settings.listIndentation < 1 || settings.listIndentation > 4) {
settings.listIndentation = 1
}
} else if (settings.listIndentation !== 'tab' && settings.listIndentation !== 'dfm') {
settings.listIndentation = 1
}
if (brokenSettings) {
log('Broken settings detected; fallback to default value(s).')
}

View File

@ -236,6 +236,8 @@ export const MUYA_DEFAULT_OPTION = {
bulletListMarker: '-',
orderListMarker: '.',
tabSize: 4,
// bullet/list marker width + listIndentation, tab or Daring Fireball Markdown (4 spaces) --> list indentation
listIndentation: 1,
sequenceTheme: 'hand', // hand or simple
mermaidTheme: 'forest', // dark or forest
hideQuickInsertHint: false

View File

@ -155,7 +155,8 @@ const copyCutCtrl = ContentState => {
case 'copyTable': {
const table = this.getTableBlock()
if (!table) return
const markdown = new ExportMarkdown([ table ]).generate()
const listIndentation = this.listIndentation
const markdown = new ExportMarkdown([ table ], listIndentation).generate()
event.clipboardData.setData('text/html', '')
event.clipboardData.setData('text/plain', markdown)
break

View File

@ -270,7 +270,8 @@ const paragraphCtrl = ContentState => {
const codeBlock = this.createBlock('code')
preBlock.functionType = 'fencecode'
preBlock.lang = codeBlock.lang = ''
const markdown = new ExportMarkdown(children.slice(startIndex, endIndex + 1)).generate()
const listIndentation = this.listIndentation
const markdown = new ExportMarkdown(children.slice(startIndex, endIndex + 1), listIndentation).generate()
markdown.split(LINE_BREAKS_REG).forEach(text => {
const codeLine = this.createBlock('span', text)

View File

@ -59,7 +59,8 @@ class Muya {
getMarkdown () {
const blocks = this.contentState.getBlocks()
return new ExportMarkdown(blocks).generate()
const listIndentation = this.contentState.listIndentation
return new ExportMarkdown(blocks, listIndentation).generate()
}
getHistory () {
@ -141,9 +142,21 @@ class Muya {
tabSize = 4
} else if (tabSize < 1) {
tabSize = 1
} else if (tabSize > 4) {
tabSize = 4
}
this.contentState.tabSize = tabSize
}
this.contentState.tabSize = tabSize
setListIndentation (listIndentation) {
if (typeof listIndentation === 'number') {
if (listIndentation < 1 || listIndentation > 4) {
listIndentation = 1
}
} else if (listIndentation !== 'tab' && listIndentation !== 'dfm') {
listIndentation = 1
}
this.contentState.listIndentation = listIndentation
}
updateParagraph (type) {

View File

@ -7,14 +7,29 @@
* and GitHub Flavored Markdown Spec: https://github.github.com/gfm/
* The output markdown needs to obey the standards of the two Spec.
*/
// const LINE_BREAKS = /\n/
class ExportMarkdown {
constructor (blocks) {
constructor (blocks, listIndentation = 1) {
this.blocks = blocks
this.listType = [] // 'ul' or 'ol'
// helper to translate the first tight item in a nested list
this.isLooseParentList = true
// set and validate settings
if (listIndentation === 'tab') {
this.listIndentation = '\t'
this.listIndentationCount = null
} else if (listIndentation === 'dfm') {
// static 4 spaces
this.listIndentation = ' '
this.listIndentationCount = null
} else if (typeof listIndentation === 'number') {
this.listIndentation = null
this.listIndentationCount = Math.min(Math.max(listIndentation, 1), 4)
} else {
this.listIndentation = null
this.listIndentationCount = 1
}
}
generate () {
@ -302,26 +317,45 @@ class ExportMarkdown {
normalizeListItem (block, indent) {
const result = []
const listInfo = this.listType[this.listType.length - 1]
const isUnorderedList = listInfo.type === 'ul'
let { children, bulletMarkerOrDelimiter } = block
let itemMarker
if (listInfo.type === 'ul') {
if (isUnorderedList) {
itemMarker = bulletMarkerOrDelimiter ? `${bulletMarkerOrDelimiter} ` : '- '
if (block.listItemType === 'task') {
const firstChild = children[0]
itemMarker += firstChild.checked ? '[x] ' : '[ ] '
children = children.slice(1)
}
} else {
const delimiter = bulletMarkerOrDelimiter ? bulletMarkerOrDelimiter : '.'
itemMarker = `${listInfo.listCount++}${delimiter} `
}
const newIndent = indent + ' '.repeat(itemMarker.length)
// We already added one space to the indentation
const listIndent = this.getListIndentation(itemMarker.length - 1)
const newIndent = indent + listIndent
if (isUnorderedList && block.listItemType === 'task') {
const firstChild = children[0]
itemMarker += firstChild.checked ? '[x] ' : '[ ] '
children = children.slice(1)
}
result.push(`${indent}${itemMarker}`)
result.push(this.translateBlocks2Markdown(children, newIndent).substring(newIndent.length))
return result.join('')
}
getListIndentation (listMarkerWidth) {
// listIndentation:
// tab: Indent subsequent paragraphs by one tab.
// dfm: Each subsequent paragraph in a list item must be indented by either 4 spaces or one tab (used by Bitbucket and Daring Fireball).
// number: Dynamic indent subsequent paragraphs by the given number (1-4) plus list marker width.
if (this.listIndentation) {
return this.listIndentation
} else if (this.listIndentationCount) {
return ' '.repeat(listMarkerWidth + this.listIndentationCount)
}
return ' '.repeat(listMarkerWidth)
}
}
export default ExportMarkdown

View File

@ -273,7 +273,8 @@ const importRegister = ContentState => {
const block = this.getBlock(key)
const { text } = block
block.text = text.substring(0, offset) + CURSOR_DNA + text.substring(offset)
const markdown = new ExportMarkdown(blocks).generate()
const listIndentation = this.listIndentation
const markdown = new ExportMarkdown(blocks, listIndentation).generate()
const cursor = markdown.split('\n').reduce((acc, line, index) => {
const ch = line.indexOf(CURSOR_DNA)
if (ch > -1) {

View File

@ -121,6 +121,7 @@
'autoPairQuote': state => state.preferences.autoPairQuote,
'bulletListMarker': state => state.preferences.bulletListMarker,
'tabSize': state => state.preferences.tabSize,
'listIndentation': state => state.preferences.listIndentation,
'lineHeight': state => state.preferences.lineHeight,
'fontSize': state => state.preferences.fontSize,
'lightColor': state => state.preferences.lightColor,
@ -181,6 +182,12 @@
if (value !== oldValue && editor) {
editor.setTabSize(value)
}
},
listIndentation: function (value, oldValue) {
const { editor } = this
if (value !== oldValue && editor) {
editor.setListIndentation(value)
}
}
},
created () {
@ -197,6 +204,7 @@
autoPairQuote,
bulletListMarker,
tabSize,
listIndentation,
hideQuickInsertHint
} = this
@ -218,6 +226,7 @@
autoPairQuote,
bulletListMarker,
tabSize,
listIndentation,
hideQuickInsertHint
})

View File

@ -18,6 +18,8 @@ const state = {
autoPairMarkdownSyntax: true,
autoPairQuote: true,
tabSize: 4,
// bullet/list marker width + listIndentation, tab or Daring Fireball Markdown (4 spaces) --> list indentation
listIndentation: 1,
hideQuickInsertHint: false,
titleBarStyle: 'csd',
// edit modes (they are not in preference.md, but still put them here)

View File

@ -8,6 +8,8 @@ Edit and save to update preferences. You can only change the JSON below!
- **endOfLine**: *String* `lf`, `crlf` or `default`
- **listIndentation**: `"dfm"`, `"tab"` or number (`1-4`)
- **bulletListMarker**: *String* `+`,`-` or `*`
- **textDirection**: *String* `ltr` or `rtl`
@ -34,6 +36,7 @@ Edit and save to update preferences. You can only change the JSON below!
"autoPairQuote": true,
"endOfLine": "default",
"tabSize": 4,
"listIndentation": 1,
"textDirection": "ltr",
"titleBarStyle": "csd",
"openFilesInNewWindow": true

View File

@ -0,0 +1,2 @@
[*.md]
trim_trailing_whitespace = false

View File

@ -9,3 +9,9 @@
\`\`\`
This isn't a code block without language identifier.
\`\`\`
\$ You can also escape math \$.
\$\$
This isn't a math block.
\$\$

View File

@ -165,6 +165,8 @@ Foo
- baz
```
Issue `-` is replaced by `- `:
```
- foo
-

View File

@ -1,3 +1,5 @@
# Basic Text Formatting
~~this is strike through text~~
\~\~and this not\~\~

View File

@ -0,0 +1,223 @@
import ContentState from '../../../src/muya/lib/contentState'
import EventCenter from '../../../src/muya/lib/eventHandler/event'
import ExportMarkdown from '../../../src/muya/lib/utils/exportMarkdown'
import { MUYA_DEFAULT_OPTION } from '../../../src/muya/lib/config'
const createMuyaContext = listIdentation => {
const ctx = {}
ctx.options = Object.assign({}, MUYA_DEFAULT_OPTION, { listIdentation })
ctx.eventCenter = new EventCenter()
ctx.contentState = new ContentState(ctx, ctx.options)
return ctx
}
// ----------------------------------------------------------------------------
// Muya parser (Markdown to HTML to Markdown)
//
const verifyMarkdown = (expectedMarkdown, listIdentation) => {
const markdown = `start
- foo
- foo
- foo
- foo
- foo
- foo
- foo
- foo
- foo
sep
1. foo
2. foo
1. foo
2. foo
1. foo
3. foo
3. foo
20. foo
141. foo
1. foo
`
const ctx = createMuyaContext(listIdentation)
ctx.contentState.importMarkdown(markdown)
const blocks = ctx.contentState.getBlocks()
const exportedMarkdown = new ExportMarkdown(blocks, listIdentation).generate()
expect(exportedMarkdown).to.equal(expectedMarkdown)
}
describe('Muya tab identation', () => {
it('Indent by 1 space', () => {
const md = `start
- foo
- foo
- foo
- foo
- foo
- foo
- foo
- foo
- foo
sep
1. foo
2. foo
1. foo
2. foo
1. foo
3. foo
3. foo
20. foo
141. foo
1. foo
`
verifyMarkdown(md, 1)
})
it('Indent by 2 spaces', () => {
const md = `start
- foo
- foo
- foo
- foo
- foo
- foo
- foo
- foo
- foo
sep
1. foo
2. foo
1. foo
2. foo
1. foo
3. foo
3. foo
20. foo
141. foo
1. foo
`
verifyMarkdown(md, 2)
})
it('Indent by 3 spaces', () => {
const md = `start
- foo
- foo
- foo
- foo
- foo
- foo
- foo
- foo
- foo
sep
1. foo
2. foo
1. foo
2. foo
1. foo
3. foo
3. foo
20. foo
141. foo
1. foo
`
verifyMarkdown(md, 3)
})
it('Indent by 4 spaces', () => {
const md = `start
- foo
- foo
- foo
- foo
- foo
- foo
- foo
- foo
- foo
sep
1. foo
2. foo
1. foo
2. foo
1. foo
3. foo
3. foo
20. foo
141. foo
1. foo
`
verifyMarkdown(md, 4)
})
it('Indent by one tab', () => {
const md = `start
- foo
- foo
\t- foo
\t- foo
\t\t- foo
\t\t- foo
\t\t\t- foo
\t- foo
- foo
sep
1. foo
2. foo
\t1. foo
\t2. foo
\t\t1. foo
\t3. foo
3. foo
\t20. foo
\t\t141. foo
\t\t\t1. foo
`
verifyMarkdown(md, "tab")
})
it('Indent using Daring Fireball Markdown Spec', () => {
const md = `start
- foo
- foo
- foo
- foo
- foo
- foo
- foo
- foo
- foo
sep
1. foo
2. foo
1. foo
2. foo
1. foo
3. foo
3. foo
20. foo
141. foo
1. foo
`
verifyMarkdown(md, "dfm")
})
})