1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Use ACE for Markdown syntax highlighting in editor

This commit is contained in:
Laurent Cozic 2017-11-09 22:44:10 +00:00
parent 0806530fd9
commit d4a0f7791a
4 changed files with 122 additions and 8 deletions

View File

@ -31,21 +31,21 @@ class MainScreenComponent extends React.Component {
const rowHeight = style.height - theme.headerHeight;
const sideBarStyle = {
width: layoutUtils.size(style.width * .2, 100, 300),
width: Math.floor(layoutUtils.size(style.width * .2, 100, 300)),
height: rowHeight,
display: 'inline-block',
verticalAlign: 'top',
};
const noteListStyle = {
width: layoutUtils.size(style.width * .2, 100, 300),
width: Math.floor(layoutUtils.size(style.width * .2, 100, 300)),
height: rowHeight,
display: 'inline-block',
verticalAlign: 'top',
};
const noteTextStyle = {
width: layoutUtils.size(style.width - sideBarStyle.width - noteListStyle.width, 0),
width: Math.floor(layoutUtils.size(style.width - sideBarStyle.width - noteListStyle.width, 0)),
height: rowHeight,
display: 'inline-block',
verticalAlign: 'top',

View File

@ -7,6 +7,9 @@ const MdToHtml = require('lib/MdToHtml');
const shared = require('lib/components/shared/note-screen-shared.js');
const { bridge } = require('electron').remote.require('./bridge');
const { themeStyle } = require('../theme.js');
const AceEditor = require('react-ace').default;
require('brace/mode/markdown');
require('brace/theme/chrome');
class NoteTextComponent extends React.Component {
@ -23,6 +26,7 @@ class NoteTextComponent extends React.Component {
isLoading: true,
webviewReady: false,
scrollHeight: null,
editorScrollTop: 0,
};
this.lastLoadedNoteId_ = null;
@ -30,6 +34,20 @@ class NoteTextComponent extends React.Component {
this.webviewListeners_ = null;
this.ignoreNextEditorScroll_ = false;
this.scheduleSaveTimeout_ = null;
this.restoreScrollTop_ = null;
// Complicated but reliable method to get editor content height
// https://github.com/ajaxorg/ace/issues/2046
this.editorMaxScrollTop_ = 0;
this.onAfterEditorRender_ = () => {
const r = this.editor_.editor.renderer;
this.editorMaxScrollTop_ = Math.max(0, r.layerConfig.maxHeight - r.$size.scrollerHeight);
if (this.restoreScrollTop_) {
this.editorSetScrollTop(this.restoreScrollTop_);
this.restoreScrollTop_ = null;
}
}
}
mdToHtml() {
@ -120,6 +138,12 @@ class NoteTextComponent extends React.Component {
reg.logger().info('Got ipc-message: ' + msg, args);
if (msg.indexOf('checkboxclick:') === 0) {
// Ugly hack because setting the body here will make the scrollbar
// go to some random position. So we save the scrollTop here and it
// will be restored after the editor ref has been reset, and the
// "afterRender" event has been called.
this.restoreScrollTop_ = this.editorScrollTop();
const newBody = this.mdToHtml_.handleCheckboxClick(msg, this.state.note.body);
this.saveOneProperty('body', newBody);
} else if (msg.toLowerCase().indexOf('http') === 0) {
@ -143,11 +167,19 @@ class NoteTextComponent extends React.Component {
}
editorMaxScroll() {
return Math.max(0, this.editor_.scrollHeight - this.editor_.clientHeight);
return this.editorMaxScrollTop_;
}
editorScrollTop() {
return this.editor_.editor.getSession().getScrollTop();
}
editorSetScrollTop(v) {
this.editor_.editor.getSession().setScrollTop(v);
}
setEditorPercentScroll(p) {
this.editor_.scrollTop = p * this.editorMaxScroll();
this.editorSetScrollTop(p * this.editorMaxScroll());
}
setViewerPercentScroll(p) {
@ -159,8 +191,9 @@ class NoteTextComponent extends React.Component {
this.ignoreNextEditorScroll_ = false;
return;
}
const m = this.editorMaxScroll();
this.setViewerPercentScroll(m ? this.editor_.scrollTop / m : 0);
this.setViewerPercentScroll(m ? this.editorScrollTop() / m : 0);
}
webview_domReady() {
@ -188,7 +221,17 @@ class NoteTextComponent extends React.Component {
editor_ref(element) {
if (this.editor_ === element) return;
if (this.editor_) {
this.editorMaxScrollTop_ = 0;
this.editor_.editor.renderer.off('afterRender', this.onAfterEditorRender_);
}
this.editor_ = element;
if (this.editor_) {
this.editor_.editor.renderer.on('afterRender', this.onAfterEditorRender_);
}
}
initWebview(wv) {
@ -220,6 +263,11 @@ class NoteTextComponent extends React.Component {
this.webview_ = null;
}
aceEditor_change(body) {
shared.noteComponent_change(this, 'body', body);
this.scheduleSave();
}
render() {
const style = this.props.style;
const note = this.state.note;
@ -239,7 +287,7 @@ class NoteTextComponent extends React.Component {
const editorStyle = {
width: style.width - viewerStyle.width,
height: style.height - paddingTop,
overflowY: 'scroll',
overflowY: 'hidden',
float: 'left',
verticalAlign: 'top',
paddingTop: paddingTop + 'px',
@ -259,7 +307,32 @@ class NoteTextComponent extends React.Component {
}
const viewer = <webview style={viewerStyle} nodeintegration="1" src="note-content.html" ref={(elem) => { this.webview_ref(elem); } } />
const editor = <textarea style={editorStyle} value={body} onScroll={() => { this.editor_scroll(); }} onChange={(event) => { this.editor_change(event) }} ref={(elem) => { this.editor_ref(elem); } }></textarea>
const editorRootStyle = Object.assign({}, editorStyle);
delete editorRootStyle.width;
delete editorRootStyle.height;
delete editorRootStyle.fontSize;
const editor = <AceEditor
value={body}
mode="markdown"
theme="chrome"
style={editorRootStyle}
width={editorStyle.width + 'px'}
height={editorStyle.height + 'px'}
fontSize={editorStyle.fontSize}
showGutter={false}
name="note-editor"
wrapEnabled={true}
onScroll={(event) => { this.editor_scroll(); }}
ref={(elem) => { this.editor_ref(elem); } }
onChange={(body) => { this.aceEditor_change(body) }}
// Disable warning: "Automatically scrolling cursor into view after
// selection change this will be disabled in the next version set
// editor.$blockScrolling = Infinity to disable this message"
editorProps={{$blockScrolling: true}}
/>
return (
<div style={style}>

View File

@ -780,6 +780,14 @@
}
}
},
"brace": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/brace/-/brace-0.10.0.tgz",
"integrity": "sha1-7e9OubCSi6HuX3F//BV3SabdXXY=",
"requires": {
"w3c-blob": "0.0.1"
}
},
"brace-expansion": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
@ -2062,6 +2070,11 @@
"sntp": "2.1.0"
}
},
"highlight.js": {
"version": "9.12.0",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz",
"integrity": "sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4="
},
"hoek": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz",
@ -2576,6 +2589,16 @@
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=",
"dev": true
},
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
},
"lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
},
"lodash.toarray": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
@ -3471,6 +3494,17 @@
"prop-types": "15.6.0"
}
},
"react-ace": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/react-ace/-/react-ace-5.5.0.tgz",
"integrity": "sha1-N9nCAR23IYTr8nq2/9inmBqAQUI=",
"requires": {
"brace": "0.10.0",
"lodash.get": "4.4.2",
"lodash.isequal": "4.5.0",
"prop-types": "15.6.0"
}
},
"react-dom": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.0.0.tgz",
@ -5055,6 +5089,11 @@
"extsprintf": "1.3.0"
}
},
"w3c-blob": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/w3c-blob/-/w3c-blob-0.0.1.tgz",
"integrity": "sha1-sM01KhpQ9RVWNCD/1YYflQ8dhbg="
},
"whatwg-fetch": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",

View File

@ -28,6 +28,7 @@
"app-module-path": "^2.2.0",
"electron-context-menu": "^0.9.1",
"fs-extra": "^4.0.2",
"highlight.js": "^9.12.0",
"html-entities": "^1.2.1",
"lodash": "^4.17.4",
"markdown-it": "^8.4.0",
@ -38,6 +39,7 @@
"promise": "^8.0.1",
"query-string": "^5.0.1",
"react": "^16.0.0",
"react-ace": "^5.5.0",
"react-dom": "^16.0.0",
"react-redux": "^5.0.6",
"redux": "^3.7.2",