Skip to content

Commit 9a704a2

Browse files
authored
Merge branch 'master' into feature/scrollbarAppearance
2 parents c2a26a8 + 857e755 commit 9a704a2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+481
-162
lines changed

.eslintrc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
"globals": {
1919
"FileReader": true,
2020
"localStorage": true,
21-
"fetch": true
21+
"fetch": true,
22+
"Image": true,
23+
"MutationObserver": true
2224
},
2325
"env": {
2426
"jest": true

browser/components/CodeEditor.js

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ import styles from '../components/CodeEditor.styl'
2020
const { ipcRenderer, remote, clipboard } = require('electron')
2121
import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily'
2222
const spellcheck = require('browser/lib/spellcheck')
23-
const buildEditorContextMenu = require('browser/lib/contextMenuBuilder')
23+
const buildEditorContextMenu = require('browser/lib/contextMenuBuilder').buildEditorContextMenu
2424
import TurndownService from 'turndown'
2525
import {languageMaps} from '../lib/CMLanguageList'
2626
import snippetManager from '../lib/SnippetManager'
2727
import {generateInEditor, tocExistsInEditor} from 'browser/lib/markdown-toc-generator'
2828
import markdownlint from 'markdownlint'
2929
import Jsonlint from 'jsonlint-mod'
3030
import { DEFAULT_CONFIG } from '../main/lib/ConfigManager'
31+
import prettier from 'prettier'
3132

3233
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
3334

@@ -106,7 +107,7 @@ export default class CodeEditor extends React.Component {
106107
const component = this
107108

108109
if (component.searchState) cm.removeOverlay(component.searchState)
109-
if (msg.length < 3) return
110+
if (msg.length < 1) return
110111

111112
cm.operation(function () {
112113
component.searchState = makeOverlay(msg, 'searching')
@@ -216,6 +217,28 @@ export default class CodeEditor extends React.Component {
216217
}
217218
return CodeMirror.Pass
218219
},
220+
[translateHotkey(hotkey.prettifyMarkdown)]: cm => {
221+
// Default / User configured prettier options
222+
const currentConfig = JSON.parse(self.props.prettierConfig)
223+
224+
// Parser type will always need to be markdown so we override the option before use
225+
currentConfig.parser = 'markdown'
226+
227+
// Get current cursor position
228+
const cursorPos = cm.getCursor()
229+
currentConfig.cursorOffset = cm.doc.indexFromPos(cursorPos)
230+
231+
// Prettify contents of editor
232+
const formattedTextDetails = prettier.formatWithCursor(cm.doc.getValue(), currentConfig)
233+
234+
const formattedText = formattedTextDetails.formatted
235+
const formattedCursorPos = formattedTextDetails.cursorOffset
236+
cm.doc.setValue(formattedText)
237+
238+
// Reset Cursor position to be at the same markdown as was before prettifying
239+
const newCursorPos = cm.doc.posFromIndex(formattedCursorPos)
240+
cm.doc.setCursor(newCursorPos)
241+
},
219242
[translateHotkey(hotkey.pasteSmartly)]: cm => {
220243
this.handlePaste(cm, true)
221244
}
@@ -251,7 +274,7 @@ export default class CodeEditor extends React.Component {
251274
value: this.props.value,
252275
linesHighlighted: this.props.linesHighlighted,
253276
lineNumbers: this.props.displayLineNumbers,
254-
lineWrapping: true,
277+
lineWrapping: this.props.lineWrapping,
255278
theme: this.props.theme,
256279
indentUnit: this.props.indentSize,
257280
tabSize: this.props.indentSize,
@@ -269,7 +292,8 @@ export default class CodeEditor extends React.Component {
269292
explode: this.props.explodingPairs,
270293
override: true
271294
},
272-
extraKeys: this.defaultKeyMap
295+
extraKeys: this.defaultKeyMap,
296+
prettierConfig: this.props.prettierConfig
273297
})
274298

275299
document.querySelector('.CodeMirror-lint-markers').style.display = enableMarkdownLint ? 'inline-block' : 'none'
@@ -550,6 +574,10 @@ export default class CodeEditor extends React.Component {
550574
this.editor.setOption('lineNumbers', this.props.displayLineNumbers)
551575
}
552576

577+
if (prevProps.lineWrapping !== this.props.lineWrapping) {
578+
this.editor.setOption('lineWrapping', this.props.lineWrapping)
579+
}
580+
553581
if (prevProps.scrollPastEnd !== this.props.scrollPastEnd) {
554582
this.editor.setOption('scrollPastEnd', this.props.scrollPastEnd)
555583
}
@@ -832,6 +860,17 @@ export default class CodeEditor extends React.Component {
832860
this.editor.setCursor(cursor)
833861
}
834862

863+
/**
864+
* Update content of one line
865+
* @param {Number} lineNumber
866+
* @param {String} content
867+
*/
868+
setLineContent (lineNumber, content) {
869+
const prevContent = this.editor.getLine(lineNumber)
870+
const prevContentLength = prevContent ? prevContent.length : 0
871+
this.editor.replaceRange(content, { line: lineNumber, ch: 0 }, { line: lineNumber, ch: prevContentLength })
872+
}
873+
835874
handleDropImage (dropEvent) {
836875
dropEvent.preventDefault()
837876
const {
@@ -1179,5 +1218,6 @@ CodeEditor.defaultProps = {
11791218
autoDetect: false,
11801219
spellCheck: false,
11811220
enableMarkdownLint: DEFAULT_CONFIG.editor.enableMarkdownLint,
1182-
customMarkdownLintConfig: DEFAULT_CONFIG.editor.customMarkdownLintConfig
1221+
customMarkdownLintConfig: DEFAULT_CONFIG.editor.customMarkdownLintConfig,
1222+
prettierConfig: DEFAULT_CONFIG.editor.prettierConfig
11831223
}

browser/components/MarkdownEditor.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,15 @@ class MarkdownEditor extends React.Component {
169169
.split('\n')
170170

171171
const targetLine = lines[lineIndex]
172+
let newLine = targetLine
172173

173174
if (targetLine.match(checkedMatch)) {
174-
lines[lineIndex] = targetLine.replace(checkReplace, '[ ]')
175+
newLine = targetLine.replace(checkReplace, '[ ]')
175176
}
176177
if (targetLine.match(uncheckedMatch)) {
177-
lines[lineIndex] = targetLine.replace(uncheckReplace, '[x]')
178+
newLine = targetLine.replace(uncheckReplace, '[x]')
178179
}
179-
this.refs.code.setValue(lines.join('\n'))
180+
this.refs.code.setLineContent(lineIndex, newLine)
180181
}
181182
}
182183

@@ -304,6 +305,7 @@ class MarkdownEditor extends React.Component {
304305
enableRulers={config.editor.enableRulers}
305306
rulers={config.editor.rulers}
306307
displayLineNumbers={config.editor.displayLineNumbers}
308+
lineWrapping
307309
matchingPairs={config.editor.matchingPairs}
308310
matchingTriples={config.editor.matchingTriples}
309311
explodingPairs={config.editor.explodingPairs}
@@ -321,6 +323,7 @@ class MarkdownEditor extends React.Component {
321323
switchPreview={config.editor.switchPreview}
322324
enableMarkdownLint={config.editor.enableMarkdownLint}
323325
customMarkdownLintConfig={config.editor.customMarkdownLintConfig}
326+
prettierConfig={config.editor.prettierConfig}
324327
/>
325328
<MarkdownPreview styleName={this.state.status === 'PREVIEW'
326329
? 'preview'
@@ -340,6 +343,7 @@ class MarkdownEditor extends React.Component {
340343
smartArrows={config.preview.smartArrows}
341344
breaks={config.preview.breaks}
342345
sanitize={config.preview.sanitize}
346+
mermaidHTMLLabel={config.preview.mermaidHTMLLabel}
343347
ref='preview'
344348
onContextMenu={(e) => this.handleContextMenu(e)}
345349
onDoubleClick={(e) => this.handleDoubleClick(e)}

browser/components/MarkdownPreview.js

Lines changed: 30 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,20 @@ import mdurl from 'mdurl'
1818
import exportNote from 'browser/main/lib/dataApi/exportNote'
1919
import { escapeHtmlCharacters } from 'browser/lib/utils'
2020
import yaml from 'js-yaml'
21-
import context from 'browser/lib/context'
22-
import i18n from 'browser/lib/i18n'
23-
import fs from 'fs'
2421
import { render } from 'react-dom'
2522
import Carousel from 'react-image-carousel'
2623
import ConfigManager from '../main/lib/ConfigManager'
2724

2825
const { remote, shell } = require('electron')
2926
const attachmentManagement = require('../main/lib/dataApi/attachmentManagement')
27+
const buildMarkdownPreviewContextMenu = require('browser/lib/contextMenuBuilder').buildMarkdownPreviewContextMenu
3028

3129
const { app } = remote
3230
const path = require('path')
3331
const fileUrl = require('file-url')
3432

3533
const dialog = remote.dialog
3634

37-
const uri2path = require('file-uri-to-path')
38-
3935
const markdownStyle = require('!!css!stylus?sourceMap!./markdown.styl')[0][1]
4036
const appPath = fileUrl(
4137
process.env.NODE_ENV === 'production' ? app.getAppPath() : path.resolve()
@@ -45,7 +41,6 @@ const CSS_FILES = [
4541
`${appPath}/node_modules/codemirror/lib/codemirror.css`,
4642
`${appPath}/node_modules/react-image-carousel/lib/css/main.min.css`
4743
]
48-
const win = global.process.platform === 'win32'
4944

5045
/**
5146
* @param {Object} opts
@@ -267,30 +262,12 @@ export default class MarkdownPreview extends React.Component {
267262
}
268263

269264
handleContextMenu (event) {
270-
// If a contextMenu handler was passed to us, use it instead of the self-defined one -> return
271-
if (_.isFunction(this.props.onContextMenu)) {
265+
const menu = buildMarkdownPreviewContextMenu(this, event)
266+
const switchPreview = ConfigManager.get().editor.switchPreview
267+
if (menu != null && switchPreview !== 'RIGHTCLICK') {
268+
menu.popup(remote.getCurrentWindow())
269+
} else if (_.isFunction(this.props.onContextMenu)) {
272270
this.props.onContextMenu(event)
273-
return
274-
}
275-
// No contextMenu was passed to us -> execute our own link-opener
276-
if (event.target.tagName.toLowerCase() === 'a' && event.target.getAttribute('href')) {
277-
const href = event.target.href
278-
const isLocalFile = href.startsWith('file:')
279-
if (isLocalFile) {
280-
const absPath = uri2path(href)
281-
try {
282-
if (fs.lstatSync(absPath).isFile()) {
283-
context.popup([
284-
{
285-
label: i18n.__('Show in explorer'),
286-
click: (e) => shell.showItemInFolder(absPath)
287-
}
288-
])
289-
}
290-
} catch (e) {
291-
console.log('Error while evaluating if the file is locally available', e)
292-
}
293-
}
294271
}
295272
}
296273

@@ -367,7 +344,7 @@ export default class MarkdownPreview extends React.Component {
367344
body,
368345
this.props.storagePath
369346
)
370-
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
347+
const files = [this.getCodeThemeLink(codeBlockTheme), ...CSS_FILES]
371348
files.forEach(file => {
372349
if (global.process.platform === 'win32') {
373350
file = file.replace('file:///', '')
@@ -403,7 +380,7 @@ export default class MarkdownPreview extends React.Component {
403380

404381
handleSaveAsPdf () {
405382
this.exportAsDocument('pdf', (noteContent, exportTasks, targetDir) => {
406-
const printout = new remote.BrowserWindow({show: false, webPreferences: {webSecurity: false}})
383+
const printout = new remote.BrowserWindow({show: false, webPreferences: {webSecurity: false, javascript: false}})
407384
printout.loadURL('data:text/html;charset=UTF-8,' + this.htmlContentFormatter(noteContent, exportTasks, targetDir))
408385
return new Promise((resolve, reject) => {
409386
printout.webContents.on('did-finish-load', () => {
@@ -598,16 +575,19 @@ export default class MarkdownPreview extends React.Component {
598575
}
599576

600577
componentDidUpdate (prevProps) {
601-
if (prevProps.value !== this.props.value) this.rewriteIframe()
578+
// actual rewriteIframe function should be called only once
579+
let needsRewriteIframe = false
580+
if (prevProps.value !== this.props.value) needsRewriteIframe = true
602581
if (
603582
prevProps.smartQuotes !== this.props.smartQuotes ||
604583
prevProps.sanitize !== this.props.sanitize ||
584+
prevProps.mermaidHTMLLabel !== this.props.mermaidHTMLLabel ||
605585
prevProps.smartArrows !== this.props.smartArrows ||
606586
prevProps.breaks !== this.props.breaks ||
607587
prevProps.lineThroughCheckbox !== this.props.lineThroughCheckbox
608588
) {
609589
this.initMarkdown()
610-
this.rewriteIframe()
590+
needsRewriteIframe = true
611591
}
612592
if (
613593
prevProps.fontFamily !== this.props.fontFamily ||
@@ -622,8 +602,17 @@ export default class MarkdownPreview extends React.Component {
622602
prevProps.customCSS !== this.props.customCSS
623603
) {
624604
this.applyStyle()
605+
needsRewriteIframe = true
606+
}
607+
608+
if (needsRewriteIframe) {
625609
this.rewriteIframe()
626610
}
611+
612+
// Should scroll to top after selecting another note
613+
if (prevProps.noteKey !== this.props.noteKey) {
614+
this.getWindow().scrollTo(0, 0)
615+
}
627616
}
628617

629618
getStyleParams () {
@@ -679,8 +668,7 @@ export default class MarkdownPreview extends React.Component {
679668

680669
this.getWindow().document.getElementById(
681670
'codeTheme'
682-
).href = this.GetCodeThemeLink(codeBlockTheme)
683-
671+
).href = this.getCodeThemeLink(codeBlockTheme)
684672
this.getWindow().document.getElementById('style').innerHTML = buildStyle({
685673
fontFamily,
686674
fontSize,
@@ -695,11 +683,11 @@ export default class MarkdownPreview extends React.Component {
695683
this.getWindow().document.documentElement.style.overflowY = 'hidden'
696684
}
697685

698-
GetCodeThemeLink (name) {
686+
getCodeThemeLink (name) {
699687
const theme = consts.THEMES.find(theme => theme.name === name)
700688

701-
return theme
702-
? (win ? theme.path : `${appPath}/${theme.path}`)
689+
return theme != null
690+
? theme.path
703691
: `${appPath}/node_modules/codemirror/theme/elegant.css`
704692
}
705693

@@ -726,7 +714,8 @@ export default class MarkdownPreview extends React.Component {
726714
showCopyNotification,
727715
storagePath,
728716
noteKey,
729-
sanitize
717+
sanitize,
718+
mermaidHTMLLabel
730719
} = this.props
731720
let { value, codeBlockTheme } = this.props
732721

@@ -858,6 +847,7 @@ export default class MarkdownPreview extends React.Component {
858847
canvas.height = height.value + 'vh'
859848
}
860849

850+
// eslint-disable-next-line no-unused-vars
861851
const chart = new Chart(canvas, chartConfig)
862852
} catch (e) {
863853
el.className = 'chart-error'
@@ -868,7 +858,7 @@ export default class MarkdownPreview extends React.Component {
868858
_.forEach(
869859
this.refs.root.contentWindow.document.querySelectorAll('.mermaid'),
870860
el => {
871-
mermaidRender(el, htmlTextHelper.decodeEntities(el.innerHTML), theme)
861+
mermaidRender(el, htmlTextHelper.decodeEntities(el.innerHTML), theme, mermaidHTMLLabel)
872862
}
873863
)
874864

@@ -996,8 +986,6 @@ export default class MarkdownPreview extends React.Component {
996986
overlay.appendChild(zoomImg)
997987
document.body.appendChild(overlay)
998988
}
999-
1000-
this.getWindow().scrollTo(0, 0)
1001989
}
1002990

1003991
focus () {

0 commit comments

Comments
 (0)