Allow to set editor line width and window zoom (#1092)

* Allow to set editor line width and window zoom

* Improve input text changed event

* Make textbox smaller
This commit is contained in:
Felix Häusler 2019-06-16 15:39:02 +02:00 committed by GitHub
parent 47da3bd193
commit d7defe97dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 306 additions and 47 deletions

View File

@ -12,7 +12,8 @@ export const editorWinOptions = {
useContentSize: true,
show: true,
frame: false,
titleBarStyle: 'hiddenInset'
titleBarStyle: 'hiddenInset',
zoomFactor: 1.0
}
export const defaultPreferenceWinOptions = {
@ -31,7 +32,8 @@ export const defaultPreferenceWinOptions = {
show: false,
frame: false,
thickFrame: !isOsx,
titleBarStyle: 'hiddenInset'
titleBarStyle: 'hiddenInset',
zoomFactor: 1.0
}
export const PANDOC_EXTENSIONS = [

View File

@ -1,6 +1,6 @@
import './globalSetting'
import path from 'path'
import { app } from 'electron'
import { app, dialog } from 'electron'
import cli from './cli'
import setupExceptionHandler, { initExceptionLogger } from './exceptionHandler'
import log from 'electron-log'
@ -14,7 +14,7 @@ const initializeLogger = appEnvironment => {
log.transports.rendererConsole = null
log.transports.file.file = path.join(appEnvironment.paths.logPath, 'main.log')
log.transports.file.level = getLogLevel()
log.transports.file.sync = false
log.transports.file.sync = true
log.transports.file.init()
initExceptionLogger()
}
@ -48,7 +48,30 @@ if (!process.mas && process.env.NODE_ENV !== 'development') {
// Mark Text environment is configured successfully. You can now access paths, use the logger etc.
// Create other instances that need access to the modules from above.
const accessor = new Accessor(appEnvironment)
let accessor = null
try {
accessor = new Accessor(appEnvironment)
} catch (err) {
// Catch errors that may come from invalid configuration files like settings.
const msgHint = err.message.includes('Config schema violation')
? 'This seems to be an issue with your configuration file(s). ' : ''
log.error(`Loading Mark Text failed during initialization! ${msgHint}`)
log.error(err)
const EXIT_ON_ERROR = !!process.env.MARKTEXT_EXIT_ON_ERROR
const SHOW_ERROR_DIALOG = !process.env.MARKTEXT_ERROR_INTERACTION
if (!EXIT_ON_ERROR && SHOW_ERROR_DIALOG) {
dialog.showErrorBox(
'There was an error during loading',
`${msgHint}${err.message}\n\n${err.stack}`
)
}
process.exit(1)
}
// Use synchronous only to report errors in early stage of startup.
log.transports.file.sync = false
// -----------------------------------------------
// Be careful when changing code before this line!

View File

@ -5,3 +5,17 @@ export const toggleAlwaysOnTop = win => {
ipcMain.emit('window-toggle-always-on-top', win)
}
}
export const zoomIn = win => {
const { webContents } = win
const zoom = webContents.getZoomFactor()
// WORKAROUND: Electron#16018
webContents.send('mt::window-zoom', Math.min(2.0, zoom + 0.125))
}
export const zoomOut = win => {
const { webContents } = win
const zoom = webContents.getZoomFactor()
// WORKAROUND: Electron#16018
webContents.send('mt::window-zoom', Math.max(1.0, zoom - 0.125))
}

View File

@ -1,4 +1,4 @@
import { toggleAlwaysOnTop } from '../actions/window'
import { toggleAlwaysOnTop, zoomIn, zoomOut } from '../actions/window'
import { isOsx } from '../../config'
export default function (keybindings) {
@ -18,6 +18,18 @@ export default function (keybindings) {
}
}, {
type: 'separator'
}, {
label: 'Zoom In',
click (menuItem, browserWindow) {
zoomIn(browserWindow)
}
}, {
label: 'Zoom Out',
click (menuItem, browserWindow) {
zoomOut(browserWindow)
}
}, {
type: 'separator'
}, {
label: 'Toggle Full Screen',
accelerator: keybindings.getAccelerator('viewToggleFullScreen'),

View File

@ -27,6 +27,9 @@ class Preference extends EventEmitter {
/**
* @param {AppPaths} userDataPath The path instance.
*
* NOTE: This throws an exception when validation fails.
*
*/
constructor (paths) {
super()

View File

@ -4,7 +4,7 @@
"type": "boolean"
},
"autoSaveDelay": {
"description": "General--How long do you want to save your document(ms)?",
"description": "General--The time in ms after a change that the file is saved.",
"type": "number",
"minimum": 1000
},
@ -23,6 +23,11 @@
"description": "General--Open folder via menu in a new window.",
"type": "boolean"
},
"hideScrollbar": {
"description": "General--Whether to hide scrollbars.",
"type": "boolean",
"default": false
},
"aidou": {
"description": "General--Enable aidou",
"type": "boolean"
@ -76,6 +81,11 @@
"minimum": 1.2,
"default": 1.6
},
"editorLineWidth": {
"description": "Editor--Defines the maximum editor area width. An empty string or suffixes of ch (characters), px (pixels) or % (percentage) are allowed.",
"type": "string",
"pattern": "^(?:$|[0-9]+(?:ch|px|%)$)"
},
"codeFontSize": {
"description": "Editor--Font size in code Block, the range is 12 ~ 18",
"type": "number",
@ -92,6 +102,7 @@
"monospace"
]
},
"autoPairBracket": {
"description": "Editor--Automatically brackets when editing",
"type": "boolean"

View File

@ -72,7 +72,13 @@ class BaseWindow extends EventEmitter {
// NOTE: Only send absolutely necessary values. Full settings are delay loaded.
const { type } = this
const { debug, paths } = env
const { codeFontFamily, codeFontSize, theme, titleBarStyle } = userPreference.getAll()
const {
codeFontFamily,
codeFontSize,
hideScrollbar,
theme,
titleBarStyle
} = userPreference.getAll()
const baseUrl = process.env.NODE_ENV === 'development'
? `http://localhost:9091`
@ -87,6 +93,7 @@ class BaseWindow extends EventEmitter {
// Settings
url.searchParams.set('cff', codeFontFamily)
url.searchParams.set('cfs', codeFontSize)
url.searchParams.set('hsb', hideScrollbar ? '1' : '0')
url.searchParams.set('theme', theme)
url.searchParams.set('tbs', titleBarStyle)

View File

@ -1,5 +1,6 @@
import path from 'path'
import { BrowserWindow, ipcMain } from 'electron'
import electronLocalshortcut from '@hfelix/electron-localshortcut'
import BaseWindow, { WindowLifecycle, WindowType } from './base'
import { centerWindowOptions } from './utils'
import { TITLE_BAR_HEIGHT, defaultPreferenceWinOptions, isLinux, isOsx } from '../config'
@ -20,7 +21,7 @@ class SettingWindow extends BaseWindow {
* @param {*} [options] BrowserWindow options.
*/
createWindow (options = {}) {
const { menu: appMenu, env, preferences } = this._accessor
const { menu: appMenu, env, keybindings, preferences } = this._accessor
const winOptions = Object.assign({}, defaultPreferenceWinOptions, options)
centerWindowOptions(winOptions)
if (isLinux) {
@ -78,6 +79,13 @@ class SettingWindow extends BaseWindow {
win.loadURL(this._buildUrlString(this.id, env, preferences))
win.setSheetOffset(TITLE_BAR_HEIGHT)
electronLocalshortcut.register(
win,
keybindings.getAccelerator('viewDevToggleDeveloperTools'),
() => {
win.webContents.toggleDevTools()
}
)
return win
}
}

View File

@ -311,6 +311,7 @@ class Muya {
if (needRender) {
this.contentState.render()
}
// Set quick insert hint visibility
const hideQuickInsertHint = options['hideQuickInsertHint']
if (typeof hideQuickInsertHint !== 'undefined') {
@ -321,6 +322,7 @@ class Muya {
this.container.classList.add('ag-show-quick-insert-hint')
}
}
if (options.bulletListMarker) {
this.contentState.turndownConfig.bulletListMarker = options.bulletListMarker
}

View File

@ -126,6 +126,7 @@ kbd {
#ag-editor-id {
max-width: var(--editorAreaWidth);
min-width: 400px;
min-height: 100%;
margin: 0 auto;
padding: 20px 50px 100px 50px;

View File

@ -21,6 +21,7 @@ const parseUrlArgs = () => {
const codeFontFamily = params.get('cff')
const codeFontSize = params.get('cfs')
const debug = params.get('debug') === '1'
const hideScrollbar = params.get('hsb') === '1'
const theme = params.get('theme')
const titleBarStyle = params.get('tbs')
const userDataPath = params.get('udp')
@ -34,6 +35,7 @@ const parseUrlArgs = () => {
initialState: {
codeFontFamily,
codeFontSize,
hideScrollbar,
theme,
titleBarStyle
}

View File

@ -9,6 +9,7 @@
<div
ref="editor"
class="editor-component"
:style="[ getEditorLineWidth ]"
></div>
<div
class="image-viewer"
@ -133,13 +134,13 @@
'fontSize': state => state.preferences.fontSize,
'codeFontSize': state => state.preferences.codeFontSize,
'codeFontFamily': state => state.preferences.codeFontFamily,
'lightColor': state => state.preferences.lightColor,
'darkColor': state => state.preferences.darkColor,
'editorFontFamily': state => state.preferences.editorFontFamily,
'hideQuickInsertHint': state => state.preferences.hideQuickInsertHint,
'editorLineWidth': state => state.preferences.editorLineWidth,
'imageInsertAction': state => state.preferences.imageInsertAction,
'imageFolderPath': state => state.preferences.imageFolderPath,
'theme': state => state.preferences.theme,
'hideScrollbar': state => state.preferences.hideScrollbar,
'currentFile': state => state.editor.currentFile,
@ -147,7 +148,17 @@
'typewriter': state => state.preferences.typewriter,
'focus': state => state.preferences.focus,
'sourceCode': state => state.preferences.sourceCode
})
}),
getEditorLineWidth () {
const { editorLineWidth } = this
if (!editorLineWidth || !/^[0-9]+(?:ch|px|%)$/.test(editorLineWidth)) {
return {}
}
// Overwrite the theme value and add 100px for padding.
return { '--editorAreaWidth': `calc(100px + ${editorLineWidth})` }
}
},
data () {
this.defaultFontFamily = DEFAULT_EDITOR_FONT_FAMILY
@ -228,6 +239,11 @@
editor.setOptions({ hideQuickInsertHint: value })
}
},
editorLineWidth: function (value, oldValue) {
if (value !== oldValue) {
// TODO: Ask vue to reload 'getEditorLineWidth'
}
},
autoPairBracket: function (value, oldValue) {
const { editor } = this
if (value !== oldValue && editor) {
@ -262,7 +278,8 @@
if (value !== oldValue) {
addCommonStyle({
codeFontSize: value,
codeFontFamily: this.codeFontFamily
codeFontFamily: this.codeFontFamily,
hideScrollbar: this.hideScrollbar
})
}
},
@ -270,7 +287,17 @@
if (value !== oldValue) {
addCommonStyle({
codeFontSize: this.codeFontSize,
codeFontFamily: value
codeFontFamily: value,
hideScrollbar: this.hideScrollbar
})
}
},
hideScrollbar: function (value, oldValue) {
if (value !== oldValue) {
addCommonStyle({
codeFontSize: this.codeFontSize,
codeFontFamily: this.codeFontFamily,
hideScrollbar: value
})
}
},

View File

@ -10,6 +10,7 @@ export const DEFAULT_CODE_FONT_FAMILY = '"DejaVu Sans Mono", "Source Code Pro",
export const DEFAULT_STYLE = {
codeFontFamily: DEFAULT_CODE_FONT_FAMILY,
codeFontSize: '14px',
hideScrollbar: false,
theme: 'light'
}

View File

@ -0,0 +1,106 @@
<template>
<section class="pref-text-box-item" :class="{'ag-underdevelop': disable}">
<div class="description">
<span>{{description}}</span>
<i class="el-icon-info" v-if="more"
@click="handleMoreClick"
></i>
</div>
<el-input
class="input"
:class="{error: invalidInput}"
:placeholder="defaultValue"
v-model="inputText"
@input="handleInput"
style="width: 240px"
size="small"
clearable>
</el-input>
</section>
</template>
<script>
import { shell } from 'electron'
export default {
data () {
this.inputTimer = null
return {
inputText: this.input,
invalidInput: false
}
},
props: {
description: String,
input: String,
onChange: Function,
more: String,
disable: {
type: Boolean,
default: false
},
defaultValue: {
type: String,
default: ''
},
regexValidator: {
type: RegExp,
default: /(.*?)/
}
},
watch: {
input: function (value, oldValue) {
if (value !== oldValue) {
this.inputText = value
}
}
},
methods: {
handleMoreClick () {
if (typeof this.more === 'string') {
shell.openExternal(this.more)
}
},
handleInput (value) {
const result = this.regexValidator.test(value)
this.invalidInput = !result
if (result) {
// Only clear timer when input is valid, otherwise write the last value.
if (this.inputTimer) {
clearTimeout(this.inputTimer)
}
// Setting delay a little bit higher to prevent continuously file writes when typing.
this.inputTimer = setTimeout(() => {
this.inputTimer = null
this.onChange(value)
}, 800)
}
}
}
}
</script>
<style>
.pref-text-box-item {
font-size: 14px;
user-select: none;
margin: 20px 0;
color: var(--editorColor);
}
.pref-text-box-item .description {
margin-bottom: 10px;
& i {
cursor: pointer;
opacity: .7;
color: var(--iconColor);
}
& i:hover {
color: var(--themeColor);
}
}
.pref-text-box-item .el-input.error input {
color: #f56c6c;
}
</style>

View File

@ -2,7 +2,7 @@
<div class="pref-editor">
<h4>Editor</h4>
<range
description="Font size in editor"
description="The font size of editor text"
:value="fontSize"
:min="12"
:max="32"
@ -11,13 +11,13 @@
:onChange="value => onSelectChange('fontSize', value)"
></range>
<cur-select
description="Font used in editor"
description="The used font in the editor."
:value="editorFontFamily"
:options="editorFontFamilyOptions"
:onChange="value => onSelectChange('editorFontFamily', value)"
></cur-select>
<range
description="Line height in editor"
description="Line height of editor lines."
:value="lineHeight"
:min="1.2"
:max="2.0"
@ -26,7 +26,7 @@
></range>
<separator></separator>
<bool
description="Automatically brackets when editing"
description="Automatically brackets when editing."
:bool="autoPairBracket"
:onChange="value => onSelectChange('autoPairBracket', value)"
></bool>
@ -42,20 +42,20 @@
></bool>
<separator></separator>
<cur-select
description="The default end of line character, if you select default, which will be selected according to your system intelligence"
description="The default end of line character, if you select default, which will be selected according to your system intelligence."
:value="endOfLine"
:options="endOfLineOptions"
:onChange="value => onSelectChange('endOfLine', value)"
></cur-select>
<cur-select
description="The writing text direction"
description="The writing text direction."
:value="textDirection"
:options="textDirectionOptions"
:onChange="value => onSelectChange('textDirection', value)"
></cur-select>
<separator></separator>
<range
description="Code block font size in editor"
description="The code block font size in editor."
:value="codeFontSize"
:min="12"
:max="28"
@ -64,17 +64,25 @@
:onChange="value => onSelectChange('codeFontSize', value)"
></range>
<cur-select
description="Font used in code block"
description="The used code block font in the editor."
:value="codeFontFamily"
:options="codeFontFamilyOptions"
:onChange="value => onSelectChange('codeFontFamily', value)"
></cur-select>
<separator></separator>
<bool
description="Hide hint for quickly creating paragraphs"
:bool="hideQuickInsertHint"
description="Hide hint for quickly creating paragraphs."
:input="hideQuickInsertHint"
:onChange="value => onSelectChange('hideQuickInsertHint', value)"
></bool>
<separator></separator>
<text-box
description="Defines the maximum editor area width. An empty string or suffixes of ch (characters), px (pixels) or % (percentage) are allowed."
:input="editorLineWidth"
:regexValidator="/^(?:$|[0-9]+(?:ch|px|%)$)/"
defaultValue="The default value from the current theme"
:onChange="value => onSelectChange('editorLineWidth', value)"
></text-box>
</div>
</template>
@ -84,6 +92,7 @@ import Range from '../common/range'
import CurSelect from '../common/select'
import Bool from '../common/bool'
import Separator from '../common/separator'
import TextBox from '../common/textBox'
import {
editorFontFamilyOptions,
endOfLineOptions,
@ -96,7 +105,8 @@ export default {
Range,
CurSelect,
Bool,
Separator
Separator,
TextBox
},
data () {
this.editorFontFamilyOptions = editorFontFamilyOptions
@ -117,7 +127,8 @@ export default {
textDirection: state => state.preferences.textDirection,
codeFontSize: state => state.preferences.codeFontSize,
codeFontFamily: state => state.preferences.codeFontFamily,
hideQuickInsertHint: state => state.preferences.hideQuickInsertHint
hideQuickInsertHint: state => state.preferences.hideQuickInsertHint,
editorLineWidth: state => state.preferences.editorLineWidth
})
},
methods: {

View File

@ -2,12 +2,12 @@
<div class="pref-general">
<h4>General</h4>
<bool
description="Automatically save the content being edited"
description="Automatically save the content being edited."
:bool="autoSave"
:onChange="value => onSelectChange('autoSave', value)"
></bool>
<range
description="How long do you want to save your document?"
description="The time in ms after a change that the file is saved."
:value="autoSaveDelay"
:min="1000"
:max="10000"
@ -17,39 +17,49 @@
></range>
<cur-select
v-if="!isOsx"
description="The title bar style, frameless or not. (You need to restart Mark Text to enable it)"
description="The title bar style, frameless or not. You need to restart Mark Text to enable it."
:value="titleBarStyle"
:options="titleBarStyleOptions"
:onChange="value => onSelectChange('titleBarStyle', value)"
></cur-select>
<separator></separator>
<bool
description="Open file in new window"
description="Open files in new window."
:bool="openFilesInNewWindow"
:onChange="value => onSelectChange('openFilesInNewWindow', value)"
></bool>
<bool
description="Enable Aidou"
description="Open folder via menu in a new window."
:bool="openFolderInNewWindow"
:onChange="value => onSelectChange('openFolderInNewWindow', value)"
></bool>
<bool
description="Whether to hide scrollbars."
:bool="hideScrollbar"
:onChange="value => onSelectChange('hideScrollbar', value)"
></bool>
<bool
description="Enable Aidou."
:bool="aidou"
:onChange="value => onSelectChange('aidou', value)"
></bool>
<separator></separator>
<cur-select
description="Sort files in opened folder by created time modified time and title"
description="Sort files in opened folder by created time modified time and title."
:value="fileSortBy"
:options="fileSortByOptions"
:onChange="value => onSelectChange('fileSortBy', value)"
:disable="true"
></cur-select>
<section class="startup-action-ctrl">
<div>The action after Mark Text startup, open the last edited content, open the specified folder or blank page</div>
<div>The action after Mark Text startup: open the last edited content, open the specified folder or blank page.</div>
<el-radio class="ag-underdevelop" v-model="startUpAction" label="lastState">Open the last window state</el-radio>
<el-radio v-model="startUpAction" label="folder">Open a default directory</el-radio>
<el-button size="small" @click="selectDefaultDirectoryToOpen">Select Folder</el-button>
<el-radio v-model="startUpAction" label="blank">Open blank page</el-radio>
</section>
<cur-select
description="The language Mark Text use"
description="The language Mark Text use."
:value="language"
:options="languageOptions"
:onChange="value => onSelectChange('language', value)"
@ -92,6 +102,8 @@ export default {
autoSaveDelay: state => state.preferences.autoSaveDelay,
titleBarStyle: state => state.preferences.titleBarStyle,
openFilesInNewWindow: state => state.preferences.openFilesInNewWindow,
openFolderInNewWindow: state => state.preferences.openFolderInNewWindow,
hideScrollbar: state => state.preferences.hideScrollbar,
aidou: state => state.preferences.aidou,
fileSortBy: state => state.preferences.fileSortBy,
startUpAction: state => state.preferences.startUpAction,

View File

@ -3,7 +3,7 @@
<h4>Image</h4>
<section class="image-ctrl">
<div>The default behavior after insert image from local folder.
<el-tooltip class='item' effect='dark' content='Mark Text can not get image path from paste event in Linux system.' placement='top-start'>
<el-tooltip class='item' effect='dark' content='Mark Text can not get image path from paste event on Linux.' placement='top-start'>
<i class="el-icon-info"></i>
</el-tooltip>
</div>

View File

@ -2,20 +2,20 @@
<div class="pref-markdown">
<h4>markdown</h4>
<bool
description="Preferred loose list item"
description="Preferred loose list item."
:bool="preferLooseListItem"
:onChange="value => onSelectChange('preferLooseListItem', value)"
more="https://spec.commonmark.org/0.29/#loose"
></bool>
<cus-select
description="The preferred marker used in bullet list"
description="The preferred marker used in bullet list."
:value="bulletListMarker"
:options="bulletListMarkerOptions"
:onChange="value => onSelectChange('bulletListMarker', value)"
more="https://spec.commonmark.org/0.29/#bullet-list-marker"
></cus-select>
<cus-select
description="The preferred dilimiter used in order list"
description="The preferred dilimiter used in order list."
:value="orderListDelimiter"
:options="orderListDelimiterOptions"
:onChange="value => onSelectChange('orderListDelimiter', value)"
@ -29,13 +29,13 @@
:disable="true"
></cus-select>
<cus-select
description="The number of spaces a tab is equal to"
description="The number of spaces a tab is equal to."
:value="tabSize"
:options="tabSizeOptions"
:onChange="value => onSelectChange('tabSize', value)"
></cus-select>
<cus-select
description="The list indentation of sub list items or paragraphs"
description="The list indentation of sub list items or paragraphs."
:value="listIndentation"
:options="listIndentationOptions"
:onChange="value => onSelectChange('listIndentation', value)"

View File

@ -1,4 +1,4 @@
import { clipboard, ipcRenderer, shell } from 'electron'
import { clipboard, ipcRenderer, shell, webFrame } from 'electron'
import path from 'path'
import equal from 'deep-equal'
import { isSamePathSync } from 'common/filesystem/paths'
@ -873,6 +873,12 @@ const actions = {
ASK_FOR_IMAGE_PATH ({ commit }) {
return ipcRenderer.sendSync('mt::ask-for-image-path')
},
LISTEN_WINDOW_ZOOM () {
ipcRenderer.on('mt::window-zoom', (e, zoomFactor) => {
webFrame.setZoomFactor(zoomFactor)
})
}
}

View File

@ -7,6 +7,7 @@ const state = {
titleBarStyle: 'custom',
openFilesInNewWindow: false,
openFolderInNewWindow: false,
hideScrollbar: false,
aidou: true,
fileSortBy: 'created',
startUpAction: 'lastState',
@ -18,6 +19,8 @@ const state = {
lineHeight: 1.6,
codeFontSize: 14,
codeFontFamily: 'DejaVu Sans Mono',
editorLineWidth: "",
autoPairBracket: true,
autoPairMarkdownSyntax: true,
autoPairQuote: true,

View File

@ -103,8 +103,8 @@ export const addThemeStyle = theme => {
}
}
export const addCommonStyle = style => {
const { codeFontFamily, codeFontSize } = style
export const addCommonStyle = options => {
const { codeFontFamily, codeFontSize, hideScrollbar } = options
let sheet = document.querySelector(`#${COMMON_STYLE_ID}`)
if (!sheet) {
sheet = document.createElement('style')
@ -112,7 +112,12 @@ export const addCommonStyle = style => {
document.head.appendChild(sheet)
}
sheet.innerHTML = `
let scrollbarStyle = ''
if (hideScrollbar) {
scrollbarStyle = '::-webkit-scrollbar {display: none;}'
}
sheet.innerHTML = `${scrollbarStyle}
span code,
td code,
th code,
@ -146,8 +151,8 @@ export const addElementStyle = () => {
}
// Append common sheet and theme at the end of head - order is important.
export const addStyles = style => {
const { theme } = style
export const addStyles = options => {
const { theme } = options
addThemeStyle(theme)
addCommonStyle(style)
addCommonStyle(options)
}

View File

@ -4,6 +4,7 @@
"titleBarStyle": "custom",
"openFilesInNewWindow": false,
"openFolderInNewWindow": false,
"hideScrollbar": false,
"aidou": true,
"fileSortBy": "created",
"startUpAction": "lastState",
@ -15,6 +16,8 @@
"lineHeight": 1.6,
"codeFontSize": 14,
"codeFontFamily": "DejaVu Sans Mono",
"editorLineWidth": "",
"autoPairBracket": true,
"autoPairMarkdownSyntax": true,
"autoPairQuote": true,