From e9dac8c8f30719357b59fa03c54879ec3f95321a Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 1 Oct 2018 18:16:26 +0200 Subject: [PATCH 01/59] fix scrolling in note list --- browser/main/NoteList/index.js | 3 +-- browser/main/TopBar/index.js | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 30ad93c3..a9818e4f 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -2,7 +2,6 @@ import PropTypes from 'prop-types' import React from 'react' import CSSModules from 'browser/lib/CSSModules' -import debounceRender from 'react-debounce-render' import styles from './NoteList.styl' import moment from 'moment' import _ from 'lodash' @@ -1078,4 +1077,4 @@ NoteList.propTypes = { }) } -export default debounceRender(CSSModules(NoteList, styles)) +export default CSSModules(NoteList, styles) diff --git a/browser/main/TopBar/index.js b/browser/main/TopBar/index.js index a5687ecb..d1844532 100644 --- a/browser/main/TopBar/index.js +++ b/browser/main/TopBar/index.js @@ -6,6 +6,7 @@ import _ from 'lodash' import ee from 'browser/main/lib/eventEmitter' import NewNoteButton from 'browser/main/NewNoteButton' import i18n from 'browser/lib/i18n' +import debounce from 'lodash/debounce' class TopBar extends React.Component { constructor (props) { @@ -25,6 +26,10 @@ class TopBar extends React.Component { } this.codeInitHandler = this.handleCodeInit.bind(this) + + this.updateKeyword = debounce(this.updateKeyword, 1000 / 60, { + maxWait: 1000 / 8 + }) } componentDidMount () { @@ -94,7 +99,6 @@ class TopBar extends React.Component { } handleKeyUp (e) { - const { router } = this.context // reset states this.setState({ isConfirmTranslation: false @@ -106,21 +110,21 @@ class TopBar extends React.Component { isConfirmTranslation: true }) const keyword = this.refs.searchInput.value - router.push(`/searched/${encodeURIComponent(keyword)}`) - this.setState({ - search: keyword - }) + this.updateKeyword(keyword) } } handleSearchChange (e) { - const { router } = this.context - const keyword = this.refs.searchInput.value if (this.state.isAlphabet || this.state.isConfirmTranslation) { - router.push(`/searched/${encodeURIComponent(keyword)}`) + const keyword = this.refs.searchInput.value + this.updateKeyword(keyword) } else { e.preventDefault() } + } + + updateKeyword(keyword) { + this.context.router.push(`/searched/${encodeURIComponent(keyword)}`) this.setState({ search: keyword }) From fa6c504b34934609edb9a99007a8811addabcdae Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 1 Oct 2018 18:42:18 +0200 Subject: [PATCH 02/59] fix lint error --- browser/main/TopBar/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/TopBar/index.js b/browser/main/TopBar/index.js index d1844532..91256daf 100644 --- a/browser/main/TopBar/index.js +++ b/browser/main/TopBar/index.js @@ -123,7 +123,7 @@ class TopBar extends React.Component { } } - updateKeyword(keyword) { + updateKeyword (keyword) { this.context.router.push(`/searched/${encodeURIComponent(keyword)}`) this.setState({ search: keyword From e52bcf33c579de9ddb80f7fb4bb5751adb39bb02 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 1 Oct 2018 18:54:50 +0200 Subject: [PATCH 03/59] add `precommit` command --- package.json | 6 +++ yarn.lock | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index f4e5f532..4fe3434a 100644 --- a/package.json +++ b/package.json @@ -132,6 +132,7 @@ "grunt": "^0.4.5", "grunt-electron-installer": "2.1.0", "history": "^1.17.0", + "husky": "^1.1.0", "identity-obj-proxy": "^3.0.0", "jest": "^22.4.3", "jest-localstorage-mock": "^2.2.0", @@ -179,5 +180,10 @@ "/tests/jest.js", "jest-localstorage-mock" ] + }, + "husky": { + "hooks": { + "pre-commit": "npm run lint" + } } } diff --git a/yarn.lock b/yarn.lock index 687bb6de..5c9c1cc4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1677,6 +1677,10 @@ ci-info@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.3.tgz#710193264bb05c77b8c90d02f5aaf22216a667b2" +ci-info@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + circular-json@^0.3.1: version "0.3.3" resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" @@ -2074,6 +2078,14 @@ core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" +cosmiconfig@^5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.6.tgz#dca6cf680a0bd03589aff684700858c81abeeb39" + dependencies: + is-directory "^0.3.1" + js-yaml "^3.9.0" + parse-json "^4.0.0" + create-error-class@^3.0.0, create-error-class@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" @@ -3309,6 +3321,18 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.9.0.tgz#adb7ce62cf985071f60580deb4a88b9e34712d01" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + exit-hook@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" @@ -3615,6 +3639,12 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + dependencies: + locate-path "^3.0.0" + findup-sync@~0.1.2: version "0.1.3" resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.1.3.tgz#7f3e7a97b82392c653bf06589bd85190e93c3683" @@ -3870,6 +3900,10 @@ get-stdin@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -4411,6 +4445,21 @@ humanize-plus@^1.8.1: version "1.8.2" resolved "https://registry.yarnpkg.com/humanize-plus/-/humanize-plus-1.8.2.tgz#a65b34459ad6367adbb3707a82a3c9f916167030" +husky@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/husky/-/husky-1.1.0.tgz#7271e85f5d98b54349788839b720c9a60cd95dba" + dependencies: + cosmiconfig "^5.0.6" + execa "^0.9.0" + find-up "^3.0.0" + get-stdin "^6.0.0" + is-ci "^1.2.1" + pkg-dir "^3.0.0" + please-upgrade-node "^3.1.1" + read-pkg "^4.0.1" + run-node "^1.0.0" + slash "^2.0.0" + i18n-2@^0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/i18n-2/-/i18n-2-0.7.2.tgz#7efb1a7adc67869adea0688951577464aa793aaf" @@ -4617,6 +4666,12 @@ is-ci@^1.0.10, is-ci@^1.0.7: dependencies: ci-info "^1.0.0" +is-ci@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" + dependencies: + ci-info "^1.5.0" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -4649,6 +4704,10 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -5286,7 +5345,7 @@ js-yaml@^3.10.0, js-yaml@^3.5.1, js-yaml@^3.7.0: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^3.8.1: +js-yaml@^3.8.1, js-yaml@^3.9.0: version "3.12.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" dependencies: @@ -5612,6 +5671,13 @@ locate-path@^2.0.0: p-locate "^2.0.0" path-exists "^3.0.0" +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + lodash-es@^4.2.1: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.10.tgz#62cd7104cdf5dd87f235a837f0ede0e8e5117e05" @@ -6595,16 +6661,32 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" +p-limit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec" + dependencies: + p-try "^2.0.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" dependencies: p-limit "^1.1.0" +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + dependencies: + p-limit "^2.0.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" +p-try@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" + package-hash@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-1.2.0.tgz#003e56cd57b736a6ed6114cc2b81542672770e44" @@ -6800,12 +6882,24 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + dependencies: + find-up "^3.0.0" + pkg-up@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" dependencies: find-up "^2.1.0" +please-upgrade-node@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz#ed320051dfcc5024fae696712c8288993595e8ac" + dependencies: + semver-compare "^1.0.0" + plist@^2.0.0, plist@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/plist/-/plist-2.1.0.tgz#57ccdb7a0821df21831217a3cad54e3e146a1025" @@ -7455,6 +7549,14 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" +read-pkg@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237" + dependencies: + normalize-package-data "^2.3.2" + parse-json "^4.0.0" + pify "^3.0.0" + readable-stream@^1.1.8, readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -7805,6 +7907,10 @@ run-async@^0.1.0: dependencies: once "^1.3.0" +run-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/run-node/-/run-node-1.0.0.tgz#46b50b946a2aa2d4947ae1d886e9856fd9cabe5e" + run-parallel@^1.1.2: version "1.1.9" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" @@ -7904,6 +8010,10 @@ section-iterator@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/section-iterator/-/section-iterator-2.0.0.tgz#bf444d7afeeb94ad43c39ad2fb26151627ccba2a" +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + semver-diff@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" @@ -8057,6 +8167,10 @@ slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" From f308836264e8e438c2e111b8607c975fce3afe2c Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Tue, 2 Oct 2018 23:48:07 +0200 Subject: [PATCH 04/59] add new fenced block language: gallery --- browser/components/MarkdownPreview.js | 33 ++++++++++++++++++- browser/components/markdown.styl | 46 ++++++++++++++++++++++++++- browser/lib/markdown.js | 6 ++++ package.json | 2 ++ yarn.lock | 38 ++++++++++++++++++++++ 5 files changed, 123 insertions(+), 2 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index eacc4e21..48951204 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -21,6 +21,8 @@ import yaml from 'js-yaml' import context from 'browser/lib/context' import i18n from 'browser/lib/i18n' import fs from 'fs' +import { render } from 'react-dom' +import Carousel from 'react-image-carousel' const { remote, shell } = require('electron') const attachmentManagement = require('../main/lib/dataApi/attachmentManagement') @@ -39,7 +41,8 @@ const appPath = fileUrl( ) const CSS_FILES = [ `${appPath}/node_modules/katex/dist/katex.min.css`, - `${appPath}/node_modules/codemirror/lib/codemirror.css` + `${appPath}/node_modules/codemirror/lib/codemirror.css`, + `${appPath}/node_modules/react-image-carousel/lib/css/main.min.css` ] function buildStyle ( @@ -789,6 +792,34 @@ export default class MarkdownPreview extends React.Component { mermaidRender(el, htmlTextHelper.decodeEntities(el.innerHTML), theme) } ) + + _.forEach( + this.refs.root.contentWindow.document.querySelectorAll('.gallery'), + el => { + const images = el.innerHTML.split(/\n/g).filter(i => i.length > 0) + el.innerHTML = '' + + const height = el.attributes.getNamedItem('data-height') + if (height && height.value !== 'undefined') { + el.style.height = height.value + 'vh' + } + + let autoplay = el.attributes.getNamedItem('data-autoplay') + if (autoplay && autoplay.value !== 'undefined') { + autoplay = parseInt(autoplay.value, 10) || 0 + } else { + autoplay = 0 + } + + render( + , + el + ) + } + ) } focus () { diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl index 2e17a75b..6e43273e 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -381,6 +381,26 @@ pre.fence canvas, svg max-width 100% !important + .gallery + width 100% + height 50vh + + .carousel + .carousel-main img + min-width auto + max-width 100% + min-height auto + max-height 100% + + .carousel-footer::-webkit-scrollbar-corner + background-color transparent + + .carousel-main, .carousel-footer + background-color $ui-noteDetail-backgroundColor + .prev, .next + color $ui-text-color + background-color $ui-tag-backgroundColor + themeDarkBackground = darken(#21252B, 10%) themeDarkText = #f9f9f9 themeDarkBorder = lighten(themeDarkBackground, 20%) @@ -432,6 +452,14 @@ body[data-theme="dark"] background-color themeDarkBorder color themeDarkText + pre.fence + .gallery + .carousel-main, .carousel-footer + background-color $ui-dark-noteDetail-backgroundColor + .prev, .next + color $ui-dark-text-color + background-color $ui-dark-tag-backgroundColor + themeSolarizedDarkTableOdd = $ui-solarized-dark-noteDetail-backgroundColor themeSolarizedDarkTableEven = darken($ui-solarized-dark-noteDetail-backgroundColor, 10%) themeSolarizedDarkTableHead = themeSolarizedDarkTableEven @@ -459,6 +487,14 @@ body[data-theme="solarized-dark"] &:last-child border-right solid 1px themeSolarizedDarkTableBorder + pre.fence + .gallery + .carousel-main, .carousel-footer + background-color $ui-solarized-dark-noteDetail-backgroundColor + .prev, .next + color $ui-solarized-dark-text-color + background-color $ui-solarized-dark-tag-backgroundColor + themeMonokaiTableOdd = $ui-monokai-noteDetail-backgroundColor themeMonokaiTableEven = darken($ui-monokai-noteDetail-backgroundColor, 10%) themeMonokaiTableHead = themeMonokaiTableEven @@ -486,4 +522,12 @@ body[data-theme="monokai"] &:last-child border-right solid 1px themeMonokaiTableBorder kbd - background-color themeDarkBackground \ No newline at end of file + background-color themeDarkBackground + + pre.fence + .gallery + .carousel-main, .carousel-footer + background-color $ui-monokai-noteDetail-backgroundColor + .prev, .next + color $ui-monokai-text-color + background-color $ui-monokai-tag-backgroundColor diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index f8ae7f05..52a13700 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -143,6 +143,12 @@ class Markdown {
${token.content}
` }, + gallery: token => { + return `
+          ${token.fileName}
+          
+        
` + }, mermaid: token => { return `
           ${token.fileName}
diff --git a/package.json b/package.json
index 3e95603b..560a2e7d 100644
--- a/package.json
+++ b/package.json
@@ -95,8 +95,10 @@
     "react-codemirror": "^0.3.0",
     "react-debounce-render": "^4.0.1",
     "react-dom": "^15.0.2",
+    "react-image-carousel": "^2.0.18",
     "react-redux": "^4.4.5",
     "react-sortable-hoc": "^0.6.7",
+    "react-transition-group": "^2.5.0",
     "redux": "^3.5.2",
     "sander": "^0.5.1",
     "sanitize-html": "^1.18.2",
diff --git a/yarn.lock b/yarn.lock
index 5c426c92..55f4102a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2701,6 +2701,10 @@ doctrine@^2.0.0, doctrine@^2.0.2:
   dependencies:
     esutils "^2.0.2"
 
+dom-helpers@^3.3.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6"
+
 dom-serializer@0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
@@ -5279,6 +5283,10 @@ js-tokens@^3.0.0, js-tokens@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
 
+"js-tokens@^3.0.0 || ^4.0.0":
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+
 js-yaml@^3.10.0, js-yaml@^3.5.1, js-yaml@^3.7.0:
   version "3.11.0"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef"
@@ -5729,6 +5737,12 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3
   dependencies:
     js-tokens "^3.0.0"
 
+loose-envify@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  dependencies:
+    js-tokens "^3.0.0 || ^4.0.0"
+
 loud-rejection@^1.0.0, loud-rejection@^1.2.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
@@ -7155,6 +7169,13 @@ prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8,
     loose-envify "^1.3.1"
     object-assign "^4.1.1"
 
+prop-types@^15.6.2:
+  version "15.6.2"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
+  dependencies:
+    loose-envify "^1.3.1"
+    object-assign "^4.1.1"
+
 proxy-addr@~2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
@@ -7330,6 +7351,10 @@ react-dom@^15.0.2:
     object-assign "^4.1.0"
     prop-types "^15.5.10"
 
+react-image-carousel@^2.0.18:
+  version "2.0.18"
+  resolved "https://registry.yarnpkg.com/react-image-carousel/-/react-image-carousel-2.0.18.tgz#5868ea09bd9cca09c4467d3d02695cd4e7792f28"
+
 react-input-autosize@^1.1.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-1.2.0.tgz#87241071159f742123897691da6796ec33b57d05"
@@ -7337,6 +7362,10 @@ react-input-autosize@^1.1.0:
     create-react-class "^15.5.2"
     prop-types "^15.5.8"
 
+react-lifecycles-compat@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
+
 react-proxy@^1.1.7:
   version "1.1.8"
   resolved "https://registry.yarnpkg.com/react-proxy/-/react-proxy-1.1.8.tgz#9dbfd9d927528c3aa9f444e4558c37830ab8c26a"
@@ -7402,6 +7431,15 @@ react-transform-hmr@^1.0.3:
     global "^4.3.0"
     react-proxy "^1.1.7"
 
+react-transition-group@^2.5.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.5.0.tgz#70bca0e3546102c4dc5cf3f5f57f73447cce6874"
+  dependencies:
+    dom-helpers "^3.3.1"
+    loose-envify "^1.4.0"
+    prop-types "^15.6.2"
+    react-lifecycles-compat "^3.0.4"
+
 react@^15.5.4:
   version "15.6.2"
   resolved "https://registry.yarnpkg.com/react/-/react-15.6.2.tgz#dba0434ab439cfe82f108f0f511663908179aa72"

From 39eaed260a834e51283cbcc42c002f1152955a36 Mon Sep 17 00:00:00 2001
From: Baptiste Augrain 
Date: Mon, 8 Oct 2018 15:57:42 +0200
Subject: [PATCH 05/59] display correctly attached image

---
 browser/main/lib/dataApi/attachmentManagement.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js
index 912450c1..38648941 100644
--- a/browser/main/lib/dataApi/attachmentManagement.js
+++ b/browser/main/lib/dataApi/attachmentManagement.js
@@ -227,7 +227,7 @@ function migrateAttachments (markdownContent, storagePath, noteKey) {
  * @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths.
  */
 function fixLocalURLS (renderedHTML, storagePath) {
-  return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?"', 'g'), function (match) {
+  return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '[\w\/]+', 'g'), function (match) {
     var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g')
     return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER))
   })

From 7a5a821f8a8b8919d9c5ae0b0dca377485ec25f2 Mon Sep 17 00:00:00 2001
From: Baptiste Augrain 
Date: Tue, 9 Oct 2018 00:47:37 +0200
Subject: [PATCH 06/59] fix failing test

---
 browser/main/lib/dataApi/attachmentManagement.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js
index 38648941..a81d3bd4 100644
--- a/browser/main/lib/dataApi/attachmentManagement.js
+++ b/browser/main/lib/dataApi/attachmentManagement.js
@@ -227,7 +227,7 @@ function migrateAttachments (markdownContent, storagePath, noteKey) {
  * @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths.
  */
 function fixLocalURLS (renderedHTML, storagePath) {
-  return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '[\w\/]+', 'g'), function (match) {
+  return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:\\\/|%5C)[\\w.]+', 'g'), function (match) {
     var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g')
     return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER))
   })

From 5d9b1abe82a68c51b99564f6f117191c246f98b3 Mon Sep 17 00:00:00 2001
From: Baptiste Augrain 
Date: Tue, 9 Oct 2018 01:18:19 +0200
Subject: [PATCH 07/59] allow markdown image syntax

---
 browser/lib/markdown.js | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js
index 52a13700..cc5e86dc 100644
--- a/browser/lib/markdown.js
+++ b/browser/lib/markdown.js
@@ -144,9 +144,18 @@ class Markdown {
         
` }, gallery: token => { + const content = token.content.split('\n').slice(0, -1).map(line => { + const match = /!\[[^\]]*]\(([^\)]*)\)/.exec(line) + if (match) { + return match[1] + } else { + return line + } + }).join('\n') + return `
           ${token.fileName}
-          
+          
         
` }, mermaid: token => { From db97ab51acf97786048061929371d5f6141cd930 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 11:44:03 +0000 Subject: [PATCH 08/59] Bracket matching option added to config --- browser/components/CodeEditor.js | 373 ++++++++++++------ browser/components/MarkdownEditor.js | 1 + browser/components/MarkdownSplitEditor.js | 1 + browser/main/Detail/SnippetNoteDetail.js | 1 + browser/main/lib/ConfigManager.js | 1 + .../modals/PreferencesModal/SnippetEditor.js | 11 +- .../modals/PreferencesModal/SnippetTab.js | 1 + browser/main/modals/PreferencesModal/UiTab.js | 17 +- 8 files changed, 284 insertions(+), 122 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 41d71622..f4ce77b9 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -5,25 +5,35 @@ import CodeMirror from 'codemirror' import 'codemirror-mode-elixir' import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' import convertModeName from 'browser/lib/convertModeName' -import { options, TableEditor, Alignment } from '@susisu/mte-kernel' +import { + options, + TableEditor, + Alignment +} from '@susisu/mte-kernel' import TextEditorInterface from 'browser/lib/TextEditorInterface' import eventEmitter from 'browser/main/lib/eventEmitter' import iconv from 'iconv-lite' import crypto from 'crypto' import consts from 'browser/lib/consts' import fs from 'fs' -const { ipcRenderer } = require('electron') +const { + ipcRenderer +} = require('electron') import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' import TurndownService from 'turndown' -import { gfm } from 'turndown-plugin-gfm' +import { + gfm +} from 'turndown-plugin-gfm' CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' const buildCMRulers = (rulers, enableRulers) => - (enableRulers ? rulers.map(ruler => ({ column: ruler })) : []) + (enableRulers ? rulers.map(ruler => ({ + column: ruler + })) : []) export default class CodeEditor extends React.Component { - constructor (props) { + constructor(props) { super(props) this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, { @@ -46,7 +56,10 @@ export default class CodeEditor extends React.Component { } this.props.onBlur != null && this.props.onBlur(e) - const { storageKey, noteKey } = this.props + const { + storageKey, + noteKey + } = this.props attachmentManagement.deleteAttachmentsNotPresentInNote( this.editor.getValue(), storageKey, @@ -65,7 +78,7 @@ export default class CodeEditor extends React.Component { this.editorActivityHandler = () => this.handleEditorActivity() } - handleSearch (msg) { + handleSearch(msg) { const cm = this.editor const component = this @@ -76,7 +89,7 @@ export default class CodeEditor extends React.Component { component.searchState = makeOverlay(msg, 'searching') cm.addOverlay(component.searchState) - function makeOverlay (query, style) { + function makeOverlay(query, style) { query = new RegExp( query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), 'gi' @@ -99,17 +112,19 @@ export default class CodeEditor extends React.Component { }) } - handleFormatTable () { - this.tableEditor.formatAll(options({textWidthOptions: {}})) + handleFormatTable() { + this.tableEditor.formatAll(options({ + textWidthOptions: {} + })) } - handleEditorActivity () { + handleEditorActivity() { if (!this.textEditorInterface.transaction) { this.updateTableEditorState() } } - updateTableEditorState () { + updateTableEditorState() { const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions) if (active) { if (this.extraKeysMode !== 'editor') { @@ -125,19 +140,20 @@ export default class CodeEditor extends React.Component { } } - componentDidMount () { - const { rulers, enableRulers } = this.props + componentDidMount() { + const { + rulers, + enableRulers + } = this.props const expandSnippet = this.expandSnippet.bind(this) eventEmitter.on('line:jump', this.scrollToLineHandeler) - const defaultSnippet = [ - { - id: crypto.randomBytes(16).toString('hex'), - name: 'Dummy text', - prefix: ['lorem', 'ipsum'], - content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' - } - ] + const defaultSnippet = [{ + id: crypto.randomBytes(16).toString('hex'), + name: 'Dummy text', + prefix: ['lorem', 'ipsum'], + content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' + }] if (!fs.existsSync(consts.SNIPPET_FILE)) { fs.writeFileSync( consts.SNIPPET_FILE, @@ -215,12 +231,17 @@ export default class CodeEditor extends React.Component { dragDrop: false, foldGutter: true, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], - autoCloseBrackets: { - pairs: '()[]{}\'\'""$$**``', + autoCloseBrackets: (this.props.enableBracketMatching ? { + pairs: '()[]{}\'\'""$$**``ll', triples: '```"""\'\'\'', explode: '[]{}``$$', override: true - }, + } : { + pairs: '', + triples: '', + explode: '', + override: true + }), extraKeys: this.defaultKeyMap }) @@ -253,43 +274,117 @@ export default class CodeEditor extends React.Component { }) this.editorKeyMap = CodeMirror.normalizeKeyMap({ - 'Tab': () => { this.tableEditor.nextCell(this.tableEditorOptions) }, - 'Shift-Tab': () => { this.tableEditor.previousCell(this.tableEditorOptions) }, - 'Enter': () => { this.tableEditor.nextRow(this.tableEditorOptions) }, - 'Ctrl-Enter': () => { this.tableEditor.escape(this.tableEditorOptions) }, - 'Cmd-Enter': () => { this.tableEditor.escape(this.tableEditorOptions) }, - 'Shift-Ctrl-Left': () => { this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) }, - 'Shift-Cmd-Left': () => { this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) }, - 'Shift-Ctrl-Right': () => { this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) }, - 'Shift-Cmd-Right': () => { this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) }, - 'Shift-Ctrl-Up': () => { this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) }, - 'Shift-Cmd-Up': () => { this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) }, - 'Shift-Ctrl-Down': () => { this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) }, - 'Shift-Cmd-Down': () => { this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) }, - 'Ctrl-Left': () => { this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) }, - 'Cmd-Left': () => { this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) }, - 'Ctrl-Right': () => { this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) }, - 'Cmd-Right': () => { this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) }, - 'Ctrl-Up': () => { this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) }, - 'Cmd-Up': () => { this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) }, - 'Ctrl-Down': () => { this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) }, - 'Cmd-Down': () => { this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) }, - 'Ctrl-K Ctrl-I': () => { this.tableEditor.insertRow(this.tableEditorOptions) }, - 'Cmd-K Cmd-I': () => { this.tableEditor.insertRow(this.tableEditorOptions) }, - 'Ctrl-L Ctrl-I': () => { this.tableEditor.deleteRow(this.tableEditorOptions) }, - 'Cmd-L Cmd-I': () => { this.tableEditor.deleteRow(this.tableEditorOptions) }, - 'Ctrl-K Ctrl-J': () => { this.tableEditor.insertColumn(this.tableEditorOptions) }, - 'Cmd-K Cmd-J': () => { this.tableEditor.insertColumn(this.tableEditorOptions) }, - 'Ctrl-L Ctrl-J': () => { this.tableEditor.deleteColumn(this.tableEditorOptions) }, - 'Cmd-L Cmd-J': () => { this.tableEditor.deleteColumn(this.tableEditorOptions) }, - 'Alt-Shift-Ctrl-Left': () => { this.tableEditor.moveColumn(-1, this.tableEditorOptions) }, - 'Alt-Shift-Cmd-Left': () => { this.tableEditor.moveColumn(-1, this.tableEditorOptions) }, - 'Alt-Shift-Ctrl-Right': () => { this.tableEditor.moveColumn(1, this.tableEditorOptions) }, - 'Alt-Shift-Cmd-Right': () => { this.tableEditor.moveColumn(1, this.tableEditorOptions) }, - 'Alt-Shift-Ctrl-Up': () => { this.tableEditor.moveRow(-1, this.tableEditorOptions) }, - 'Alt-Shift-Cmd-Up': () => { this.tableEditor.moveRow(-1, this.tableEditorOptions) }, - 'Alt-Shift-Ctrl-Down': () => { this.tableEditor.moveRow(1, this.tableEditorOptions) }, - 'Alt-Shift-Cmd-Down': () => { this.tableEditor.moveRow(1, this.tableEditorOptions) } + 'Tab': () => { + this.tableEditor.nextCell(this.tableEditorOptions) + }, + 'Shift-Tab': () => { + this.tableEditor.previousCell(this.tableEditorOptions) + }, + 'Enter': () => { + this.tableEditor.nextRow(this.tableEditorOptions) + }, + 'Ctrl-Enter': () => { + this.tableEditor.escape(this.tableEditorOptions) + }, + 'Cmd-Enter': () => { + this.tableEditor.escape(this.tableEditorOptions) + }, + 'Shift-Ctrl-Left': () => { + this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) + }, + 'Shift-Cmd-Left': () => { + this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) + }, + 'Shift-Ctrl-Right': () => { + this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) + }, + 'Shift-Cmd-Right': () => { + this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) + }, + 'Shift-Ctrl-Up': () => { + this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) + }, + 'Shift-Cmd-Up': () => { + this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) + }, + 'Shift-Ctrl-Down': () => { + this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) + }, + 'Shift-Cmd-Down': () => { + this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) + }, + 'Ctrl-Left': () => { + this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) + }, + 'Cmd-Left': () => { + this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) + }, + 'Ctrl-Right': () => { + this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) + }, + 'Cmd-Right': () => { + this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) + }, + 'Ctrl-Up': () => { + this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) + }, + 'Cmd-Up': () => { + this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) + }, + 'Ctrl-Down': () => { + this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) + }, + 'Cmd-Down': () => { + this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) + }, + 'Ctrl-K Ctrl-I': () => { + this.tableEditor.insertRow(this.tableEditorOptions) + }, + 'Cmd-K Cmd-I': () => { + this.tableEditor.insertRow(this.tableEditorOptions) + }, + 'Ctrl-L Ctrl-I': () => { + this.tableEditor.deleteRow(this.tableEditorOptions) + }, + 'Cmd-L Cmd-I': () => { + this.tableEditor.deleteRow(this.tableEditorOptions) + }, + 'Ctrl-K Ctrl-J': () => { + this.tableEditor.insertColumn(this.tableEditorOptions) + }, + 'Cmd-K Cmd-J': () => { + this.tableEditor.insertColumn(this.tableEditorOptions) + }, + 'Ctrl-L Ctrl-J': () => { + this.tableEditor.deleteColumn(this.tableEditorOptions) + }, + 'Cmd-L Cmd-J': () => { + this.tableEditor.deleteColumn(this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Left': () => { + this.tableEditor.moveColumn(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Left': () => { + this.tableEditor.moveColumn(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Right': () => { + this.tableEditor.moveColumn(1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Right': () => { + this.tableEditor.moveColumn(1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Up': () => { + this.tableEditor.moveRow(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Up': () => { + this.tableEditor.moveRow(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Down': () => { + this.tableEditor.moveRow(1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Down': () => { + this.tableEditor.moveRow(1, this.tableEditorOptions) + } }) if (this.props.enableTableEditor) { @@ -302,7 +397,7 @@ export default class CodeEditor extends React.Component { }) } - expandSnippet (line, cursor, cm, snippets) { + expandSnippet(line, cursor, cm, snippets) { const wordBeforeCursor = this.getWordBeforeCursor( line, cursor.line, @@ -345,7 +440,7 @@ export default class CodeEditor extends React.Component { return false } - getWordBeforeCursor (line, lineNumber, cursorPosition) { + getWordBeforeCursor(line, lineNumber, cursorPosition) { let wordBeforeCursor = '' const originCursorPosition = cursorPosition const emptyChars = /\t|\s|\r|\n/ @@ -370,17 +465,23 @@ export default class CodeEditor extends React.Component { return { text: wordBeforeCursor, range: { - from: { line: lineNumber, ch: originCursorPosition }, - to: { line: lineNumber, ch: cursorPosition } + from: { + line: lineNumber, + ch: originCursorPosition + }, + to: { + line: lineNumber, + ch: cursorPosition + } } } } - quitEditor () { + quitEditor() { document.querySelector('textarea').blur() } - componentWillUnmount () { + componentWillUnmount() { this.editor.off('focus', this.focusHandler) this.editor.off('blur', this.blurHandler) this.editor.off('change', this.changeHandler) @@ -393,9 +494,12 @@ export default class CodeEditor extends React.Component { eventEmitter.off('code:format-table', this.formatTable) } - componentDidUpdate (prevProps, prevState) { + componentDidUpdate(prevProps, prevState) { let needRefresh = false - const { rulers, enableRulers } = this.props + const { + rulers, + enableRulers + } = this.props if (prevProps.mode !== this.props.mode) { this.setMode(this.props.mode) } @@ -436,6 +540,21 @@ export default class CodeEditor extends React.Component { this.editor.setOption('scrollPastEnd', this.props.scrollPastEnd) } + if (prevProps.enableBracketMatching !== this.props.enableBracketMatching) { + const bracketObject = (this.props.enableBracketMatching ? { + pairs: '()[]{}\'\'""$$**``ll', + triples: '```"""\'\'\'', + explode: '[]{}``$$', + override: true + } : { + pairs: '', + triples: '', + explode: '', + override: true + }); + this.editor.setOption('autoCloseBrackets', bracketObject) + } + if (prevProps.enableTableEditor !== this.props.enableTableEditor) { if (this.props.enableTableEditor) { this.editor.on('cursorActivity', this.editorActivityHandler) @@ -462,7 +581,7 @@ export default class CodeEditor extends React.Component { } } - setMode (mode) { + setMode(mode) { let syntax = CodeMirror.findModeByName(convertModeName(mode)) if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') @@ -470,16 +589,16 @@ export default class CodeEditor extends React.Component { CodeMirror.autoLoadMode(this.editor, syntax.mode) } - handleChange (e) { + handleChange(e) { this.value = this.editor.getValue() if (this.props.onChange) { this.props.onChange(e) } } - moveCursorTo (row, col) {} + moveCursorTo(row, col) {} - scrollToLine (event, num) { + scrollToLine(event, num) { const cursor = { line: num, ch: 1 @@ -487,15 +606,15 @@ export default class CodeEditor extends React.Component { this.editor.setCursor(cursor) } - focus () { + focus() { this.editor.focus() } - blur () { + blur() { this.editor.blur() } - reload () { + reload() { // Change event shouldn't be fired when switch note this.editor.off('change', this.changeHandler) this.value = this.props.value @@ -505,15 +624,18 @@ export default class CodeEditor extends React.Component { this.editor.refresh() } - setValue (value) { + setValue(value) { const cursor = this.editor.getCursor() this.editor.setValue(value) this.editor.setCursor(cursor) } - handleDropImage (dropEvent) { + handleDropImage(dropEvent) { dropEvent.preventDefault() - const { storageKey, noteKey } = this.props + const { + storageKey, + noteKey + } = this.props attachmentManagement.handleAttachmentDrop( this, storageKey, @@ -522,13 +644,16 @@ export default class CodeEditor extends React.Component { ) } - insertAttachmentMd (imageMd) { + insertAttachmentMd(imageMd) { this.editor.replaceSelection(imageMd) } - handlePaste (editor, e) { + handlePaste(editor, e) { const clipboardData = e.clipboardData - const { storageKey, noteKey } = this.props + const { + storageKey, + noteKey + } = this.props const dataTransferItem = clipboardData.items[0] const pastedTxt = clipboardData.getData('text') const isURL = str => { @@ -537,15 +662,21 @@ export default class CodeEditor extends React.Component { } const isInLinkTag = editor => { const startCursor = editor.getCursor('start') - const prevChar = editor.getRange( - { line: startCursor.line, ch: startCursor.ch - 2 }, - { line: startCursor.line, ch: startCursor.ch } - ) + const prevChar = editor.getRange({ + line: startCursor.line, + ch: startCursor.ch - 2 + }, { + line: startCursor.line, + ch: startCursor.ch + }) const endCursor = editor.getCursor('end') - const nextChar = editor.getRange( - { line: endCursor.line, ch: endCursor.ch }, - { line: endCursor.line, ch: endCursor.ch + 1 } - ) + const nextChar = editor.getRange({ + line: endCursor.line, + ch: endCursor.ch + }, { + line: endCursor.line, + ch: endCursor.ch + 1 + }) return prevChar === '](' && nextChar === ')' } @@ -576,13 +707,13 @@ export default class CodeEditor extends React.Component { } } - handleScroll (e) { + handleScroll(e) { if (this.props.onScroll) { this.props.onScroll(e) } } - handlePasteUrl (e, editor, pastedTxt) { + handlePasteUrl(e, editor, pastedTxt) { e.preventDefault() const taggedUrl = `<${pastedTxt}>` editor.replaceSelection(taggedUrl) @@ -605,8 +736,8 @@ export default class CodeEditor extends React.Component { } fetch(pastedTxt, { - method: 'get' - }) + method: 'get' + }) .then(response => { if (isImageReponse(response)) { return this.mapImageResponse(response, pastedTxt) @@ -622,13 +753,13 @@ export default class CodeEditor extends React.Component { }) } - handlePasteHtml (e, editor, pastedHtml) { + handlePasteHtml(e, editor, pastedHtml) { e.preventDefault() const markdown = this.turndownService.turndown(pastedHtml) editor.replaceSelection(markdown) } - mapNormalResponse (response, pastedTxt) { + mapNormalResponse(response, pastedTxt) { return this.decodeResponse(response).then(body => { return new Promise((resolve, reject) => { try { @@ -648,7 +779,7 @@ export default class CodeEditor extends React.Component { }) } - mapImageResponse (response, pastedTxt) { + mapImageResponse(response, pastedTxt) { return new Promise((resolve, reject) => { try { const url = response.url @@ -661,18 +792,18 @@ export default class CodeEditor extends React.Component { }) } - decodeResponse (response) { + decodeResponse(response) { const headers = response.headers - const _charset = headers.has('content-type') - ? this.extractContentTypeCharset(headers.get('content-type')) - : undefined + const _charset = headers.has('content-type') ? + this.extractContentTypeCharset(headers.get('content-type')) : + undefined return response.arrayBuffer().then(buff => { return new Promise((resolve, reject) => { try { const charset = _charset !== undefined && - iconv.encodingExists(_charset) - ? _charset - : 'utf-8' + iconv.encodingExists(_charset) ? + _charset : + 'utf-8' resolve(iconv.decode(new Buffer(buff), charset).toString()) } catch (e) { reject(e) @@ -681,7 +812,7 @@ export default class CodeEditor extends React.Component { }) } - extractContentTypeCharset (contentType) { + extractContentTypeCharset(contentType) { return contentType .split(';') .filter(str => { @@ -692,21 +823,29 @@ export default class CodeEditor extends React.Component { })[0] } - render () { - const {className, fontSize} = this.props + render() { + const { + className, + fontSize + } = this.props const fontFamily = normalizeEditorFontFamily(this.props.fontFamily) const width = this.props.width - return ( -
this.handleDropImage(e)} + } + } + onDrop = { + e => this.handleDropImage(e) + } /> ) } @@ -731,4 +870,4 @@ CodeEditor.defaultProps = { fontFamily: 'Monaco, Consolas', indentSize: 4, indentType: 'space' -} +} \ No newline at end of file diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 9c8a06d6..f10ea8bc 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -268,6 +268,7 @@ class MarkdownEditor extends React.Component { enableRulers={config.editor.enableRulers} rulers={config.editor.rulers} displayLineNumbers={config.editor.displayLineNumbers} + enableBracketMatching={config.editor.enableBracketMatching} scrollPastEnd={config.editor.scrollPastEnd} storageKey={storageKey} noteKey={noteKey} diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index ca2d3108..004a6eb0 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -158,6 +158,7 @@ class MarkdownSplitEditor extends React.Component { fontFamily={config.editor.fontFamily} fontSize={editorFontSize} displayLineNumbers={config.editor.displayLineNumbers} + enableBracketMatching={config.editor.enableBracketMatching} indentType={config.editor.indentType} indentSize={editorIndentSize} enableRulers={config.editor.enableRulers} diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index afd81102..90b8faa9 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -699,6 +699,7 @@ class SnippetNoteDetail extends React.Component { indentType={config.editor.indentType} indentSize={editorIndentSize} displayLineNumbers={config.editor.displayLineNumbers} + enableBracketMatching={config.editor.enableBracketMatching} keyMap={config.editor.keyMap} scrollPastEnd={config.editor.scrollPastEnd} fetchUrlTitle={config.editor.fetchUrlTitle} diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index 9611f21d..eddf0909 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -44,6 +44,7 @@ export const DEFAULT_CONFIG = { enableRulers: false, rulers: [80, 120], displayLineNumbers: true, + enableBracketMatching: true, switchPreview: 'BLUR', // 'BLUR', 'DBL_CLICK', 'RIGHTCLICK' delfaultStatus: 'PREVIEW', // 'PREVIEW', 'CODE' scrollPastEnd: false, diff --git a/browser/main/modals/PreferencesModal/SnippetEditor.js b/browser/main/modals/PreferencesModal/SnippetEditor.js index 4ce5dc34..ec3698c7 100644 --- a/browser/main/modals/PreferencesModal/SnippetEditor.js +++ b/browser/main/modals/PreferencesModal/SnippetEditor.js @@ -27,12 +27,17 @@ class SnippetEditor extends React.Component { dragDrop: false, foldGutter: true, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], - autoCloseBrackets: { - pairs: '()[]{}\'\'""$$**``', + autoCloseBrackets: (this.enableBracketMatching ? { + pairs: '()[]{}\'\'""$$**``ll', triples: '```"""\'\'\'', explode: '[]{}``$$', override: true - }, + }: { + pairs: '', + triples: '', + explode: '', + override: true + }), mode: 'null' }) this.cm.setSize('100%', '100%') diff --git a/browser/main/modals/PreferencesModal/SnippetTab.js b/browser/main/modals/PreferencesModal/SnippetTab.js index b83fa205..40b65d39 100644 --- a/browser/main/modals/PreferencesModal/SnippetTab.js +++ b/browser/main/modals/PreferencesModal/SnippetTab.js @@ -136,6 +136,7 @@ class SnippetTab extends React.Component { enableRulers={config.editor.enableRulers} rulers={config.editor.rulers} displayLineNumbers={config.editor.displayLineNumbers} + enableBracketMatching={config.editor.enableBracketMatching} scrollPastEnd={config.editor.scrollPastEnd} onRef={ref => { this.snippetEditor = ref }} />
diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index 6bc3b0a3..5c6110c3 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -62,7 +62,8 @@ class UiTab extends React.Component { checkHighLight.setAttribute('rel', 'stylesheet') document.head.appendChild(checkHighLight) } - + console.log("This is a console log") + const newConfig = { ui: { theme: this.refs.uiTheme.value, @@ -94,7 +95,8 @@ class UiTab extends React.Component { fetchUrlTitle: this.refs.editorFetchUrlTitle.checked, enableTableEditor: this.refs.enableTableEditor.checked, enableFrontMatterTitle: this.refs.enableFrontMatterTitle.checked, - frontMatterTitleField: this.refs.frontMatterTitleField.value + frontMatterTitleField: this.refs.frontMatterTitleField.value, + enableBracketMatching: this.refs.enableBracketMatching.checked }, preview: { fontSize: this.refs.previewFontSize.value, @@ -539,6 +541,17 @@ class UiTab extends React.Component { +
+ +
+
{i18n.__('Preview')}
From 59d31c9a182bacda1a0b5db2b4211dff6f875bc1 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 12:01:12 +0000 Subject: [PATCH 09/59] Deleted useless console log --- browser/main/modals/PreferencesModal/UiTab.js | 1 - 1 file changed, 1 deletion(-) diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index 5c6110c3..ab72c914 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -62,7 +62,6 @@ class UiTab extends React.Component { checkHighLight.setAttribute('rel', 'stylesheet') document.head.appendChild(checkHighLight) } - console.log("This is a console log") const newConfig = { ui: { From ab65fb7a5ca41b1bb3561d15b2d9259d64aea330 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 13:18:13 +0000 Subject: [PATCH 10/59] Choosing which characters to match and explode --- browser/components/CodeEditor.js | 993 ++++++++---------- browser/components/MarkdownEditor.js | 3 + browser/components/MarkdownSplitEditor.js | 3 + browser/main/Detail/SnippetNoteDetail.js | 3 + browser/main/lib/ConfigManager.js | 3 + .../modals/PreferencesModal/SnippetTab.js | 3 + browser/main/modals/PreferencesModal/UiTab.js | 48 +- 7 files changed, 516 insertions(+), 540 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index f4ce77b9..3e416ab2 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -1,121 +1,110 @@ +import 'codemirror-mode-elixir' + +import {Alignment, options, TableEditor} from '@susisu/mte-kernel' +import consts from 'browser/lib/consts' +import convertModeName from 'browser/lib/convertModeName' +import TextEditorInterface from 'browser/lib/TextEditorInterface' +import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' +import eventEmitter from 'browser/main/lib/eventEmitter' +import CodeMirror from 'codemirror' +import crypto from 'crypto' +import fs from 'fs' +import iconv from 'iconv-lite' +import _ from 'lodash' import PropTypes from 'prop-types' import React from 'react' -import _ from 'lodash' -import CodeMirror from 'codemirror' -import 'codemirror-mode-elixir' -import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' -import convertModeName from 'browser/lib/convertModeName' -import { - options, - TableEditor, - Alignment -} from '@susisu/mte-kernel' -import TextEditorInterface from 'browser/lib/TextEditorInterface' -import eventEmitter from 'browser/main/lib/eventEmitter' -import iconv from 'iconv-lite' -import crypto from 'crypto' -import consts from 'browser/lib/consts' -import fs from 'fs' -const { - ipcRenderer -} = require('electron') + +const {ipcRenderer} = require('electron') import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' import TurndownService from 'turndown' -import { - gfm -} from 'turndown-plugin-gfm' +import{gfm} from 'turndown-plugin-gfm' CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' const buildCMRulers = (rulers, enableRulers) => - (enableRulers ? rulers.map(ruler => ({ - column: ruler - })) : []) + (enableRulers ? rulers.map(ruler => ({column: ruler})) : []) export default class CodeEditor extends React.Component { constructor(props) { super(props) - this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, { - leading: false, - trailing: true - }) - this.changeHandler = e => this.handleChange(e) - this.focusHandler = () => { - ipcRenderer.send('editor:focused', true) - } - this.blurHandler = (editor, e) => { - ipcRenderer.send('editor:focused', false) - if (e == null) return null - let el = e.relatedTarget - while (el != null) { - if (el === this.refs.root) { - return - } - el = el.parentNode - } - this.props.onBlur != null && this.props.onBlur(e) + this.scrollHandler = + _.debounce( + this.handleScroll.bind(this), 100, + {leading: false, trailing: true}) this.changeHandler = e => + this.handleChange(e) this.focusHandler = + () => { + ipcRenderer.send('editor:focused', true) + } this.blurHandler = + (editor, e) => { + ipcRenderer.send('editor:focused', false) + if (e == null) return null + let el = e.relatedTarget + while (el != null) { + if (el === this.refs.root) { + return + } + el = el.parentNode + } + this.props.onBlur != null && this.props.onBlur(e) - const { - storageKey, - noteKey - } = this.props - attachmentManagement.deleteAttachmentsNotPresentInNote( - this.editor.getValue(), - storageKey, - noteKey - ) - } - this.pasteHandler = (editor, e) => this.handlePaste(editor, e) - this.loadStyleHandler = e => { - this.editor.refresh() - } - this.searchHandler = (e, msg) => this.handleSearch(msg) - this.searchState = null - this.scrollToLineHandeler = this.scrollToLine.bind(this) + const {storageKey, noteKey} = this.props + attachmentManagement.deleteAttachmentsNotPresentInNote( + this.editor.getValue(), storageKey, noteKey) + } this.pasteHandler = (editor, e) => + this.handlePaste(editor, e) this.loadStyleHandler = + e => { + this.editor.refresh() + } this.searchHandler = (e, msg) => + this.handleSearch(msg) this.searchState = + null this.scrollToLineHandeler = + this.scrollToLine + .bind(this) - this.formatTable = () => this.handleFormatTable() - this.editorActivityHandler = () => this.handleEditorActivity() + this.formatTable = () => + this.handleFormatTable() this + .editorActivityHandler = () => + this.handleEditorActivity() } handleSearch(msg) { const cm = this.editor const component = this - if (component.searchState) cm.removeOverlay(component.searchState) + if (component.searchState) + cm.removeOverlay(component.searchState) if (msg.length < 3) return - cm.operation(function () { - component.searchState = makeOverlay(msg, 'searching') - cm.addOverlay(component.searchState) + cm.operation(function() { + component.searchState = makeOverlay(msg, 'searching') + cm.addOverlay(component.searchState) - function makeOverlay(query, style) { - query = new RegExp( - query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), - 'gi' - ) - return { - token: function (stream) { - query.lastIndex = stream.pos - var match = query.exec(stream.string) - if (match && match.index === stream.pos) { - stream.pos += match[0].length || 1 - return style - } else if (match) { - stream.pos = match.index - } else { - stream.skipToEnd() + function makeOverlay(query, style) { + query = new RegExp( + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), + 'gi') + return { + token: function(stream) { + query.lastIndex = stream.pos + var match = query.exec(stream.string) + if (match && match.index === stream.pos) { + stream.pos += match[0].length || 1 + return style + } + else if (match) { + stream.pos = match.index + } + else { + stream.skipToEnd() + } + } } } - } - } - }) + }) } handleFormatTable() { - this.tableEditor.formatAll(options({ - textWidthOptions: {} - })) + this.tableEditor.formatAll(options({textWidthOptions: {}})) } handleEditorActivity() { @@ -128,23 +117,23 @@ export default class CodeEditor extends React.Component { const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions) if (active) { if (this.extraKeysMode !== 'editor') { - this.extraKeysMode = 'editor' - this.editor.setOption('extraKeys', this.editorKeyMap) + this.extraKeysMode = + 'editor' this.editor.setOption('extraKeys', this.editorKeyMap) } - } else { + } + else { if (this.extraKeysMode !== 'default') { - this.extraKeysMode = 'default' - this.editor.setOption('extraKeys', this.defaultKeyMap) - this.tableEditor.resetSmartCursor() + this.extraKeysMode = + 'default' this.editor + .setOption( + 'extraKeys', + this.defaultKeyMap) this.tableEditor.resetSmartCursor() } } } componentDidMount() { - const { - rulers, - enableRulers - } = this.props + const {rulers, enableRulers} = this.props const expandSnippet = this.expandSnippet.bind(this) eventEmitter.on('line:jump', this.scrollToLineHandeler) @@ -152,109 +141,99 @@ export default class CodeEditor extends React.Component { id: crypto.randomBytes(16).toString('hex'), name: 'Dummy text', prefix: ['lorem', 'ipsum'], - content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' - }] - if (!fs.existsSync(consts.SNIPPET_FILE)) { + content: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' + }] if (!fs.existsSync(consts.SNIPPET_FILE)) { fs.writeFileSync( - consts.SNIPPET_FILE, - JSON.stringify(defaultSnippet, null, 4), - 'utf8' - ) + consts.SNIPPET_FILE, JSON.stringify(defaultSnippet, null, 4), 'utf8') } - this.defaultKeyMap = CodeMirror.normalizeKeyMap({ - Tab: function (cm) { - const cursor = cm.getCursor() - const line = cm.getLine(cursor.line) - const cursorPosition = cursor.ch - const charBeforeCursor = line.substr(cursorPosition - 1, 1) - if (cm.somethingSelected()) cm.indentSelection('add') - else { - const tabs = cm.getOption('indentWithTabs') - if (line.trimLeft().match(/^(-|\*|\+) (\[( |x)] )?$/)) { - cm.execCommand('goLineStart') - if (tabs) { - cm.execCommand('insertTab') - } else { - cm.execCommand('insertSoftTab') - } - cm.execCommand('goLineEnd') - } else if ( - !charBeforeCursor.match(/\t|\s|\r|\n/) && - cursor.ch > 1 - ) { - // text expansion on tab key if the char before is alphabet - const snippets = JSON.parse( - fs.readFileSync(consts.SNIPPET_FILE, 'utf8') - ) - if (expandSnippet(line, cursor, cm, snippets) === false) { - if (tabs) { - cm.execCommand('insertTab') - } else { - cm.execCommand('insertSoftTab') + this.defaultKeyMap = + CodeMirror + .normalizeKeyMap({ + Tab: function(cm) { + const cursor = cm.getCursor() + const line = cm.getLine(cursor.line) + const cursorPosition = cursor.ch + const charBeforeCursor = line.substr(cursorPosition - 1, 1) + if (cm.somethingSelected()) cm.indentSelection('add') else { + const tabs = cm.getOption('indentWithTabs') + if (line.trimLeft().match(/^(-|\*|\+) (\[( |x)] )?$/)) { + cm.execCommand('goLineStart') + if (tabs) { + cm.execCommand('insertTab') + } + else {cm.execCommand('insertSoftTab')} cm.execCommand( + 'goLineEnd') + } + else if ( + !charBeforeCursor.match(/\t|\s|\r|\n/) && cursor.ch > 1) { + // text expansion on tab key if the char before is alphabet + const snippets = + JSON.parse(fs.readFileSync(consts.SNIPPET_FILE, 'utf8')) + if (expandSnippet(line, cursor, cm, snippets) === false) { + if (tabs) { + cm.execCommand('insertTab') + } else { + cm.execCommand('insertSoftTab') + } + } + } + else { + if (tabs) { + cm.execCommand('insertTab') + } else { + cm.execCommand('insertSoftTab') + } + } + } + }, + 'Cmd-T': function(cm) { + // Do nothing + }, + Enter: 'boostNewLineAndIndentContinueMarkdownList', + 'Ctrl-C': cm => { + if (cm.getOption('keyMap').substr(0, 3) === 'vim') { + document.execCommand('copy') + } + return CodeMirror.Pass } - } - } else { - if (tabs) { - cm.execCommand('insertTab') - } else { - cm.execCommand('insertSoftTab') - } - } - } - }, - 'Cmd-T': function (cm) { - // Do nothing - }, - Enter: 'boostNewLineAndIndentContinueMarkdownList', - 'Ctrl-C': cm => { - if (cm.getOption('keyMap').substr(0, 3) === 'vim') { - document.execCommand('copy') - } - return CodeMirror.Pass - } - }) + }) - this.value = this.props.value - this.editor = CodeMirror(this.refs.root, { - rulers: buildCMRulers(rulers, enableRulers), - value: this.props.value, - lineNumbers: this.props.displayLineNumbers, - lineWrapping: true, - theme: this.props.theme, - indentUnit: this.props.indentSize, - tabSize: this.props.indentSize, - indentWithTabs: this.props.indentType !== 'space', - keyMap: this.props.keyMap, - scrollPastEnd: this.props.scrollPastEnd, - inputStyle: 'textarea', - dragDrop: false, - foldGutter: true, - gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], - autoCloseBrackets: (this.props.enableBracketMatching ? { - pairs: '()[]{}\'\'""$$**``ll', - triples: '```"""\'\'\'', - explode: '[]{}``$$', - override: true - } : { - pairs: '', - triples: '', - explode: '', - override: true - }), - extraKeys: this.defaultKeyMap - }) + this.value = this.props.value this.editor = + CodeMirror(this.refs.root, { + rulers: buildCMRulers(rulers, enableRulers), + value: this.props.value, + lineNumbers: this.props.displayLineNumbers, + lineWrapping: true, + theme: this.props.theme, + indentUnit: this.props.indentSize, + tabSize: this.props.indentSize, + indentWithTabs: this.props.indentType !== 'space', + keyMap: this.props.keyMap, + scrollPastEnd: this.props.scrollPastEnd, + inputStyle: 'textarea', + dragDrop: false, + foldGutter: true, + gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], + autoCloseBrackets: { + pairs: this.props.matchingPairs, + triples: this.props.matchingTriples, + explode: this.props.explodingPairs, + override: true + }, + extraKeys: this.defaultKeyMap + }) - this.setMode(this.props.mode) + this.setMode(this.props.mode) - this.editor.on('focus', this.focusHandler) - this.editor.on('blur', this.blurHandler) - this.editor.on('change', this.changeHandler) - this.editor.on('paste', this.pasteHandler) + this.editor.on('focus', this.focusHandler) this.editor + .on('blur', this.blurHandler) this.editor + .on('change', this.changeHandler) this.editor.on( + 'paste', this.pasteHandler) eventEmitter.on('top:search', this.searchHandler) - eventEmitter.emit('code:init') - this.editor.on('scroll', this.scrollHandler) + eventEmitter.emit('code:init') this.editor.on('scroll', this.scrollHandler) const editorTheme = document.getElementById('editorTheme') editorTheme.addEventListener('load', this.loadStyleHandler) @@ -263,146 +242,151 @@ export default class CodeEditor extends React.Component { CodeMirror.Vim.defineEx('q!', 'q!', this.quitEditor) CodeMirror.Vim.defineEx('wq', 'wq', this.quitEditor) CodeMirror.Vim.defineEx('qw', 'qw', this.quitEditor) - CodeMirror.Vim.map('ZZ', ':q', 'normal') + CodeMirror.Vim + .map('ZZ', ':q', 'normal') - this.textEditorInterface = new TextEditorInterface(this.editor) - this.tableEditor = new TableEditor(this.textEditorInterface) - eventEmitter.on('code:format-table', this.formatTable) + this.textEditorInterface = + new TextEditorInterface(this.editor) this.tableEditor = + new TableEditor(this.textEditorInterface) + eventEmitter + .on('code:format-table', this.formatTable) - this.tableEditorOptions = options({ - smartCursor: true - }) + this.tableEditorOptions = options({smartCursor: true}) - this.editorKeyMap = CodeMirror.normalizeKeyMap({ - 'Tab': () => { - this.tableEditor.nextCell(this.tableEditorOptions) - }, - 'Shift-Tab': () => { - this.tableEditor.previousCell(this.tableEditorOptions) - }, - 'Enter': () => { - this.tableEditor.nextRow(this.tableEditorOptions) - }, - 'Ctrl-Enter': () => { - this.tableEditor.escape(this.tableEditorOptions) - }, - 'Cmd-Enter': () => { - this.tableEditor.escape(this.tableEditorOptions) - }, - 'Shift-Ctrl-Left': () => { - this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) - }, - 'Shift-Cmd-Left': () => { - this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) - }, - 'Shift-Ctrl-Right': () => { - this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) - }, - 'Shift-Cmd-Right': () => { - this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) - }, - 'Shift-Ctrl-Up': () => { - this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) - }, - 'Shift-Cmd-Up': () => { - this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) - }, - 'Shift-Ctrl-Down': () => { - this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) - }, - 'Shift-Cmd-Down': () => { - this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) - }, - 'Ctrl-Left': () => { - this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) - }, - 'Cmd-Left': () => { - this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) - }, - 'Ctrl-Right': () => { - this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) - }, - 'Cmd-Right': () => { - this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) - }, - 'Ctrl-Up': () => { - this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) - }, - 'Cmd-Up': () => { - this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) - }, - 'Ctrl-Down': () => { - this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) - }, - 'Cmd-Down': () => { - this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) - }, - 'Ctrl-K Ctrl-I': () => { - this.tableEditor.insertRow(this.tableEditorOptions) - }, - 'Cmd-K Cmd-I': () => { - this.tableEditor.insertRow(this.tableEditorOptions) - }, - 'Ctrl-L Ctrl-I': () => { - this.tableEditor.deleteRow(this.tableEditorOptions) - }, - 'Cmd-L Cmd-I': () => { - this.tableEditor.deleteRow(this.tableEditorOptions) - }, - 'Ctrl-K Ctrl-J': () => { - this.tableEditor.insertColumn(this.tableEditorOptions) - }, - 'Cmd-K Cmd-J': () => { - this.tableEditor.insertColumn(this.tableEditorOptions) - }, - 'Ctrl-L Ctrl-J': () => { - this.tableEditor.deleteColumn(this.tableEditorOptions) - }, - 'Cmd-L Cmd-J': () => { - this.tableEditor.deleteColumn(this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Left': () => { - this.tableEditor.moveColumn(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Left': () => { - this.tableEditor.moveColumn(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Right': () => { - this.tableEditor.moveColumn(1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Right': () => { - this.tableEditor.moveColumn(1, this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Up': () => { - this.tableEditor.moveRow(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Up': () => { - this.tableEditor.moveRow(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Down': () => { - this.tableEditor.moveRow(1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Down': () => { - this.tableEditor.moveRow(1, this.tableEditorOptions) - } - }) + this.editorKeyMap = + CodeMirror.normalizeKeyMap({ + 'Tab': () => { + this.tableEditor.nextCell(this.tableEditorOptions) + }, + 'Shift-Tab': () => { + this.tableEditor.previousCell(this.tableEditorOptions) + }, + 'Enter': () => { + this.tableEditor.nextRow(this.tableEditorOptions) + }, + 'Ctrl-Enter': () => { + this.tableEditor.escape(this.tableEditorOptions) + }, + 'Cmd-Enter': () => { + this.tableEditor.escape(this.tableEditorOptions) + }, + 'Shift-Ctrl-Left': () => { + this.tableEditor.alignColumn( + Alignment.LEFT, this.tableEditorOptions) + }, + 'Shift-Cmd-Left': () => { + this.tableEditor.alignColumn( + Alignment.LEFT, this.tableEditorOptions) + }, + 'Shift-Ctrl-Right': () => { + this.tableEditor.alignColumn( + Alignment.RIGHT, this.tableEditorOptions) + }, + 'Shift-Cmd-Right': () => { + this.tableEditor.alignColumn( + Alignment.RIGHT, this.tableEditorOptions) + }, + 'Shift-Ctrl-Up': () => { + this.tableEditor.alignColumn( + Alignment.CENTER, this.tableEditorOptions) + }, + 'Shift-Cmd-Up': () => { + this.tableEditor.alignColumn( + Alignment.CENTER, this.tableEditorOptions) + }, + 'Shift-Ctrl-Down': () => { + this.tableEditor.alignColumn( + Alignment.NONE, this.tableEditorOptions) + }, + 'Shift-Cmd-Down': () => { + this.tableEditor.alignColumn( + Alignment.NONE, this.tableEditorOptions) + }, + 'Ctrl-Left': () => { + this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) + }, + 'Cmd-Left': () => { + this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) + }, + 'Ctrl-Right': () => { + this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) + }, + 'Cmd-Right': () => { + this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) + }, + 'Ctrl-Up': () => { + this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) + }, + 'Cmd-Up': () => { + this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) + }, + 'Ctrl-Down': () => { + this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) + }, + 'Cmd-Down': () => { + this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) + }, + 'Ctrl-K Ctrl-I': () => { + this.tableEditor.insertRow(this.tableEditorOptions) + }, + 'Cmd-K Cmd-I': () => { + this.tableEditor.insertRow(this.tableEditorOptions) + }, + 'Ctrl-L Ctrl-I': () => { + this.tableEditor.deleteRow(this.tableEditorOptions) + }, + 'Cmd-L Cmd-I': () => { + this.tableEditor.deleteRow(this.tableEditorOptions) + }, + 'Ctrl-K Ctrl-J': () => { + this.tableEditor.insertColumn(this.tableEditorOptions) + }, + 'Cmd-K Cmd-J': () => { + this.tableEditor.insertColumn(this.tableEditorOptions) + }, + 'Ctrl-L Ctrl-J': () => { + this.tableEditor.deleteColumn(this.tableEditorOptions) + }, + 'Cmd-L Cmd-J': () => { + this.tableEditor.deleteColumn(this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Left': () => { + this.tableEditor.moveColumn(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Left': () => { + this.tableEditor.moveColumn(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Right': () => { + this.tableEditor.moveColumn(1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Right': () => { + this.tableEditor.moveColumn(1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Up': () => { + this.tableEditor.moveRow(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Up': () => { + this.tableEditor.moveRow(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Down': () => { + this.tableEditor.moveRow(1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Down': () => { + this.tableEditor.moveRow(1, this.tableEditorOptions) + } + }) if (this.props.enableTableEditor) { - this.editor.on('cursorActivity', this.editorActivityHandler) - this.editor.on('changes', this.editorActivityHandler) + this.editor.on('cursorActivity', this.editorActivityHandler) this.editor + .on('changes', this.editorActivityHandler) } - this.setState({ - clientWidth: this.refs.root.clientWidth - }) + this.setState({clientWidth: this.refs.root.clientWidth}) } expandSnippet(line, cursor, cm, snippets) { - const wordBeforeCursor = this.getWordBeforeCursor( - line, - cursor.line, - cursor.ch - ) + const wordBeforeCursor = + this.getWordBeforeCursor(line, cursor.line, cursor.ch) const templateCursorString = ':{}' for (let i = 0; i < snippets.length; i++) { if (snippets[i].prefix.indexOf(wordBeforeCursor.text) !== -1) { @@ -416,10 +400,8 @@ export default class CodeEditor extends React.Component { cursorLineNumber = j cursorLinePosition = cursorIndex cm.replaceRange( - snippets[i].content.replace(templateCursorString, ''), - wordBeforeCursor.range.from, - wordBeforeCursor.range.to - ) + snippets[i].content.replace(templateCursorString, ''), + wordBeforeCursor.range.from, wordBeforeCursor.range.to) cm.setCursor({ line: cursor.line + cursorLineNumber, ch: cursorLinePosition @@ -428,10 +410,8 @@ export default class CodeEditor extends React.Component { } } else { cm.replaceRange( - snippets[i].content, - wordBeforeCursor.range.from, - wordBeforeCursor.range.to - ) + snippets[i].content, wordBeforeCursor.range.from, + wordBeforeCursor.range.to) } return true } @@ -446,7 +426,8 @@ export default class CodeEditor extends React.Component { const emptyChars = /\t|\s|\r|\n/ // to prevent the word to expand is long that will crash the whole app - // the safeStop is there to stop user to expand words that longer than 20 chars + // the safeStop is there to stop user to expand words that longer than 20 + // chars const safeStop = 20 while (cursorPosition > 0) { @@ -454,25 +435,17 @@ export default class CodeEditor extends React.Component { // if char is not an empty char if (!emptyChars.test(currentChar)) { wordBeforeCursor = currentChar + wordBeforeCursor - } else if (wordBeforeCursor.length >= safeStop) { - throw new Error('Your snippet trigger is too long !') - } else { - break } - cursorPosition-- + else if (wordBeforeCursor.length >= safeStop) { + throw new Error('Your snippet trigger is too long !') + } + else {break} cursorPosition-- } return { - text: wordBeforeCursor, - range: { - from: { - line: lineNumber, - ch: originCursorPosition - }, - to: { - line: lineNumber, - ch: cursorPosition - } + text: wordBeforeCursor, range: { + from: {line: lineNumber, ch: originCursorPosition}, + to: {line: lineNumber, ch: cursorPosition} } } } @@ -482,12 +455,12 @@ export default class CodeEditor extends React.Component { } componentWillUnmount() { - this.editor.off('focus', this.focusHandler) - this.editor.off('blur', this.blurHandler) - this.editor.off('change', this.changeHandler) - this.editor.off('paste', this.pasteHandler) - eventEmitter.off('top:search', this.searchHandler) - this.editor.off('scroll', this.scrollHandler) + this.editor.off('focus', this.focusHandler) this.editor + .off('blur', this.blurHandler) this.editor + .off('change', this.changeHandler) this.editor.off( + 'paste', this.pasteHandler) + eventEmitter.off('top:search', this.searchHandler) this.editor.off( + 'scroll', this.scrollHandler) const editorTheme = document.getElementById('editorTheme') editorTheme.removeEventListener('load', this.loadStyleHandler) @@ -496,10 +469,7 @@ export default class CodeEditor extends React.Component { componentDidUpdate(prevProps, prevState) { let needRefresh = false - const { - rulers, - enableRulers - } = this.props + const {rulers, enableRulers} = this.props if (prevProps.mode !== this.props.mode) { this.setMode(this.props.mode) } @@ -517,16 +487,14 @@ export default class CodeEditor extends React.Component { needRefresh = true } - if ( - prevProps.enableRulers !== enableRulers || - prevProps.rulers !== rulers - ) { + if (prevProps.enableRulers !== enableRulers || + prevProps.rulers !== rulers) { this.editor.setOption('rulers', buildCMRulers(rulers, enableRulers)) } if (prevProps.indentSize !== this.props.indentSize) { - this.editor.setOption('indentUnit', this.props.indentSize) - this.editor.setOption('tabSize', this.props.indentSize) + this.editor.setOption('indentUnit', this.props.indentSize) this.editor + .setOption('tabSize', this.props.indentSize) } if (prevProps.indentType !== this.props.indentType) { this.editor.setOption('indentWithTabs', this.props.indentType !== 'space') @@ -540,38 +508,33 @@ export default class CodeEditor extends React.Component { this.editor.setOption('scrollPastEnd', this.props.scrollPastEnd) } - if (prevProps.enableBracketMatching !== this.props.enableBracketMatching) { - const bracketObject = (this.props.enableBracketMatching ? { - pairs: '()[]{}\'\'""$$**``ll', - triples: '```"""\'\'\'', - explode: '[]{}``$$', + if (prevProps.matchingPairs !== this.props.matchingPairs || + prevProps.matchingTriples !== this.props.matchingTriples || + prevProps.explodingPairs !== this.props.explodingPairs) { + const bracketObject = { + pairs: this.props.matchingPairs, + triples: this.props.matchingTriples, + explode: this.props.explodingPairs, override: true - } : { - pairs: '', - triples: '', - explode: '', - override: true - }); + } this.editor.setOption('autoCloseBrackets', bracketObject) } if (prevProps.enableTableEditor !== this.props.enableTableEditor) { if (this.props.enableTableEditor) { - this.editor.on('cursorActivity', this.editorActivityHandler) - this.editor.on('changes', this.editorActivityHandler) + this.editor.on('cursorActivity', this.editorActivityHandler) this.editor + .on('changes', this.editorActivityHandler) } else { - this.editor.off('cursorActivity', this.editorActivityHandler) - this.editor.off('changes', this.editorActivityHandler) + this.editor.off('cursorActivity', this.editorActivityHandler) this + .editor.off('changes', this.editorActivityHandler) } - this.extraKeysMode = 'default' - this.editor.setOption('extraKeys', this.defaultKeyMap) + this.extraKeysMode = + 'default' this.editor.setOption('extraKeys', this.defaultKeyMap) } if (this.state.clientWidth !== this.refs.root.clientWidth) { - this.setState({ - clientWidth: this.refs.root.clientWidth - }) + this.setState({clientWidth: this.refs.root.clientWidth}) needRefresh = true } @@ -583,9 +546,11 @@ export default class CodeEditor extends React.Component { setMode(mode) { let syntax = CodeMirror.findModeByName(convertModeName(mode)) - if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') + if (syntax == null) syntax = + CodeMirror + .findModeByName('Plain Text') - this.editor.setOption('mode', syntax.mime) + this.editor.setOption('mode', syntax.mime) CodeMirror.autoLoadMode(this.editor, syntax.mode) } @@ -599,11 +564,7 @@ export default class CodeEditor extends React.Component { moveCursorTo(row, col) {} scrollToLine(event, num) { - const cursor = { - line: num, - ch: 1 - } - this.editor.setCursor(cursor) + const cursor = { line: num, ch: 1 } this.editor.setCursor(cursor) } focus() { @@ -616,32 +577,22 @@ export default class CodeEditor extends React.Component { reload() { // Change event shouldn't be fired when switch note - this.editor.off('change', this.changeHandler) - this.value = this.props.value - this.editor.setValue(this.props.value) - this.editor.clearHistory() - this.editor.on('change', this.changeHandler) - this.editor.refresh() + this.editor.off('change', this.changeHandler) this.value = + this.props.value this.editor.setValue(this.props.value) this.editor + .clearHistory() this.editor.on('change', this.changeHandler) this + .editor.refresh() } setValue(value) { - const cursor = this.editor.getCursor() - this.editor.setValue(value) - this.editor.setCursor(cursor) + const cursor = this.editor.getCursor() this.editor.setValue(value) this + .editor.setCursor(cursor) } handleDropImage(dropEvent) { dropEvent.preventDefault() - const { - storageKey, - noteKey - } = this.props + const {storageKey, noteKey} = this.props attachmentManagement.handleAttachmentDrop( - this, - storageKey, - noteKey, - dropEvent - ) + this, storageKey, noteKey, dropEvent) } insertAttachmentMd(imageMd) { @@ -650,59 +601,43 @@ export default class CodeEditor extends React.Component { handlePaste(editor, e) { const clipboardData = e.clipboardData - const { - storageKey, - noteKey - } = this.props - const dataTransferItem = clipboardData.items[0] - const pastedTxt = clipboardData.getData('text') - const isURL = str => { - const matcher = /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/ - return matcher.test(str) - } - const isInLinkTag = editor => { - const startCursor = editor.getCursor('start') - const prevChar = editor.getRange({ - line: startCursor.line, - ch: startCursor.ch - 2 - }, { - line: startCursor.line, - ch: startCursor.ch - }) - const endCursor = editor.getCursor('end') - const nextChar = editor.getRange({ - line: endCursor.line, - ch: endCursor.ch - }, { - line: endCursor.line, - ch: endCursor.ch + 1 - }) - return prevChar === '](' && nextChar === ')' - } + const {storageKey, noteKey} = this.props + const dataTransferItem = clipboardData.items[0] const pastedTxt = + clipboardData.getData('text') + const isURL = + str => { + const matcher = + /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/ + return matcher.test(str) + } const isInLinkTag = + editor => { + const startCursor = editor.getCursor('start') + const prevChar = editor.getRange( + {line: startCursor.line, ch: startCursor.ch - 2}, + {line: startCursor.line, ch: startCursor.ch}) + const endCursor = editor.getCursor('end') + const nextChar = editor.getRange( + {line: endCursor.line, ch: endCursor.ch}, + {line: endCursor.line, ch: endCursor.ch + 1}) + return prevChar === '](' && nextChar === ')' + } const pastedHtml = clipboardData.getData('text/html') if (pastedHtml !== '') { this.handlePasteHtml(e, editor, pastedHtml) - } else if (dataTransferItem.type.match('image')) { + } + else if (dataTransferItem.type.match('image')) { attachmentManagement.handlePastImageEvent( - this, - storageKey, - noteKey, - dataTransferItem - ) - } else if ( - this.props.fetchUrlTitle && - isURL(pastedTxt) && - !isInLinkTag(editor) - ) { + this, storageKey, noteKey, dataTransferItem) + } + else if ( + this.props.fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) { this.handlePasteUrl(e, editor, pastedTxt) } if (attachmentManagement.isAttachmentLink(pastedTxt)) { attachmentManagement - .handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt) - .then(modifiedText => { - this.editor.replaceSelection(modifiedText) - }) + .handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt) + .then(modifiedText => {this.editor.replaceSelection(modifiedText)}) e.preventDefault() } } @@ -718,39 +653,36 @@ export default class CodeEditor extends React.Component { const taggedUrl = `<${pastedTxt}>` editor.replaceSelection(taggedUrl) - const isImageReponse = response => { - return ( - response.headers.has('content-type') && - response.headers.get('content-type').match(/^image\/.+$/) - ) - } - const replaceTaggedUrl = replacement => { - const value = editor.getValue() - const cursor = editor.getCursor() - const newValue = value.replace(taggedUrl, replacement) - const newCursor = Object.assign({}, cursor, { - ch: cursor.ch + newValue.length - value.length - }) - editor.setValue(newValue) - editor.setCursor(newCursor) - } + const isImageReponse = + response => { + return ( + response.headers.has('content-type') && + response.headers.get('content-type').match(/^image\/.+$/)) + } const replaceTaggedUrl = + replacement => { + const value = editor.getValue() + const cursor = editor.getCursor() + const newValue = value.replace(taggedUrl, replacement) + const newCursor = Object.assign( + {}, cursor, {ch: cursor.ch + newValue.length - value.length}) + editor.setValue(newValue) + editor.setCursor(newCursor) + } - fetch(pastedTxt, { - method: 'get' - }) - .then(response => { - if (isImageReponse(response)) { - return this.mapImageResponse(response, pastedTxt) - } else { - return this.mapNormalResponse(response, pastedTxt) - } - }) - .then(replacement => { - replaceTaggedUrl(replacement) - }) - .catch(e => { - replaceTaggedUrl(pastedTxt) - }) + fetch(pastedTxt, {method: 'get'}) + .then(response => { + if (isImageReponse(response)) { + return this.mapImageResponse( + response, pastedTxt) + } else { + return this.mapNormalResponse( + response, pastedTxt) + } + }) + .then( + replacement => { + replaceTaggedUrl(replacement)}) + .catch(e => {replaceTaggedUrl(pastedTxt)}) } handlePasteHtml(e, editor, pastedHtml) { @@ -760,23 +692,21 @@ export default class CodeEditor extends React.Component { } mapNormalResponse(response, pastedTxt) { - return this.decodeResponse(response).then(body => { - return new Promise((resolve, reject) => { - try { - const parsedBody = new window.DOMParser().parseFromString( - body, - 'text/html' - ) - const escapePipe = (str) => { - return str.replace('|', '\\|') + return this.decodeResponse(response).then( + body => {return new Promise((resolve, reject) => { + try { + const parsedBody = + new window.DOMParser().parseFromString(body, 'text/html') + const escapePipe = + (str) => { + return str.replace('|', '\\|') + } const linkWithTitle = + `[${escapePipe(parsedBody.title)}](${pastedTxt})` + resolve(linkWithTitle) + } catch (e) { + reject(e) } - const linkWithTitle = `[${escapePipe(parsedBody.title)}](${pastedTxt})` - resolve(linkWithTitle) - } catch (e) { - reject(e) - } - }) - }) + })}) } mapImageResponse(response, pastedTxt) { @@ -795,39 +725,30 @@ export default class CodeEditor extends React.Component { decodeResponse(response) { const headers = response.headers const _charset = headers.has('content-type') ? - this.extractContentTypeCharset(headers.get('content-type')) : - undefined - return response.arrayBuffer().then(buff => { - return new Promise((resolve, reject) => { - try { - const charset = _charset !== undefined && - iconv.encodingExists(_charset) ? - _charset : - 'utf-8' - resolve(iconv.decode(new Buffer(buff), charset).toString()) - } catch (e) { - reject(e) - } - }) - }) + this.extractContentTypeCharset(headers.get('content-type')) : + undefined + return response.arrayBuffer().then( + buff => {return new Promise((resolve, reject) => { + try { + const charset = + _charset !== undefined && iconv.encodingExists(_charset) ? + _charset : + 'utf-8' + resolve(iconv.decode(new Buffer(buff), charset).toString()) + } catch (e) { + reject(e) + } + })}) } extractContentTypeCharset(contentType) { - return contentType - .split(';') - .filter(str => { - return str.trim().toLowerCase().startsWith('charset') - }) - .map(str => { - return str.replace(/['"]/g, '').split('=')[1] - })[0] + return contentType.split(';') + .filter(str => {return str.trim().toLowerCase().startsWith('charset')}) + .map(str => {return str.replace(/['"]/g, '').split('=')[1]})[0] } render() { - const { - className, - fontSize - } = this.props + const {className, fontSize} = this.props const fontFamily = normalizeEditorFontFamily(this.props.fontFamily) const width = this.props.width return ( < @@ -835,18 +756,12 @@ export default class CodeEditor extends React.Component { className == null ? 'CodeEditor' : `CodeEditor ${className}` } ref = 'root' - tabIndex = '-1' - style = { - { - fontFamily, - fontSize: fontSize, - width: width - } - } - onDrop = { - e => this.handleDropImage(e) - } - /> + tabIndex = '-1' + style = { + { fontFamily, fontSize: fontSize, width: width } + } onDrop = { + e => this.handleDropImage(e) + } /> ) } } diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index f10ea8bc..dcb1b5ea 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -269,6 +269,9 @@ class MarkdownEditor extends React.Component { rulers={config.editor.rulers} displayLineNumbers={config.editor.displayLineNumbers} enableBracketMatching={config.editor.enableBracketMatching} + matchingPairs={config.editor.matchingPairs} + matchingTriples={config.editor.matchingTriples} + explodingPairs={config.editor.explodingPairs} scrollPastEnd={config.editor.scrollPastEnd} storageKey={storageKey} noteKey={noteKey} diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index 004a6eb0..689799c8 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -159,6 +159,9 @@ class MarkdownSplitEditor extends React.Component { fontSize={editorFontSize} displayLineNumbers={config.editor.displayLineNumbers} enableBracketMatching={config.editor.enableBracketMatching} + matchingPairs={config.editor.matchingPairs} + matchingTriples={config.editor.matchingTriples} + explodingPairs={config.editor.explodingPairs} indentType={config.editor.indentType} indentSize={editorIndentSize} enableRulers={config.editor.enableRulers} diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 90b8faa9..86ce2c7a 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -700,6 +700,9 @@ class SnippetNoteDetail extends React.Component { indentSize={editorIndentSize} displayLineNumbers={config.editor.displayLineNumbers} enableBracketMatching={config.editor.enableBracketMatching} + matchingPairs={config.editor.matchingPairs} + matchingTriples={config.editor.matchingTriples} + explodingPairs={config.editor.explodingPairs} keyMap={config.editor.keyMap} scrollPastEnd={config.editor.scrollPastEnd} fetchUrlTitle={config.editor.fetchUrlTitle} diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index eddf0909..c86e9288 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -45,6 +45,9 @@ export const DEFAULT_CONFIG = { rulers: [80, 120], displayLineNumbers: true, enableBracketMatching: true, + matchingPairs:'()[]{}\'\'""$$**``', + matchingTriples:'```"""\'\'\'', + explodingPairs:'[]{}``$$', switchPreview: 'BLUR', // 'BLUR', 'DBL_CLICK', 'RIGHTCLICK' delfaultStatus: 'PREVIEW', // 'PREVIEW', 'CODE' scrollPastEnd: false, diff --git a/browser/main/modals/PreferencesModal/SnippetTab.js b/browser/main/modals/PreferencesModal/SnippetTab.js index 40b65d39..ebd4012e 100644 --- a/browser/main/modals/PreferencesModal/SnippetTab.js +++ b/browser/main/modals/PreferencesModal/SnippetTab.js @@ -137,6 +137,9 @@ class SnippetTab extends React.Component { rulers={config.editor.rulers} displayLineNumbers={config.editor.displayLineNumbers} enableBracketMatching={config.editor.enableBracketMatching} + matchingPairs={config.editor.matchingPairs} + matchingTriples={config.editor.matchingTriples} + explodingPairs={config.editor.explodingPairs} scrollPastEnd={config.editor.scrollPastEnd} onRef={ref => { this.snippetEditor = ref }} />
diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index ab72c914..2bd0c9d0 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -95,7 +95,10 @@ class UiTab extends React.Component { enableTableEditor: this.refs.enableTableEditor.checked, enableFrontMatterTitle: this.refs.enableFrontMatterTitle.checked, frontMatterTitleField: this.refs.frontMatterTitleField.value, - enableBracketMatching: this.refs.enableBracketMatching.checked + enableBracketMatching: this.refs.enableBracketMatching.checked, + matchingPairs: this.refs.matchingPairs.value, + matchingTriples: this.refs.matchingTriples.value, + explodingPairs: this.refs.explodingPairs.value }, preview: { fontSize: this.refs.previewFontSize.value, @@ -551,6 +554,48 @@ class UiTab extends React.Component {
+
+
+ {i18n.__('Matching character pairs')} +
+
+ this.handleUIChange(e)} + type='text' + /> +
+
+ +
+
+ {i18n.__('Matching character triples')} +
+
+ this.handleUIChange(e)} + type='text' + /> +
+
+ +
+
+ {i18n.__('Exploding character pairs')} +
+
+ this.handleUIChange(e)} + type='text' + /> +
+
+
{i18n.__('Preview')}
@@ -578,6 +623,7 @@ class UiTab extends React.Component { />
+
{i18n.__('Code Block Theme')}
From 441c70b388e7505cdacb7286c6bd40676326a164 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 13:25:22 +0000 Subject: [PATCH 11/59] Removing checkmark and fixed Code Editor indentation --- browser/components/CodeEditor.js | 973 ++++++++++-------- browser/components/MarkdownEditor.js | 1 - browser/components/MarkdownSplitEditor.js | 1 - browser/main/Detail/SnippetNoteDetail.js | 1 - browser/main/lib/ConfigManager.js | 1 - .../modals/PreferencesModal/SnippetEditor.js | 15 +- .../modals/PreferencesModal/SnippetTab.js | 1 - browser/main/modals/PreferencesModal/UiTab.js | 12 - 8 files changed, 530 insertions(+), 475 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 3e416ab2..83f64a18 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -1,110 +1,121 @@ -import 'codemirror-mode-elixir' - -import {Alignment, options, TableEditor} from '@susisu/mte-kernel' -import consts from 'browser/lib/consts' -import convertModeName from 'browser/lib/convertModeName' -import TextEditorInterface from 'browser/lib/TextEditorInterface' -import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' -import eventEmitter from 'browser/main/lib/eventEmitter' -import CodeMirror from 'codemirror' -import crypto from 'crypto' -import fs from 'fs' -import iconv from 'iconv-lite' -import _ from 'lodash' import PropTypes from 'prop-types' import React from 'react' - -const {ipcRenderer} = require('electron') +import _ from 'lodash' +import CodeMirror from 'codemirror' +import 'codemirror-mode-elixir' +import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' +import convertModeName from 'browser/lib/convertModeName' +import { + options, + TableEditor, + Alignment +} from '@susisu/mte-kernel' +import TextEditorInterface from 'browser/lib/TextEditorInterface' +import eventEmitter from 'browser/main/lib/eventEmitter' +import iconv from 'iconv-lite' +import crypto from 'crypto' +import consts from 'browser/lib/consts' +import fs from 'fs' +const { + ipcRenderer +} = require('electron') import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' import TurndownService from 'turndown' -import{gfm} from 'turndown-plugin-gfm' +import { + gfm +} from 'turndown-plugin-gfm' CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' const buildCMRulers = (rulers, enableRulers) => - (enableRulers ? rulers.map(ruler => ({column: ruler})) : []) + (enableRulers ? rulers.map(ruler => ({ + column: ruler + })) : []) export default class CodeEditor extends React.Component { constructor(props) { super(props) - this.scrollHandler = - _.debounce( - this.handleScroll.bind(this), 100, - {leading: false, trailing: true}) this.changeHandler = e => - this.handleChange(e) this.focusHandler = - () => { - ipcRenderer.send('editor:focused', true) - } this.blurHandler = - (editor, e) => { - ipcRenderer.send('editor:focused', false) - if (e == null) return null - let el = e.relatedTarget - while (el != null) { - if (el === this.refs.root) { - return - } - el = el.parentNode - } - this.props.onBlur != null && this.props.onBlur(e) + this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, { + leading: false, + trailing: true + }) + this.changeHandler = e => this.handleChange(e) + this.focusHandler = () => { + ipcRenderer.send('editor:focused', true) + } + this.blurHandler = (editor, e) => { + ipcRenderer.send('editor:focused', false) + if (e == null) return null + let el = e.relatedTarget + while (el != null) { + if (el === this.refs.root) { + return + } + el = el.parentNode + } + this.props.onBlur != null && this.props.onBlur(e) - const {storageKey, noteKey} = this.props - attachmentManagement.deleteAttachmentsNotPresentInNote( - this.editor.getValue(), storageKey, noteKey) - } this.pasteHandler = (editor, e) => - this.handlePaste(editor, e) this.loadStyleHandler = - e => { - this.editor.refresh() - } this.searchHandler = (e, msg) => - this.handleSearch(msg) this.searchState = - null this.scrollToLineHandeler = - this.scrollToLine - .bind(this) + const { + storageKey, + noteKey + } = this.props + attachmentManagement.deleteAttachmentsNotPresentInNote( + this.editor.getValue(), + storageKey, + noteKey + ) + } + this.pasteHandler = (editor, e) => this.handlePaste(editor, e) + this.loadStyleHandler = e => { + this.editor.refresh() + } + this.searchHandler = (e, msg) => this.handleSearch(msg) + this.searchState = null + this.scrollToLineHandeler = this.scrollToLine.bind(this) - this.formatTable = () => - this.handleFormatTable() this - .editorActivityHandler = () => - this.handleEditorActivity() + this.formatTable = () => this.handleFormatTable() + this.editorActivityHandler = () => this.handleEditorActivity() } handleSearch(msg) { const cm = this.editor const component = this - if (component.searchState) - cm.removeOverlay(component.searchState) + if (component.searchState) cm.removeOverlay(component.searchState) if (msg.length < 3) return - cm.operation(function() { - component.searchState = makeOverlay(msg, 'searching') - cm.addOverlay(component.searchState) + cm.operation(function () { + component.searchState = makeOverlay(msg, 'searching') + cm.addOverlay(component.searchState) - function makeOverlay(query, style) { - query = new RegExp( - query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), - 'gi') - return { - token: function(stream) { - query.lastIndex = stream.pos - var match = query.exec(stream.string) - if (match && match.index === stream.pos) { - stream.pos += match[0].length || 1 - return style - } - else if (match) { - stream.pos = match.index - } - else { - stream.skipToEnd() - } - } + function makeOverlay(query, style) { + query = new RegExp( + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), + 'gi' + ) + return { + token: function (stream) { + query.lastIndex = stream.pos + var match = query.exec(stream.string) + if (match && match.index === stream.pos) { + stream.pos += match[0].length || 1 + return style + } else if (match) { + stream.pos = match.index + } else { + stream.skipToEnd() } } - }) + } + } + }) } handleFormatTable() { - this.tableEditor.formatAll(options({textWidthOptions: {}})) + this.tableEditor.formatAll(options({ + textWidthOptions: {} + })) } handleEditorActivity() { @@ -117,23 +128,23 @@ export default class CodeEditor extends React.Component { const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions) if (active) { if (this.extraKeysMode !== 'editor') { - this.extraKeysMode = - 'editor' this.editor.setOption('extraKeys', this.editorKeyMap) + this.extraKeysMode = 'editor' + this.editor.setOption('extraKeys', this.editorKeyMap) } - } - else { + } else { if (this.extraKeysMode !== 'default') { - this.extraKeysMode = - 'default' this.editor - .setOption( - 'extraKeys', - this.defaultKeyMap) this.tableEditor.resetSmartCursor() + this.extraKeysMode = 'default' + this.editor.setOption('extraKeys', this.defaultKeyMap) + this.tableEditor.resetSmartCursor() } } } componentDidMount() { - const {rulers, enableRulers} = this.props + const { + rulers, + enableRulers + } = this.props const expandSnippet = this.expandSnippet.bind(this) eventEmitter.on('line:jump', this.scrollToLineHandeler) @@ -141,99 +152,104 @@ export default class CodeEditor extends React.Component { id: crypto.randomBytes(16).toString('hex'), name: 'Dummy text', prefix: ['lorem', 'ipsum'], - content: - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' - }] if (!fs.existsSync(consts.SNIPPET_FILE)) { + content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' + }] + if (!fs.existsSync(consts.SNIPPET_FILE)) { fs.writeFileSync( - consts.SNIPPET_FILE, JSON.stringify(defaultSnippet, null, 4), 'utf8') + consts.SNIPPET_FILE, + JSON.stringify(defaultSnippet, null, 4), + 'utf8' + ) } - this.defaultKeyMap = - CodeMirror - .normalizeKeyMap({ - Tab: function(cm) { - const cursor = cm.getCursor() - const line = cm.getLine(cursor.line) - const cursorPosition = cursor.ch - const charBeforeCursor = line.substr(cursorPosition - 1, 1) - if (cm.somethingSelected()) cm.indentSelection('add') else { - const tabs = cm.getOption('indentWithTabs') - if (line.trimLeft().match(/^(-|\*|\+) (\[( |x)] )?$/)) { - cm.execCommand('goLineStart') - if (tabs) { - cm.execCommand('insertTab') - } - else {cm.execCommand('insertSoftTab')} cm.execCommand( - 'goLineEnd') - } - else if ( - !charBeforeCursor.match(/\t|\s|\r|\n/) && cursor.ch > 1) { - // text expansion on tab key if the char before is alphabet - const snippets = - JSON.parse(fs.readFileSync(consts.SNIPPET_FILE, 'utf8')) - if (expandSnippet(line, cursor, cm, snippets) === false) { - if (tabs) { - cm.execCommand('insertTab') - } else { - cm.execCommand('insertSoftTab') - } - } - } - else { - if (tabs) { - cm.execCommand('insertTab') - } else { - cm.execCommand('insertSoftTab') - } - } - } - }, - 'Cmd-T': function(cm) { - // Do nothing - }, - Enter: 'boostNewLineAndIndentContinueMarkdownList', - 'Ctrl-C': cm => { - if (cm.getOption('keyMap').substr(0, 3) === 'vim') { - document.execCommand('copy') - } - return CodeMirror.Pass + this.defaultKeyMap = CodeMirror.normalizeKeyMap({ + Tab: function (cm) { + const cursor = cm.getCursor() + const line = cm.getLine(cursor.line) + const cursorPosition = cursor.ch + const charBeforeCursor = line.substr(cursorPosition - 1, 1) + if (cm.somethingSelected()) cm.indentSelection('add') + else { + const tabs = cm.getOption('indentWithTabs') + if (line.trimLeft().match(/^(-|\*|\+) (\[( |x)] )?$/)) { + cm.execCommand('goLineStart') + if (tabs) { + cm.execCommand('insertTab') + } else { + cm.execCommand('insertSoftTab') + } + cm.execCommand('goLineEnd') + } else if ( + !charBeforeCursor.match(/\t|\s|\r|\n/) && + cursor.ch > 1 + ) { + // text expansion on tab key if the char before is alphabet + const snippets = JSON.parse( + fs.readFileSync(consts.SNIPPET_FILE, 'utf8') + ) + if (expandSnippet(line, cursor, cm, snippets) === false) { + if (tabs) { + cm.execCommand('insertTab') + } else { + cm.execCommand('insertSoftTab') } - }) + } + } else { + if (tabs) { + cm.execCommand('insertTab') + } else { + cm.execCommand('insertSoftTab') + } + } + } + }, + 'Cmd-T': function (cm) { + // Do nothing + }, + Enter: 'boostNewLineAndIndentContinueMarkdownList', + 'Ctrl-C': cm => { + if (cm.getOption('keyMap').substr(0, 3) === 'vim') { + document.execCommand('copy') + } + return CodeMirror.Pass + } + }) - this.value = this.props.value this.editor = - CodeMirror(this.refs.root, { - rulers: buildCMRulers(rulers, enableRulers), - value: this.props.value, - lineNumbers: this.props.displayLineNumbers, - lineWrapping: true, - theme: this.props.theme, - indentUnit: this.props.indentSize, - tabSize: this.props.indentSize, - indentWithTabs: this.props.indentType !== 'space', - keyMap: this.props.keyMap, - scrollPastEnd: this.props.scrollPastEnd, - inputStyle: 'textarea', - dragDrop: false, - foldGutter: true, - gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], - autoCloseBrackets: { - pairs: this.props.matchingPairs, - triples: this.props.matchingTriples, - explode: this.props.explodingPairs, - override: true - }, - extraKeys: this.defaultKeyMap - }) + this.value = this.props.value + this.editor = CodeMirror(this.refs.root, { + rulers: buildCMRulers(rulers, enableRulers), + value: this.props.value, + lineNumbers: this.props.displayLineNumbers, + lineWrapping: true, + theme: this.props.theme, + indentUnit: this.props.indentSize, + tabSize: this.props.indentSize, + indentWithTabs: this.props.indentType !== 'space', + keyMap: this.props.keyMap, + scrollPastEnd: this.props.scrollPastEnd, + inputStyle: 'textarea', + dragDrop: false, + foldGutter: true, + gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], + autoCloseBrackets: { + pairs: this.props.matchingPairs, + triples: this.props.matchingTriples, + explode: this.props.explodingPairs, + override: true + }, + extraKeys: this.defaultKeyMap + }) - this.setMode(this.props.mode) + this.setMode(this.props.mode) - this.editor.on('focus', this.focusHandler) this.editor - .on('blur', this.blurHandler) this.editor - .on('change', this.changeHandler) this.editor.on( - 'paste', this.pasteHandler) + this.editor.on('focus', this.focusHandler) + this.editor.on('blur', this.blurHandler) + this.editor.on('change', this.changeHandler) + this.editor.on('paste', this.pasteHandler) eventEmitter.on('top:search', this.searchHandler) - eventEmitter.emit('code:init') this.editor.on('scroll', this.scrollHandler) + eventEmitter.emit('code:init') + this.editor.on('scroll', this.scrollHandler) const editorTheme = document.getElementById('editorTheme') editorTheme.addEventListener('load', this.loadStyleHandler) @@ -242,151 +258,146 @@ export default class CodeEditor extends React.Component { CodeMirror.Vim.defineEx('q!', 'q!', this.quitEditor) CodeMirror.Vim.defineEx('wq', 'wq', this.quitEditor) CodeMirror.Vim.defineEx('qw', 'qw', this.quitEditor) - CodeMirror.Vim - .map('ZZ', ':q', 'normal') + CodeMirror.Vim.map('ZZ', ':q', 'normal') - this.textEditorInterface = - new TextEditorInterface(this.editor) this.tableEditor = - new TableEditor(this.textEditorInterface) - eventEmitter - .on('code:format-table', this.formatTable) + this.textEditorInterface = new TextEditorInterface(this.editor) + this.tableEditor = new TableEditor(this.textEditorInterface) + eventEmitter.on('code:format-table', this.formatTable) - this.tableEditorOptions = options({smartCursor: true}) + this.tableEditorOptions = options({ + smartCursor: true + }) - this.editorKeyMap = - CodeMirror.normalizeKeyMap({ - 'Tab': () => { - this.tableEditor.nextCell(this.tableEditorOptions) - }, - 'Shift-Tab': () => { - this.tableEditor.previousCell(this.tableEditorOptions) - }, - 'Enter': () => { - this.tableEditor.nextRow(this.tableEditorOptions) - }, - 'Ctrl-Enter': () => { - this.tableEditor.escape(this.tableEditorOptions) - }, - 'Cmd-Enter': () => { - this.tableEditor.escape(this.tableEditorOptions) - }, - 'Shift-Ctrl-Left': () => { - this.tableEditor.alignColumn( - Alignment.LEFT, this.tableEditorOptions) - }, - 'Shift-Cmd-Left': () => { - this.tableEditor.alignColumn( - Alignment.LEFT, this.tableEditorOptions) - }, - 'Shift-Ctrl-Right': () => { - this.tableEditor.alignColumn( - Alignment.RIGHT, this.tableEditorOptions) - }, - 'Shift-Cmd-Right': () => { - this.tableEditor.alignColumn( - Alignment.RIGHT, this.tableEditorOptions) - }, - 'Shift-Ctrl-Up': () => { - this.tableEditor.alignColumn( - Alignment.CENTER, this.tableEditorOptions) - }, - 'Shift-Cmd-Up': () => { - this.tableEditor.alignColumn( - Alignment.CENTER, this.tableEditorOptions) - }, - 'Shift-Ctrl-Down': () => { - this.tableEditor.alignColumn( - Alignment.NONE, this.tableEditorOptions) - }, - 'Shift-Cmd-Down': () => { - this.tableEditor.alignColumn( - Alignment.NONE, this.tableEditorOptions) - }, - 'Ctrl-Left': () => { - this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) - }, - 'Cmd-Left': () => { - this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) - }, - 'Ctrl-Right': () => { - this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) - }, - 'Cmd-Right': () => { - this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) - }, - 'Ctrl-Up': () => { - this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) - }, - 'Cmd-Up': () => { - this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) - }, - 'Ctrl-Down': () => { - this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) - }, - 'Cmd-Down': () => { - this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) - }, - 'Ctrl-K Ctrl-I': () => { - this.tableEditor.insertRow(this.tableEditorOptions) - }, - 'Cmd-K Cmd-I': () => { - this.tableEditor.insertRow(this.tableEditorOptions) - }, - 'Ctrl-L Ctrl-I': () => { - this.tableEditor.deleteRow(this.tableEditorOptions) - }, - 'Cmd-L Cmd-I': () => { - this.tableEditor.deleteRow(this.tableEditorOptions) - }, - 'Ctrl-K Ctrl-J': () => { - this.tableEditor.insertColumn(this.tableEditorOptions) - }, - 'Cmd-K Cmd-J': () => { - this.tableEditor.insertColumn(this.tableEditorOptions) - }, - 'Ctrl-L Ctrl-J': () => { - this.tableEditor.deleteColumn(this.tableEditorOptions) - }, - 'Cmd-L Cmd-J': () => { - this.tableEditor.deleteColumn(this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Left': () => { - this.tableEditor.moveColumn(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Left': () => { - this.tableEditor.moveColumn(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Right': () => { - this.tableEditor.moveColumn(1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Right': () => { - this.tableEditor.moveColumn(1, this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Up': () => { - this.tableEditor.moveRow(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Up': () => { - this.tableEditor.moveRow(-1, this.tableEditorOptions) - }, - 'Alt-Shift-Ctrl-Down': () => { - this.tableEditor.moveRow(1, this.tableEditorOptions) - }, - 'Alt-Shift-Cmd-Down': () => { - this.tableEditor.moveRow(1, this.tableEditorOptions) - } - }) + this.editorKeyMap = CodeMirror.normalizeKeyMap({ + 'Tab': () => { + this.tableEditor.nextCell(this.tableEditorOptions) + }, + 'Shift-Tab': () => { + this.tableEditor.previousCell(this.tableEditorOptions) + }, + 'Enter': () => { + this.tableEditor.nextRow(this.tableEditorOptions) + }, + 'Ctrl-Enter': () => { + this.tableEditor.escape(this.tableEditorOptions) + }, + 'Cmd-Enter': () => { + this.tableEditor.escape(this.tableEditorOptions) + }, + 'Shift-Ctrl-Left': () => { + this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) + }, + 'Shift-Cmd-Left': () => { + this.tableEditor.alignColumn(Alignment.LEFT, this.tableEditorOptions) + }, + 'Shift-Ctrl-Right': () => { + this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) + }, + 'Shift-Cmd-Right': () => { + this.tableEditor.alignColumn(Alignment.RIGHT, this.tableEditorOptions) + }, + 'Shift-Ctrl-Up': () => { + this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) + }, + 'Shift-Cmd-Up': () => { + this.tableEditor.alignColumn(Alignment.CENTER, this.tableEditorOptions) + }, + 'Shift-Ctrl-Down': () => { + this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) + }, + 'Shift-Cmd-Down': () => { + this.tableEditor.alignColumn(Alignment.NONE, this.tableEditorOptions) + }, + 'Ctrl-Left': () => { + this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) + }, + 'Cmd-Left': () => { + this.tableEditor.moveFocus(0, -1, this.tableEditorOptions) + }, + 'Ctrl-Right': () => { + this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) + }, + 'Cmd-Right': () => { + this.tableEditor.moveFocus(0, 1, this.tableEditorOptions) + }, + 'Ctrl-Up': () => { + this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) + }, + 'Cmd-Up': () => { + this.tableEditor.moveFocus(-1, 0, this.tableEditorOptions) + }, + 'Ctrl-Down': () => { + this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) + }, + 'Cmd-Down': () => { + this.tableEditor.moveFocus(1, 0, this.tableEditorOptions) + }, + 'Ctrl-K Ctrl-I': () => { + this.tableEditor.insertRow(this.tableEditorOptions) + }, + 'Cmd-K Cmd-I': () => { + this.tableEditor.insertRow(this.tableEditorOptions) + }, + 'Ctrl-L Ctrl-I': () => { + this.tableEditor.deleteRow(this.tableEditorOptions) + }, + 'Cmd-L Cmd-I': () => { + this.tableEditor.deleteRow(this.tableEditorOptions) + }, + 'Ctrl-K Ctrl-J': () => { + this.tableEditor.insertColumn(this.tableEditorOptions) + }, + 'Cmd-K Cmd-J': () => { + this.tableEditor.insertColumn(this.tableEditorOptions) + }, + 'Ctrl-L Ctrl-J': () => { + this.tableEditor.deleteColumn(this.tableEditorOptions) + }, + 'Cmd-L Cmd-J': () => { + this.tableEditor.deleteColumn(this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Left': () => { + this.tableEditor.moveColumn(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Left': () => { + this.tableEditor.moveColumn(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Right': () => { + this.tableEditor.moveColumn(1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Right': () => { + this.tableEditor.moveColumn(1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Up': () => { + this.tableEditor.moveRow(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Up': () => { + this.tableEditor.moveRow(-1, this.tableEditorOptions) + }, + 'Alt-Shift-Ctrl-Down': () => { + this.tableEditor.moveRow(1, this.tableEditorOptions) + }, + 'Alt-Shift-Cmd-Down': () => { + this.tableEditor.moveRow(1, this.tableEditorOptions) + } + }) if (this.props.enableTableEditor) { - this.editor.on('cursorActivity', this.editorActivityHandler) this.editor - .on('changes', this.editorActivityHandler) + this.editor.on('cursorActivity', this.editorActivityHandler) + this.editor.on('changes', this.editorActivityHandler) } - this.setState({clientWidth: this.refs.root.clientWidth}) + this.setState({ + clientWidth: this.refs.root.clientWidth + }) } expandSnippet(line, cursor, cm, snippets) { - const wordBeforeCursor = - this.getWordBeforeCursor(line, cursor.line, cursor.ch) + const wordBeforeCursor = this.getWordBeforeCursor( + line, + cursor.line, + cursor.ch + ) const templateCursorString = ':{}' for (let i = 0; i < snippets.length; i++) { if (snippets[i].prefix.indexOf(wordBeforeCursor.text) !== -1) { @@ -400,8 +411,10 @@ export default class CodeEditor extends React.Component { cursorLineNumber = j cursorLinePosition = cursorIndex cm.replaceRange( - snippets[i].content.replace(templateCursorString, ''), - wordBeforeCursor.range.from, wordBeforeCursor.range.to) + snippets[i].content.replace(templateCursorString, ''), + wordBeforeCursor.range.from, + wordBeforeCursor.range.to + ) cm.setCursor({ line: cursor.line + cursorLineNumber, ch: cursorLinePosition @@ -410,8 +423,10 @@ export default class CodeEditor extends React.Component { } } else { cm.replaceRange( - snippets[i].content, wordBeforeCursor.range.from, - wordBeforeCursor.range.to) + snippets[i].content, + wordBeforeCursor.range.from, + wordBeforeCursor.range.to + ) } return true } @@ -426,8 +441,7 @@ export default class CodeEditor extends React.Component { const emptyChars = /\t|\s|\r|\n/ // to prevent the word to expand is long that will crash the whole app - // the safeStop is there to stop user to expand words that longer than 20 - // chars + // the safeStop is there to stop user to expand words that longer than 20 chars const safeStop = 20 while (cursorPosition > 0) { @@ -435,17 +449,25 @@ export default class CodeEditor extends React.Component { // if char is not an empty char if (!emptyChars.test(currentChar)) { wordBeforeCursor = currentChar + wordBeforeCursor - } - else if (wordBeforeCursor.length >= safeStop) { + } else if (wordBeforeCursor.length >= safeStop) { throw new Error('Your snippet trigger is too long !') + } else { + break } - else {break} cursorPosition-- + cursorPosition-- } return { - text: wordBeforeCursor, range: { - from: {line: lineNumber, ch: originCursorPosition}, - to: {line: lineNumber, ch: cursorPosition} + text: wordBeforeCursor, + range: { + from: { + line: lineNumber, + ch: originCursorPosition + }, + to: { + line: lineNumber, + ch: cursorPosition + } } } } @@ -455,12 +477,12 @@ export default class CodeEditor extends React.Component { } componentWillUnmount() { - this.editor.off('focus', this.focusHandler) this.editor - .off('blur', this.blurHandler) this.editor - .off('change', this.changeHandler) this.editor.off( - 'paste', this.pasteHandler) - eventEmitter.off('top:search', this.searchHandler) this.editor.off( - 'scroll', this.scrollHandler) + this.editor.off('focus', this.focusHandler) + this.editor.off('blur', this.blurHandler) + this.editor.off('change', this.changeHandler) + this.editor.off('paste', this.pasteHandler) + eventEmitter.off('top:search', this.searchHandler) + this.editor.off('scroll', this.scrollHandler) const editorTheme = document.getElementById('editorTheme') editorTheme.removeEventListener('load', this.loadStyleHandler) @@ -469,7 +491,10 @@ export default class CodeEditor extends React.Component { componentDidUpdate(prevProps, prevState) { let needRefresh = false - const {rulers, enableRulers} = this.props + const { + rulers, + enableRulers + } = this.props if (prevProps.mode !== this.props.mode) { this.setMode(this.props.mode) } @@ -487,14 +512,16 @@ export default class CodeEditor extends React.Component { needRefresh = true } - if (prevProps.enableRulers !== enableRulers || - prevProps.rulers !== rulers) { + if ( + prevProps.enableRulers !== enableRulers || + prevProps.rulers !== rulers + ) { this.editor.setOption('rulers', buildCMRulers(rulers, enableRulers)) } if (prevProps.indentSize !== this.props.indentSize) { - this.editor.setOption('indentUnit', this.props.indentSize) this.editor - .setOption('tabSize', this.props.indentSize) + this.editor.setOption('indentUnit', this.props.indentSize) + this.editor.setOption('tabSize', this.props.indentSize) } if (prevProps.indentType !== this.props.indentType) { this.editor.setOption('indentWithTabs', this.props.indentType !== 'space') @@ -509,9 +536,9 @@ export default class CodeEditor extends React.Component { } if (prevProps.matchingPairs !== this.props.matchingPairs || - prevProps.matchingTriples !== this.props.matchingTriples || - prevProps.explodingPairs !== this.props.explodingPairs) { - const bracketObject = { + prevProps.matchingTriples !== this.props.matchingTriples || + prevProps.explodingPairs !== this.props.explodingPairs) { + const bracketObject ={ pairs: this.props.matchingPairs, triples: this.props.matchingTriples, explode: this.props.explodingPairs, @@ -522,19 +549,21 @@ export default class CodeEditor extends React.Component { if (prevProps.enableTableEditor !== this.props.enableTableEditor) { if (this.props.enableTableEditor) { - this.editor.on('cursorActivity', this.editorActivityHandler) this.editor - .on('changes', this.editorActivityHandler) + this.editor.on('cursorActivity', this.editorActivityHandler) + this.editor.on('changes', this.editorActivityHandler) } else { - this.editor.off('cursorActivity', this.editorActivityHandler) this - .editor.off('changes', this.editorActivityHandler) + this.editor.off('cursorActivity', this.editorActivityHandler) + this.editor.off('changes', this.editorActivityHandler) } - this.extraKeysMode = - 'default' this.editor.setOption('extraKeys', this.defaultKeyMap) + this.extraKeysMode = 'default' + this.editor.setOption('extraKeys', this.defaultKeyMap) } if (this.state.clientWidth !== this.refs.root.clientWidth) { - this.setState({clientWidth: this.refs.root.clientWidth}) + this.setState({ + clientWidth: this.refs.root.clientWidth + }) needRefresh = true } @@ -546,11 +575,9 @@ export default class CodeEditor extends React.Component { setMode(mode) { let syntax = CodeMirror.findModeByName(convertModeName(mode)) - if (syntax == null) syntax = - CodeMirror - .findModeByName('Plain Text') + if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') - this.editor.setOption('mode', syntax.mime) + this.editor.setOption('mode', syntax.mime) CodeMirror.autoLoadMode(this.editor, syntax.mode) } @@ -564,7 +591,11 @@ export default class CodeEditor extends React.Component { moveCursorTo(row, col) {} scrollToLine(event, num) { - const cursor = { line: num, ch: 1 } this.editor.setCursor(cursor) + const cursor = { + line: num, + ch: 1 + } + this.editor.setCursor(cursor) } focus() { @@ -577,22 +608,32 @@ export default class CodeEditor extends React.Component { reload() { // Change event shouldn't be fired when switch note - this.editor.off('change', this.changeHandler) this.value = - this.props.value this.editor.setValue(this.props.value) this.editor - .clearHistory() this.editor.on('change', this.changeHandler) this - .editor.refresh() + this.editor.off('change', this.changeHandler) + this.value = this.props.value + this.editor.setValue(this.props.value) + this.editor.clearHistory() + this.editor.on('change', this.changeHandler) + this.editor.refresh() } setValue(value) { - const cursor = this.editor.getCursor() this.editor.setValue(value) this - .editor.setCursor(cursor) + const cursor = this.editor.getCursor() + this.editor.setValue(value) + this.editor.setCursor(cursor) } handleDropImage(dropEvent) { dropEvent.preventDefault() - const {storageKey, noteKey} = this.props + const { + storageKey, + noteKey + } = this.props attachmentManagement.handleAttachmentDrop( - this, storageKey, noteKey, dropEvent) + this, + storageKey, + noteKey, + dropEvent + ) } insertAttachmentMd(imageMd) { @@ -601,43 +642,59 @@ export default class CodeEditor extends React.Component { handlePaste(editor, e) { const clipboardData = e.clipboardData - const {storageKey, noteKey} = this.props - const dataTransferItem = clipboardData.items[0] const pastedTxt = - clipboardData.getData('text') - const isURL = - str => { - const matcher = - /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/ - return matcher.test(str) - } const isInLinkTag = - editor => { - const startCursor = editor.getCursor('start') - const prevChar = editor.getRange( - {line: startCursor.line, ch: startCursor.ch - 2}, - {line: startCursor.line, ch: startCursor.ch}) - const endCursor = editor.getCursor('end') - const nextChar = editor.getRange( - {line: endCursor.line, ch: endCursor.ch}, - {line: endCursor.line, ch: endCursor.ch + 1}) - return prevChar === '](' && nextChar === ')' - } + const { + storageKey, + noteKey + } = this.props + const dataTransferItem = clipboardData.items[0] + const pastedTxt = clipboardData.getData('text') + const isURL = str => { + const matcher = /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/ + return matcher.test(str) + } + const isInLinkTag = editor => { + const startCursor = editor.getCursor('start') + const prevChar = editor.getRange({ + line: startCursor.line, + ch: startCursor.ch - 2 + }, { + line: startCursor.line, + ch: startCursor.ch + }) + const endCursor = editor.getCursor('end') + const nextChar = editor.getRange({ + line: endCursor.line, + ch: endCursor.ch + }, { + line: endCursor.line, + ch: endCursor.ch + 1 + }) + return prevChar === '](' && nextChar === ')' + } const pastedHtml = clipboardData.getData('text/html') if (pastedHtml !== '') { this.handlePasteHtml(e, editor, pastedHtml) - } - else if (dataTransferItem.type.match('image')) { + } else if (dataTransferItem.type.match('image')) { attachmentManagement.handlePastImageEvent( - this, storageKey, noteKey, dataTransferItem) - } - else if ( - this.props.fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) { + this, + storageKey, + noteKey, + dataTransferItem + ) + } else if ( + this.props.fetchUrlTitle && + isURL(pastedTxt) && + !isInLinkTag(editor) + ) { this.handlePasteUrl(e, editor, pastedTxt) } if (attachmentManagement.isAttachmentLink(pastedTxt)) { attachmentManagement - .handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt) - .then(modifiedText => {this.editor.replaceSelection(modifiedText)}) + .handleAttachmentLinkPaste(storageKey, noteKey, pastedTxt) + .then(modifiedText => { + this.editor.replaceSelection(modifiedText) + }) e.preventDefault() } } @@ -653,36 +710,39 @@ export default class CodeEditor extends React.Component { const taggedUrl = `<${pastedTxt}>` editor.replaceSelection(taggedUrl) - const isImageReponse = - response => { - return ( - response.headers.has('content-type') && - response.headers.get('content-type').match(/^image\/.+$/)) - } const replaceTaggedUrl = - replacement => { - const value = editor.getValue() - const cursor = editor.getCursor() - const newValue = value.replace(taggedUrl, replacement) - const newCursor = Object.assign( - {}, cursor, {ch: cursor.ch + newValue.length - value.length}) - editor.setValue(newValue) - editor.setCursor(newCursor) - } + const isImageReponse = response => { + return ( + response.headers.has('content-type') && + response.headers.get('content-type').match(/^image\/.+$/) + ) + } + const replaceTaggedUrl = replacement => { + const value = editor.getValue() + const cursor = editor.getCursor() + const newValue = value.replace(taggedUrl, replacement) + const newCursor = Object.assign({}, cursor, { + ch: cursor.ch + newValue.length - value.length + }) + editor.setValue(newValue) + editor.setCursor(newCursor) + } - fetch(pastedTxt, {method: 'get'}) - .then(response => { - if (isImageReponse(response)) { - return this.mapImageResponse( - response, pastedTxt) - } else { - return this.mapNormalResponse( - response, pastedTxt) - } - }) - .then( - replacement => { - replaceTaggedUrl(replacement)}) - .catch(e => {replaceTaggedUrl(pastedTxt)}) + fetch(pastedTxt, { + method: 'get' + }) + .then(response => { + if (isImageReponse(response)) { + return this.mapImageResponse(response, pastedTxt) + } else { + return this.mapNormalResponse(response, pastedTxt) + } + }) + .then(replacement => { + replaceTaggedUrl(replacement) + }) + .catch(e => { + replaceTaggedUrl(pastedTxt) + }) } handlePasteHtml(e, editor, pastedHtml) { @@ -692,21 +752,23 @@ export default class CodeEditor extends React.Component { } mapNormalResponse(response, pastedTxt) { - return this.decodeResponse(response).then( - body => {return new Promise((resolve, reject) => { - try { - const parsedBody = - new window.DOMParser().parseFromString(body, 'text/html') - const escapePipe = - (str) => { - return str.replace('|', '\\|') - } const linkWithTitle = - `[${escapePipe(parsedBody.title)}](${pastedTxt})` - resolve(linkWithTitle) - } catch (e) { - reject(e) + return this.decodeResponse(response).then(body => { + return new Promise((resolve, reject) => { + try { + const parsedBody = new window.DOMParser().parseFromString( + body, + 'text/html' + ) + const escapePipe = (str) => { + return str.replace('|', '\\|') } - })}) + const linkWithTitle = `[${escapePipe(parsedBody.title)}](${pastedTxt})` + resolve(linkWithTitle) + } catch (e) { + reject(e) + } + }) + }) } mapImageResponse(response, pastedTxt) { @@ -725,30 +787,39 @@ export default class CodeEditor extends React.Component { decodeResponse(response) { const headers = response.headers const _charset = headers.has('content-type') ? - this.extractContentTypeCharset(headers.get('content-type')) : - undefined - return response.arrayBuffer().then( - buff => {return new Promise((resolve, reject) => { - try { - const charset = - _charset !== undefined && iconv.encodingExists(_charset) ? - _charset : - 'utf-8' - resolve(iconv.decode(new Buffer(buff), charset).toString()) - } catch (e) { - reject(e) - } - })}) + this.extractContentTypeCharset(headers.get('content-type')) : + undefined + return response.arrayBuffer().then(buff => { + return new Promise((resolve, reject) => { + try { + const charset = _charset !== undefined && + iconv.encodingExists(_charset) ? + _charset : + 'utf-8' + resolve(iconv.decode(new Buffer(buff), charset).toString()) + } catch (e) { + reject(e) + } + }) + }) } extractContentTypeCharset(contentType) { - return contentType.split(';') - .filter(str => {return str.trim().toLowerCase().startsWith('charset')}) - .map(str => {return str.replace(/['"]/g, '').split('=')[1]})[0] + return contentType + .split(';') + .filter(str => { + return str.trim().toLowerCase().startsWith('charset') + }) + .map(str => { + return str.replace(/['"]/g, '').split('=')[1] + })[0] } render() { - const {className, fontSize} = this.props + const { + className, + fontSize + } = this.props const fontFamily = normalizeEditorFontFamily(this.props.fontFamily) const width = this.props.width return ( < @@ -756,12 +827,18 @@ export default class CodeEditor extends React.Component { className == null ? 'CodeEditor' : `CodeEditor ${className}` } ref = 'root' - tabIndex = '-1' - style = { - { fontFamily, fontSize: fontSize, width: width } - } onDrop = { - e => this.handleDropImage(e) - } /> + tabIndex = '-1' + style = { + { + fontFamily, + fontSize: fontSize, + width: width + } + } + onDrop = { + e => this.handleDropImage(e) + } + /> ) } } diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index dcb1b5ea..fcfc636e 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -268,7 +268,6 @@ class MarkdownEditor extends React.Component { enableRulers={config.editor.enableRulers} rulers={config.editor.rulers} displayLineNumbers={config.editor.displayLineNumbers} - enableBracketMatching={config.editor.enableBracketMatching} matchingPairs={config.editor.matchingPairs} matchingTriples={config.editor.matchingTriples} explodingPairs={config.editor.explodingPairs} diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index 689799c8..58867bac 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -158,7 +158,6 @@ class MarkdownSplitEditor extends React.Component { fontFamily={config.editor.fontFamily} fontSize={editorFontSize} displayLineNumbers={config.editor.displayLineNumbers} - enableBracketMatching={config.editor.enableBracketMatching} matchingPairs={config.editor.matchingPairs} matchingTriples={config.editor.matchingTriples} explodingPairs={config.editor.explodingPairs} diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 86ce2c7a..d252d274 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -699,7 +699,6 @@ class SnippetNoteDetail extends React.Component { indentType={config.editor.indentType} indentSize={editorIndentSize} displayLineNumbers={config.editor.displayLineNumbers} - enableBracketMatching={config.editor.enableBracketMatching} matchingPairs={config.editor.matchingPairs} matchingTriples={config.editor.matchingTriples} explodingPairs={config.editor.explodingPairs} diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index c86e9288..942ce93d 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -44,7 +44,6 @@ export const DEFAULT_CONFIG = { enableRulers: false, rulers: [80, 120], displayLineNumbers: true, - enableBracketMatching: true, matchingPairs:'()[]{}\'\'""$$**``', matchingTriples:'```"""\'\'\'', explodingPairs:'[]{}``$$', diff --git a/browser/main/modals/PreferencesModal/SnippetEditor.js b/browser/main/modals/PreferencesModal/SnippetEditor.js index ec3698c7..071f265f 100644 --- a/browser/main/modals/PreferencesModal/SnippetEditor.js +++ b/browser/main/modals/PreferencesModal/SnippetEditor.js @@ -27,17 +27,12 @@ class SnippetEditor extends React.Component { dragDrop: false, foldGutter: true, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], - autoCloseBrackets: (this.enableBracketMatching ? { - pairs: '()[]{}\'\'""$$**``ll', - triples: '```"""\'\'\'', - explode: '[]{}``$$', + autoCloseBrackets: { + pairs: this.props.matchingPairs, + triples: this.props.matchingTriples, + explode: this.props.explodingPairs, override: true - }: { - pairs: '', - triples: '', - explode: '', - override: true - }), + }, mode: 'null' }) this.cm.setSize('100%', '100%') diff --git a/browser/main/modals/PreferencesModal/SnippetTab.js b/browser/main/modals/PreferencesModal/SnippetTab.js index ebd4012e..5f5b0aac 100644 --- a/browser/main/modals/PreferencesModal/SnippetTab.js +++ b/browser/main/modals/PreferencesModal/SnippetTab.js @@ -136,7 +136,6 @@ class SnippetTab extends React.Component { enableRulers={config.editor.enableRulers} rulers={config.editor.rulers} displayLineNumbers={config.editor.displayLineNumbers} - enableBracketMatching={config.editor.enableBracketMatching} matchingPairs={config.editor.matchingPairs} matchingTriples={config.editor.matchingTriples} explodingPairs={config.editor.explodingPairs} diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index 2bd0c9d0..ac020238 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -95,7 +95,6 @@ class UiTab extends React.Component { enableTableEditor: this.refs.enableTableEditor.checked, enableFrontMatterTitle: this.refs.enableFrontMatterTitle.checked, frontMatterTitleField: this.refs.frontMatterTitleField.value, - enableBracketMatching: this.refs.enableBracketMatching.checked, matchingPairs: this.refs.matchingPairs.value, matchingTriples: this.refs.matchingTriples.value, explodingPairs: this.refs.explodingPairs.value @@ -543,17 +542,6 @@ class UiTab extends React.Component {
-
- -
-
{i18n.__('Matching character pairs')} From a46c51945973710be2159969256a05ea2db31ed3 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 14:03:21 +0000 Subject: [PATCH 12/59] Fixing indentations --- browser/components/CodeEditor.js | 104 +++++++++++++++---------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 83f64a18..9037c2e2 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -33,7 +33,7 @@ const buildCMRulers = (rulers, enableRulers) => })) : []) export default class CodeEditor extends React.Component { - constructor(props) { + constructor (props) { super(props) this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, { @@ -78,7 +78,7 @@ export default class CodeEditor extends React.Component { this.editorActivityHandler = () => this.handleEditorActivity() } - handleSearch(msg) { + handleSearch (msg) { const cm = this.editor const component = this @@ -89,7 +89,7 @@ export default class CodeEditor extends React.Component { component.searchState = makeOverlay(msg, 'searching') cm.addOverlay(component.searchState) - function makeOverlay(query, style) { + function makeOverlay (query, style) { query = new RegExp( query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), 'gi' @@ -112,19 +112,19 @@ export default class CodeEditor extends React.Component { }) } - handleFormatTable() { + handleFormatTable () { this.tableEditor.formatAll(options({ textWidthOptions: {} })) } - handleEditorActivity() { + handleEditorActivity () { if (!this.textEditorInterface.transaction) { this.updateTableEditorState() } } - updateTableEditorState() { + updateTableEditorState () { const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions) if (active) { if (this.extraKeysMode !== 'editor') { @@ -140,7 +140,7 @@ export default class CodeEditor extends React.Component { } } - componentDidMount() { + componentDidMount () { const { rulers, enableRulers @@ -392,7 +392,7 @@ export default class CodeEditor extends React.Component { }) } - expandSnippet(line, cursor, cm, snippets) { + expandSnippet (line, cursor, cm, snippets) { const wordBeforeCursor = this.getWordBeforeCursor( line, cursor.line, @@ -435,7 +435,7 @@ export default class CodeEditor extends React.Component { return false } - getWordBeforeCursor(line, lineNumber, cursorPosition) { + getWordBeforeCursor (line, lineNumber, cursorPosition) { let wordBeforeCursor = '' const originCursorPosition = cursorPosition const emptyChars = /\t|\s|\r|\n/ @@ -472,11 +472,11 @@ export default class CodeEditor extends React.Component { } } - quitEditor() { + quitEditor () { document.querySelector('textarea').blur() } - componentWillUnmount() { + componentWillUnmount () { this.editor.off('focus', this.focusHandler) this.editor.off('blur', this.blurHandler) this.editor.off('change', this.changeHandler) @@ -489,7 +489,7 @@ export default class CodeEditor extends React.Component { eventEmitter.off('code:format-table', this.formatTable) } - componentDidUpdate(prevProps, prevState) { + componentDidUpdate (prevProps, prevState) { let needRefresh = false const { rulers, @@ -538,7 +538,7 @@ export default class CodeEditor extends React.Component { if (prevProps.matchingPairs !== this.props.matchingPairs || prevProps.matchingTriples !== this.props.matchingTriples || prevProps.explodingPairs !== this.props.explodingPairs) { - const bracketObject ={ + const bracketObject = { pairs: this.props.matchingPairs, triples: this.props.matchingTriples, explode: this.props.explodingPairs, @@ -573,7 +573,7 @@ export default class CodeEditor extends React.Component { } } - setMode(mode) { + setMode (mode) { let syntax = CodeMirror.findModeByName(convertModeName(mode)) if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') @@ -581,16 +581,16 @@ export default class CodeEditor extends React.Component { CodeMirror.autoLoadMode(this.editor, syntax.mode) } - handleChange(e) { + handleChange (e) { this.value = this.editor.getValue() if (this.props.onChange) { this.props.onChange(e) } } - moveCursorTo(row, col) {} + moveCursorTo (row, col) {} - scrollToLine(event, num) { + scrollToLine (event, num) { const cursor = { line: num, ch: 1 @@ -598,15 +598,15 @@ export default class CodeEditor extends React.Component { this.editor.setCursor(cursor) } - focus() { + focus () { this.editor.focus() } - blur() { + blur () { this.editor.blur() } - reload() { + reload () { // Change event shouldn't be fired when switch note this.editor.off('change', this.changeHandler) this.value = this.props.value @@ -616,13 +616,13 @@ export default class CodeEditor extends React.Component { this.editor.refresh() } - setValue(value) { + setValue (value) { const cursor = this.editor.getCursor() this.editor.setValue(value) this.editor.setCursor(cursor) } - handleDropImage(dropEvent) { + handleDropImage (dropEvent) { dropEvent.preventDefault() const { storageKey, @@ -636,11 +636,11 @@ export default class CodeEditor extends React.Component { ) } - insertAttachmentMd(imageMd) { + insertAttachmentMd (imageMd) { this.editor.replaceSelection(imageMd) } - handlePaste(editor, e) { + handlePaste (editor, e) { const clipboardData = e.clipboardData const { storageKey, @@ -699,13 +699,13 @@ export default class CodeEditor extends React.Component { } } - handleScroll(e) { + handleScroll (e) { if (this.props.onScroll) { this.props.onScroll(e) } } - handlePasteUrl(e, editor, pastedTxt) { + handlePasteUrl (e, editor, pastedTxt) { e.preventDefault() const taggedUrl = `<${pastedTxt}>` editor.replaceSelection(taggedUrl) @@ -728,8 +728,8 @@ export default class CodeEditor extends React.Component { } fetch(pastedTxt, { - method: 'get' - }) + method: 'get' + }) .then(response => { if (isImageReponse(response)) { return this.mapImageResponse(response, pastedTxt) @@ -745,13 +745,13 @@ export default class CodeEditor extends React.Component { }) } - handlePasteHtml(e, editor, pastedHtml) { + handlePasteHtml (e, editor, pastedHtml) { e.preventDefault() const markdown = this.turndownService.turndown(pastedHtml) editor.replaceSelection(markdown) } - mapNormalResponse(response, pastedTxt) { + mapNormalResponse (response, pastedTxt) { return this.decodeResponse(response).then(body => { return new Promise((resolve, reject) => { try { @@ -771,7 +771,7 @@ export default class CodeEditor extends React.Component { }) } - mapImageResponse(response, pastedTxt) { + mapImageResponse (response, pastedTxt) { return new Promise((resolve, reject) => { try { const url = response.url @@ -784,18 +784,18 @@ export default class CodeEditor extends React.Component { }) } - decodeResponse(response) { + decodeResponse (response) { const headers = response.headers - const _charset = headers.has('content-type') ? - this.extractContentTypeCharset(headers.get('content-type')) : - undefined + const _charset = headers.has('content-type') + ? this.extractContentTypeCharset(headers.get('content-type')) + : undefined return response.arrayBuffer().then(buff => { return new Promise((resolve, reject) => { try { const charset = _charset !== undefined && - iconv.encodingExists(_charset) ? - _charset : - 'utf-8' + iconv.encodingExists(_charset) + ? _charset + : 'utf-8' resolve(iconv.decode(new Buffer(buff), charset).toString()) } catch (e) { reject(e) @@ -804,7 +804,7 @@ export default class CodeEditor extends React.Component { }) } - extractContentTypeCharset(contentType) { + extractContentTypeCharset (contentType) { return contentType .split(';') .filter(str => { @@ -815,27 +815,27 @@ export default class CodeEditor extends React.Component { })[0] } - render() { + render () { const { className, fontSize } = this.props const fontFamily = normalizeEditorFontFamily(this.props.fontFamily) const width = this.props.width - return ( < - div className = { + return (< + div className ={ className == null ? 'CodeEditor' : `CodeEditor ${className}` } - ref = 'root' - tabIndex = '-1' - style = { - { - fontFamily, - fontSize: fontSize, - width: width - } + ref= 'root' + tabIndex= '-1' + style= { + { + fontFamily, + fontSize: fontSize, + width: width } - onDrop = { + } + onDrop= { e => this.handleDropImage(e) } /> @@ -862,4 +862,4 @@ CodeEditor.defaultProps = { fontFamily: 'Monaco, Consolas', indentSize: 4, indentType: 'space' -} \ No newline at end of file +} From 8361106660fc78ef4cda5bc55cc04bf5e91c0ae6 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 14:07:35 +0000 Subject: [PATCH 13/59] Removing trailing spaces and added spaces in config --- browser/main/lib/ConfigManager.js | 6 +++--- browser/main/modals/PreferencesModal/UiTab.js | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index 942ce93d..7973d95f 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -44,9 +44,9 @@ export const DEFAULT_CONFIG = { enableRulers: false, rulers: [80, 120], displayLineNumbers: true, - matchingPairs:'()[]{}\'\'""$$**``', - matchingTriples:'```"""\'\'\'', - explodingPairs:'[]{}``$$', + matchingPairs: '()[]{}\'\'""$$**``', + matchingTriples: '```"""\'\'\'', + explodingPairs: '[]{}``$$', switchPreview: 'BLUR', // 'BLUR', 'DBL_CLICK', 'RIGHTCLICK' delfaultStatus: 'PREVIEW', // 'PREVIEW', 'CODE' scrollPastEnd: false, diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index ac020238..5b68d456 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -62,7 +62,7 @@ class UiTab extends React.Component { checkHighLight.setAttribute('rel', 'stylesheet') document.head.appendChild(checkHighLight) } - + const newConfig = { ui: { theme: this.refs.uiTheme.value, @@ -542,7 +542,7 @@ class UiTab extends React.Component {
-
+
{i18n.__('Matching character pairs')}
@@ -556,7 +556,7 @@ class UiTab extends React.Component {
-
+
{i18n.__('Matching character triples')}
@@ -570,7 +570,7 @@ class UiTab extends React.Component {
-
+
{i18n.__('Exploding character pairs')}
@@ -611,7 +611,7 @@ class UiTab extends React.Component { />
- +
{i18n.__('Code Block Theme')}
From 707dace3d065aaff5466d4a2f03d469a6e3f92a1 Mon Sep 17 00:00:00 2001 From: Guilherme Silva Date: Thu, 8 Nov 2018 14:11:43 +0000 Subject: [PATCH 14/59] removing spaces after = --- browser/components/CodeEditor.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 9037c2e2..98651798 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -823,19 +823,19 @@ export default class CodeEditor extends React.Component { const fontFamily = normalizeEditorFontFamily(this.props.fontFamily) const width = this.props.width return (< - div className ={ + div className={ className == null ? 'CodeEditor' : `CodeEditor ${className}` } - ref= 'root' - tabIndex= '-1' - style= { + ref='root' + tabIndex='-1' + style={ { fontFamily, fontSize: fontSize, width: width } } - onDrop= { + onDrop={ e => this.handleDropImage(e) } /> From b1d2c25ce50a3bf861f28e342f1b9970ea06c1c9 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 10 Nov 2018 00:05:45 +0100 Subject: [PATCH 15/59] when dropping an image, switch to editor and add it at the end of the file --- browser/components/MarkdownEditor.js | 24 ++++++++++++++++++++++++ browser/components/MarkdownPreview.js | 12 ++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 9c8a06d6..dbec1dc9 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -7,6 +7,7 @@ import MarkdownPreview from 'browser/components/MarkdownPreview' import eventEmitter from 'browser/main/lib/eventEmitter' import { findStorage } from 'browser/lib/findStorage' import ConfigManager from 'browser/main/lib/ConfigManager' +import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' class MarkdownEditor extends React.Component { constructor (props) { @@ -219,6 +220,28 @@ class MarkdownEditor extends React.Component { this.refs.code.editor.replaceSelection(`${mdElement}${this.refs.code.editor.getSelection()}${mdElement}`) } + handleDropImage (dropEvent) { + dropEvent.preventDefault() + const { storageKey, noteKey } = this.props + + this.setState({ + status: 'CODE' + }, () => { + this.refs.code.focus() + + this.refs.code.editor.execCommand('goDocEnd') + this.refs.code.editor.execCommand('goLineEnd') + this.refs.code.editor.execCommand('newlineAndIndent') + + attachmentManagement.handleAttachmentDrop( + this.refs.code, + storageKey, + noteKey, + dropEvent + ) + }) + } + handleKeyUp (e) { const keyPressed = this.state.keyPressed keyPressed.delete(e.keyCode) @@ -308,6 +331,7 @@ class MarkdownEditor extends React.Component { customCSS={config.preview.customCSS} allowCustomCSS={config.preview.allowCustomCSS} lineThroughCheckbox={config.preview.lineThroughCheckbox} + onDrop={(e) => this.handleDropImage(e)} />
) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index d9ff7074..704ed9a0 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -206,7 +206,7 @@ export default class MarkdownPreview extends React.Component { this.saveAsHtmlHandler = () => this.handleSaveAsHtml() this.printHandler = () => this.handlePrint() - this.linkClickHandler = this.handlelinkClick.bind(this) + this.linkClickHandler = this.handleLinkClick.bind(this) this.initMarkdown = this.initMarkdown.bind(this) this.initMarkdown() } @@ -438,6 +438,8 @@ export default class MarkdownPreview extends React.Component { } componentDidMount () { + const { onDrop } = this.props + this.refs.root.setAttribute('sandbox', 'allow-scripts') this.refs.root.contentWindow.document.body.addEventListener( 'contextmenu', @@ -475,7 +477,7 @@ export default class MarkdownPreview extends React.Component { ) this.refs.root.contentWindow.document.addEventListener( 'drop', - this.preventImageDroppedHandler + onDrop || this.preventImageDroppedHandler ) this.refs.root.contentWindow.document.addEventListener( 'dragover', @@ -492,6 +494,8 @@ export default class MarkdownPreview extends React.Component { } componentWillUnmount () { + const { onDrop } = this.props + this.refs.root.contentWindow.document.body.removeEventListener( 'contextmenu', this.contextMenuHandler @@ -510,7 +514,7 @@ export default class MarkdownPreview extends React.Component { ) this.refs.root.contentWindow.document.removeEventListener( 'drop', - this.preventImageDroppedHandler + onDrop || this.preventImageDroppedHandler ) this.refs.root.contentWindow.document.removeEventListener( 'dragover', @@ -837,7 +841,7 @@ export default class MarkdownPreview extends React.Component { return new window.Notification(title, options) } - handlelinkClick (e) { + handleLinkClick (e) { e.preventDefault() e.stopPropagation() From 3679fbe3ea36b4331e1f22559728ecb4a84863ad Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 10 Nov 2018 00:18:06 +0100 Subject: [PATCH 16/59] fix lint errors --- browser/components/MarkdownEditor.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index dbec1dc9..07d2aaf0 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -225,21 +225,21 @@ class MarkdownEditor extends React.Component { const { storageKey, noteKey } = this.props this.setState({ - status: 'CODE' - }, () => { - this.refs.code.focus() + status: 'CODE' + }, () => { + this.refs.code.focus() - this.refs.code.editor.execCommand('goDocEnd') - this.refs.code.editor.execCommand('goLineEnd') - this.refs.code.editor.execCommand('newlineAndIndent') + this.refs.code.editor.execCommand('goDocEnd') + this.refs.code.editor.execCommand('goLineEnd') + this.refs.code.editor.execCommand('newlineAndIndent') - attachmentManagement.handleAttachmentDrop( - this.refs.code, - storageKey, - noteKey, - dropEvent - ) - }) + attachmentManagement.handleAttachmentDrop( + this.refs.code, + storageKey, + noteKey, + dropEvent + ) + }) } handleKeyUp (e) { From 9d81e4be2fe810c18003a99425ac1ed42ef2f665 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 25 Nov 2018 16:17:01 +0100 Subject: [PATCH 17/59] undo change --- browser/main/lib/dataApi/attachmentManagement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index c88acfc5..59c91540 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -227,7 +227,7 @@ function migrateAttachments (markdownContent, storagePath, noteKey) { * @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths. */ function fixLocalURLS (renderedHTML, storagePath) { - return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:\\\/|%5C)[\\w.]+', 'g'), function (match) { + return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?', 'g'), function (match) { var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g') return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER)) }) From 64f7233bfc8124fc6d89e9c212718f3cdd4d02cb Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 25 Nov 2018 16:58:11 +0100 Subject: [PATCH 18/59] fix regex to match :storage reference, added comment to explain what it does. --- browser/main/lib/dataApi/attachmentManagement.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index 59c91540..73bf1303 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -227,7 +227,15 @@ function migrateAttachments (markdownContent, storagePath, noteKey) { * @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths. */ function fixLocalURLS (renderedHTML, storagePath) { - return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?', 'g'), function (match) { + /* + A :storage reference is like `:storage/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg`. + + - `STORAGE_FOLDER_PLACEHOLDER` will match `:storage` + - `(?:(?:\\\/|%5C)[\\w.]+)+` will match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg` + - `(?:\\\/|%5C)[\\w.]+` will either match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564` or `/f939b2c3.jpg` + - `(?:\\\/|%5C)` match the path seperator. `\\\/` for posix systems and `%5C` for windows. + */ + return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:(?:\\\/|%5C)[\\w.]+)+', 'g'), function (match) { var encodedPathSeparators = new RegExp(mdurl.encode(path.win32.sep) + '|' + mdurl.encode(path.posix.sep), 'g') return match.replace(encodedPathSeparators, path.sep).replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g'), 'file:///' + path.join(storagePath, DESTINATION_FOLDER)) }) From 45436f65afd66f607338bf042594ed15a147f5fa Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Fri, 30 Nov 2018 18:03:23 +0000 Subject: [PATCH 19/59] Issue #2469 almost done, missing refactor to reduce calls on code mirror --- browser/components/CodeEditor.js | 37 +++++++++++++++++++++-- browser/lib/newNote.js | 3 +- browser/main/Detail/SnippetNoteDetail.js | 6 +++- browser/main/lib/dataApi/createNote.js | 3 +- browser/main/lib/dataApi/updateNote.js | 6 ++-- browser/main/lib/dataApi/updateSnippet.js | 4 ++- 6 files changed, 51 insertions(+), 8 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 130cc86e..2ceca341 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -34,6 +34,7 @@ export default class CodeEditor extends React.Component { trailing: true }) this.changeHandler = (editor, changeObject) => this.handleChange(editor, changeObject) + this.highlightHandler = (editor, changeObject) => this.handleHighlight(editor, changeObject) this.focusHandler = () => { ipcRenderer.send('editor:focused', true) } @@ -208,12 +209,13 @@ export default class CodeEditor extends React.Component { } return CodeMirror.Pass } - }) + }) this.value = this.props.value this.editor = CodeMirror(this.refs.root, { rulers: buildCMRulers(rulers, enableRulers), value: this.props.value, + linesHighlighted:this.props.linesHighlighted, lineNumbers: this.props.displayLineNumbers, lineWrapping: true, theme: this.props.theme, @@ -240,6 +242,7 @@ export default class CodeEditor extends React.Component { this.editor.on('focus', this.focusHandler) this.editor.on('blur', this.blurHandler) this.editor.on('change', this.changeHandler) + this.editor.on("gutterClick",this.highlightHandler) this.editor.on('paste', this.pasteHandler) this.editor.on('contextmenu', this.contextMenuHandler) eventEmitter.on('top:search', this.searchHandler) @@ -316,9 +319,11 @@ export default class CodeEditor extends React.Component { this.setState({ clientWidth: this.refs.root.clientWidth }) + + this.initialHighlighting() } - expandSnippet (line, cursor, cm, snippets) { + expandSnippet (line, cursor, cm, snippets) { const wordBeforeCursor = this.getWordBeforeCursor( line, cursor.line, @@ -512,6 +517,19 @@ export default class CodeEditor extends React.Component { } } + handleHighlight (editor, changeObject) { + if(!editor.options.linesHighlighted.includes(changeObject)){ + editor.options.linesHighlighted.push(changeObject) + editor.addLineClass(changeObject,'text',"CodeMirror-activeline-background") + }else{ + editor.options.linesHighlighted.splice(editor.options.linesHighlighted.indexOf(changeObject),1) + editor.removeLineClass(changeObject,'text',"CodeMirror-activeline-background") + } + if (this.props.onChange) { + this.props.onChange(editor) + } + } + moveCursorTo (row, col) {} scrollToLine (event, num) { @@ -536,6 +554,7 @@ export default class CodeEditor extends React.Component { this.value = this.props.value this.editor.setValue(this.props.value) this.editor.clearHistory() + this.restartHighlighting() this.editor.on('change', this.changeHandler) this.editor.refresh() } @@ -546,6 +565,11 @@ export default class CodeEditor extends React.Component { this.editor.setCursor(cursor) } + restartHighlighting(){ + this.editor.options.linesHighlighted = this.props.linesHighlighted + this.initialHighlighting(); + } + handleDropImage (dropEvent) { dropEvent.preventDefault() const { storageKey, noteKey } = this.props @@ -683,6 +707,15 @@ export default class CodeEditor extends React.Component { }) } + initialHighlighting(){ + var count = this.editor.lineCount(), i; + for (i = 0; i < count; i++) { + if(this.editor.options.linesHighlighted.includes(i)){ + this.editor.addLineClass(i,'text',"CodeMirror-activeline-background") + } + } + } + mapImageResponse (response, pastedTxt) { return new Promise((resolve, reject) => { try { diff --git a/browser/lib/newNote.js b/browser/lib/newNote.js index 0b64d0e1..9b701a02 100644 --- a/browser/lib/newNote.js +++ b/browser/lib/newNote.js @@ -56,7 +56,8 @@ export function createSnippetNote (storage, folder, dispatch, location, params, { name: '', mode: config.editor.snippetDefaultLanguage || 'text', - content: '' + content: '', + linesHighlighted:[], } ] }) diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 4a38ffe5..b8d0fe5b 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -410,6 +410,8 @@ class SnippetNoteDetail extends React.Component { return (e) => { const snippets = this.state.note.snippets.slice() snippets[index].content = this.refs['code-' + index].value + snippets[index].linesHighlighted=e.options.linesHighlighted + this.setState(state => ({note: Object.assign(state.note, {snippets: snippets})})) this.setState(state => ({ note: state.note @@ -602,7 +604,8 @@ class SnippetNoteDetail extends React.Component { note.snippets = note.snippets.concat([{ name: '', mode: config.editor.snippetDefaultLanguage || 'text', - content: '' + content: '', + linesHighlighted:[] }]) const snippetIndex = note.snippets.length - 1 @@ -705,6 +708,7 @@ class SnippetNoteDetail extends React.Component { : { if (err) reject(err) resolve(snippets) From 1668ef6bb4aba35258488a9fa9984e00952d90e8 Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Fri, 30 Nov 2018 18:32:14 +0000 Subject: [PATCH 20/59] Small changes --- browser/components/CodeEditor.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 2ceca341..adcb6bf0 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -518,11 +518,12 @@ export default class CodeEditor extends React.Component { } handleHighlight (editor, changeObject) { - if(!editor.options.linesHighlighted.includes(changeObject)){ - editor.options.linesHighlighted.push(changeObject) + let lines =editor.options.linesHighlighted + if(!lines.includes(changeObject)){ + lines.push(changeObject) editor.addLineClass(changeObject,'text',"CodeMirror-activeline-background") }else{ - editor.options.linesHighlighted.splice(editor.options.linesHighlighted.indexOf(changeObject),1) + lines.splice(lines.indexOf(changeObject),1) editor.removeLineClass(changeObject,'text',"CodeMirror-activeline-background") } if (this.props.onChange) { @@ -567,7 +568,7 @@ export default class CodeEditor extends React.Component { restartHighlighting(){ this.editor.options.linesHighlighted = this.props.linesHighlighted - this.initialHighlighting(); + this.initialHighlighting() } handleDropImage (dropEvent) { @@ -708,7 +709,7 @@ export default class CodeEditor extends React.Component { } initialHighlighting(){ - var count = this.editor.lineCount(), i; + var count = this.editor.lineCount(), i for (i = 0; i < count; i++) { if(this.editor.options.linesHighlighted.includes(i)){ this.editor.addLineClass(i,'text',"CodeMirror-activeline-background") From a9442a019f9263dc3fddc038dda055fdbb781707 Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Fri, 30 Nov 2018 19:20:40 +0000 Subject: [PATCH 21/59] Changes to pass tests and lint code --- browser/components/CodeEditor.js | 34 +++++++++++------------ browser/lib/newNote.js | 2 +- browser/main/Detail/SnippetNoteDetail.js | 4 +-- browser/main/lib/dataApi/createNote.js | 2 +- browser/main/lib/dataApi/updateNote.js | 4 +-- browser/main/lib/dataApi/updateSnippet.js | 4 +-- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index adcb6bf0..8af89ded 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -209,13 +209,13 @@ export default class CodeEditor extends React.Component { } return CodeMirror.Pass } - }) + }) this.value = this.props.value this.editor = CodeMirror(this.refs.root, { rulers: buildCMRulers(rulers, enableRulers), value: this.props.value, - linesHighlighted:this.props.linesHighlighted, + linesHighlighted: this.props.linesHighlighted, lineNumbers: this.props.displayLineNumbers, lineWrapping: true, theme: this.props.theme, @@ -242,7 +242,7 @@ export default class CodeEditor extends React.Component { this.editor.on('focus', this.focusHandler) this.editor.on('blur', this.blurHandler) this.editor.on('change', this.changeHandler) - this.editor.on("gutterClick",this.highlightHandler) + this.editor.on('gutterClick', this.highlightHandler) this.editor.on('paste', this.pasteHandler) this.editor.on('contextmenu', this.contextMenuHandler) eventEmitter.on('top:search', this.searchHandler) @@ -320,10 +320,10 @@ export default class CodeEditor extends React.Component { clientWidth: this.refs.root.clientWidth }) - this.initialHighlighting() + this.initialHighlighting() } - expandSnippet (line, cursor, cm, snippets) { + expandSnippet (line, cursor, cm, snippets) { const wordBeforeCursor = this.getWordBeforeCursor( line, cursor.line, @@ -518,13 +518,13 @@ export default class CodeEditor extends React.Component { } handleHighlight (editor, changeObject) { - let lines =editor.options.linesHighlighted - if(!lines.includes(changeObject)){ + const lines = editor.options.linesHighlighted + if (!lines.includes(changeObject)) { lines.push(changeObject) - editor.addLineClass(changeObject,'text',"CodeMirror-activeline-background") - }else{ - lines.splice(lines.indexOf(changeObject),1) - editor.removeLineClass(changeObject,'text',"CodeMirror-activeline-background") + editor.addLineClass(changeObject, 'text', 'CodeMirror-activeline-background') + } else { + lines.splice(lines.indexOf(changeObject), 1) + editor.removeLineClass(changeObject, 'text', 'CodeMirror-activeline-background') } if (this.props.onChange) { this.props.onChange(editor) @@ -566,7 +566,7 @@ export default class CodeEditor extends React.Component { this.editor.setCursor(cursor) } - restartHighlighting(){ + restartHighlighting () { this.editor.options.linesHighlighted = this.props.linesHighlighted this.initialHighlighting() } @@ -708,11 +708,11 @@ export default class CodeEditor extends React.Component { }) } - initialHighlighting(){ - var count = this.editor.lineCount(), i - for (i = 0; i < count; i++) { - if(this.editor.options.linesHighlighted.includes(i)){ - this.editor.addLineClass(i,'text',"CodeMirror-activeline-background") + initialHighlighting () { + const count = this.editor.lineCount() + for (let i = 0; i < count; i++) { + if (this.editor.options.linesHighlighted.includes(i)) { + this.editor.addLineClass(i, 'text', 'CodeMirror-activeline-background') } } } diff --git a/browser/lib/newNote.js b/browser/lib/newNote.js index 9b701a02..2e407207 100644 --- a/browser/lib/newNote.js +++ b/browser/lib/newNote.js @@ -57,7 +57,7 @@ export function createSnippetNote (storage, folder, dispatch, location, params, name: '', mode: config.editor.snippetDefaultLanguage || 'text', content: '', - linesHighlighted:[], + linesHighlighted: [] } ] }) diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index b8d0fe5b..3d1027ea 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -410,7 +410,7 @@ class SnippetNoteDetail extends React.Component { return (e) => { const snippets = this.state.note.snippets.slice() snippets[index].content = this.refs['code-' + index].value - snippets[index].linesHighlighted=e.options.linesHighlighted + snippets[index].linesHighlighted = e.options.linesHighlighted this.setState(state => ({note: Object.assign(state.note, {snippets: snippets})})) this.setState(state => ({ @@ -605,7 +605,7 @@ class SnippetNoteDetail extends React.Component { name: '', mode: config.editor.snippetDefaultLanguage || 'text', content: '', - linesHighlighted:[] + linesHighlighted: [] }]) const snippetIndex = note.snippets.length - 1 diff --git a/browser/main/lib/dataApi/createNote.js b/browser/main/lib/dataApi/createNote.js index 60bf1f42..0b6e6fbe 100644 --- a/browser/main/lib/dataApi/createNote.js +++ b/browser/main/lib/dataApi/createNote.js @@ -24,7 +24,7 @@ function validateInput (input) { name: '', mode: 'text', content: '', - linesHighlighted:[], + linesHighlighted: [] }] } break diff --git a/browser/main/lib/dataApi/updateNote.js b/browser/main/lib/dataApi/updateNote.js index 1c8bd882..b7a28613 100644 --- a/browser/main/lib/dataApi/updateNote.js +++ b/browser/main/lib/dataApi/updateNote.js @@ -52,7 +52,7 @@ function validateInput (input) { name: '', mode: 'text', content: '', - linesHighlighted:[], + linesHighlighted: [] }] } else { validatedInput.snippets = input.snippets @@ -98,7 +98,7 @@ function updateNote (storageKey, noteKey, input) { name: '', mode: 'text', content: '', - linesHighlighted:[], + linesHighlighted: [] }] } : { diff --git a/browser/main/lib/dataApi/updateSnippet.js b/browser/main/lib/dataApi/updateSnippet.js index 7a296130..f132d83f 100644 --- a/browser/main/lib/dataApi/updateSnippet.js +++ b/browser/main/lib/dataApi/updateSnippet.js @@ -12,8 +12,8 @@ function updateSnippet (snippet, snippetFile) { if ( currentSnippet.name === snippet.name && currentSnippet.prefix === snippet.prefix && - currentSnippet.content === snippet.content && - currentSnippet.linesHighlighted===snippet.linesHighlighted + currentSnippet.content === snippet.content && + currentSnippet.linesHighlighted === snippet.linesHighlighted ) { // if everything is the same then don't write to disk resolve(snippets) From 1a0e15e04ceac7e793f2791302484b5929610965 Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Wed, 5 Dec 2018 14:12:29 +0000 Subject: [PATCH 22/59] Fixed bug when switching to markdown notes --- browser/components/CodeEditor.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 8af89ded..d889a0a7 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -519,6 +519,11 @@ export default class CodeEditor extends React.Component { handleHighlight (editor, changeObject) { const lines = editor.options.linesHighlighted + + if (lines == null) { + return + } + if (!lines.includes(changeObject)) { lines.push(changeObject) editor.addLineClass(changeObject, 'text', 'CodeMirror-activeline-background') @@ -566,11 +571,6 @@ export default class CodeEditor extends React.Component { this.editor.setCursor(cursor) } - restartHighlighting () { - this.editor.options.linesHighlighted = this.props.linesHighlighted - this.initialHighlighting() - } - handleDropImage (dropEvent) { dropEvent.preventDefault() const { storageKey, noteKey } = this.props @@ -709,6 +709,10 @@ export default class CodeEditor extends React.Component { } initialHighlighting () { + if (this.editor.options.linesHighlighted == null) { + return + } + const count = this.editor.lineCount() for (let i = 0; i < count; i++) { if (this.editor.options.linesHighlighted.includes(i)) { @@ -717,6 +721,11 @@ export default class CodeEditor extends React.Component { } } + restartHighlighting () { + this.editor.options.linesHighlighted = this.props.linesHighlighted + this.initialHighlighting() + } + mapImageResponse (response, pastedTxt) { return new Promise((resolve, reject) => { try { From b5604ba0a9db506aa2537f4963d312ef8cad4899 Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Thu, 6 Dec 2018 13:23:23 +0000 Subject: [PATCH 23/59] Changes to optimize initial highlighting Now iterates over highlighted lines instead of all lines of the snippet --- browser/components/CodeEditor.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index d889a0a7..4027b262 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -713,11 +713,16 @@ export default class CodeEditor extends React.Component { return } - const count = this.editor.lineCount() - for (let i = 0; i < count; i++) { - if (this.editor.options.linesHighlighted.includes(i)) { - this.editor.addLineClass(i, 'text', 'CodeMirror-activeline-background') + const totalHighlightedLines = this.editor.options.linesHighlighted.length + const totalAvailableLines = this.editor.lineCount() + + for (let i = 0; i < totalHighlightedLines; i++) { + const lineNumber = this.editor.options.linesHighlighted[i] + if (lineNumber > totalAvailableLines) { + // make sure that we skip the invalid lines althrough this case should not be happened. + continue } + this.editor.addLineClass(lineNumber, 'text', 'CodeMirror-activeline-background') } } From 191295b6de2090a5cd8bdddf1825a04cadba6d05 Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Fri, 7 Dec 2018 23:41:51 +0000 Subject: [PATCH 24/59] Added array of linesHighlighted to default snippet This makes the default snippet also handle highlight on the lines, because this snippet is created in the code without the normal snippet constructor --- browser/main/Main.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/browser/main/Main.js b/browser/main/Main.js index c426f2bd..d13bde0d 100644 --- a/browser/main/Main.js +++ b/browser/main/Main.js @@ -96,12 +96,14 @@ class Main extends React.Component { { name: 'example.html', mode: 'html', - content: "\n\n

Enjoy Boostnote!

\n\n" + content: "\n\n

Enjoy Boostnote!

\n\n", + linesHighlighted:[] }, { name: 'example.js', mode: 'javascript', - content: "var boostnote = document.getElementById('enjoy').innerHTML\n\nconsole.log(boostnote)" + content: "var boostnote = document.getElementById('enjoy').innerHTML\n\nconsole.log(boostnote)", + linesHighlighted:[] } ] }) From f483f8fdf04f64b591ef547caf7eb5f4759e262d Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Sat, 8 Dec 2018 12:07:28 +0000 Subject: [PATCH 25/59] Fix so that linesHighlighted defaults to [] when does't find it --- browser/main/lib/dataApi/updateNote.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/browser/main/lib/dataApi/updateNote.js b/browser/main/lib/dataApi/updateNote.js index b7a28613..c89bad54 100644 --- a/browser/main/lib/dataApi/updateNote.js +++ b/browser/main/lib/dataApi/updateNote.js @@ -118,6 +118,8 @@ function updateNote (storageKey, noteKey, input) { if (noteData.type === 'SNIPPET_NOTE') { noteData.title + if(noteData.linesHighlighted = null) + noteData.linesHighlighted = []; } Object.assign(noteData, input, { From 492294e11dc7522427542230af6e8a107d39d6ba Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Sat, 8 Dec 2018 12:19:42 +0000 Subject: [PATCH 26/59] Reset of linesHighlighted --- browser/main/lib/dataApi/createSnippet.js | 3 ++- browser/main/lib/dataApi/migrateFromV5Storage.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/browser/main/lib/dataApi/createSnippet.js b/browser/main/lib/dataApi/createSnippet.js index 5d189217..1e7435ad 100644 --- a/browser/main/lib/dataApi/createSnippet.js +++ b/browser/main/lib/dataApi/createSnippet.js @@ -9,7 +9,8 @@ function createSnippet (snippetFile) { id: crypto.randomBytes(16).toString('hex'), name: 'Unnamed snippet', prefix: [], - content: '' + content: '', + linesHighlighted: [], } fetchSnippet(null, snippetFile).then((snippets) => { snippets.push(newSnippet) diff --git a/browser/main/lib/dataApi/migrateFromV5Storage.js b/browser/main/lib/dataApi/migrateFromV5Storage.js index b11e66e9..0a20d648 100644 --- a/browser/main/lib/dataApi/migrateFromV5Storage.js +++ b/browser/main/lib/dataApi/migrateFromV5Storage.js @@ -87,7 +87,8 @@ function importAll (storage, data) { snippets: [{ name: article.mode, mode: article.mode, - content: article.content + content: article.content, + linesHighlighted: article.linesHighlighted, }] } notes.push(newNote) From 62609a29184e84000a0e97cc39190ab21dbea01c Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Sat, 8 Dec 2018 17:18:40 +0000 Subject: [PATCH 27/59] Fixed last nonfunctional changes made earlier Now iterates in the SnippetNoteDetail constructor the snippets and if linesHighlighted is not defined assigns an empty array --- browser/main/Detail/SnippetNoteDetail.js | 6 ++++++ browser/main/Main.js | 4 ++-- browser/main/lib/dataApi/createSnippet.js | 2 +- browser/main/lib/dataApi/migrateFromV5Storage.js | 2 +- browser/main/lib/dataApi/updateNote.js | 2 -- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 3d1027ea..7f10cb0b 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -52,6 +52,12 @@ class SnippetNoteDetail extends React.Component { }) } + this.state.note.snippets.forEach(function (s) { + if (s.linesHighlighted === undefined) { + s.linesHighlighted = [] + } + }) + this.scrollToNextTabThreshold = 0.7 this.generateToc = () => this.handleGenerateToc() } diff --git a/browser/main/Main.js b/browser/main/Main.js index d13bde0d..a9268eba 100644 --- a/browser/main/Main.js +++ b/browser/main/Main.js @@ -97,13 +97,13 @@ class Main extends React.Component { name: 'example.html', mode: 'html', content: "\n\n

Enjoy Boostnote!

\n\n", - linesHighlighted:[] + linesHighlighted: [] }, { name: 'example.js', mode: 'javascript', content: "var boostnote = document.getElementById('enjoy').innerHTML\n\nconsole.log(boostnote)", - linesHighlighted:[] + linesHighlighted: [] } ] }) diff --git a/browser/main/lib/dataApi/createSnippet.js b/browser/main/lib/dataApi/createSnippet.js index 1e7435ad..2e585c9f 100644 --- a/browser/main/lib/dataApi/createSnippet.js +++ b/browser/main/lib/dataApi/createSnippet.js @@ -10,7 +10,7 @@ function createSnippet (snippetFile) { name: 'Unnamed snippet', prefix: [], content: '', - linesHighlighted: [], + linesHighlighted: [] } fetchSnippet(null, snippetFile).then((snippets) => { snippets.push(newSnippet) diff --git a/browser/main/lib/dataApi/migrateFromV5Storage.js b/browser/main/lib/dataApi/migrateFromV5Storage.js index 0a20d648..eb3a9fb8 100644 --- a/browser/main/lib/dataApi/migrateFromV5Storage.js +++ b/browser/main/lib/dataApi/migrateFromV5Storage.js @@ -88,7 +88,7 @@ function importAll (storage, data) { name: article.mode, mode: article.mode, content: article.content, - linesHighlighted: article.linesHighlighted, + linesHighlighted: article.linesHighlighted }] } notes.push(newNote) diff --git a/browser/main/lib/dataApi/updateNote.js b/browser/main/lib/dataApi/updateNote.js index c89bad54..b7a28613 100644 --- a/browser/main/lib/dataApi/updateNote.js +++ b/browser/main/lib/dataApi/updateNote.js @@ -118,8 +118,6 @@ function updateNote (storageKey, noteKey, input) { if (noteData.type === 'SNIPPET_NOTE') { noteData.title - if(noteData.linesHighlighted = null) - noteData.linesHighlighted = []; } Object.assign(noteData, input, { From e65c48be33bfbe0bb8b692ad48610910771181bf Mon Sep 17 00:00:00 2001 From: Brunovsky Date: Tue, 11 Dec 2018 00:41:51 +0000 Subject: [PATCH 28/59] Fix issue 2557 katex alignment in display math --- browser/components/markdown.styl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl index b7f219b8..e5647c33 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -55,11 +55,12 @@ body line-height 1.6 overflow-x hidden background-color $ui-noteDetail-backgroundColor + // do not allow display line breaks + .katex-display > .katex + white-space nowrap + // allow inline line breaks .katex - font 400 1.2em 'KaTeX_Main' - line-height 1.2em white-space initial - text-indent 0 .katex .mfrac>.vlist>span:nth-child(2) top 0 !important .katex-error From 4fb11b68e4d47425e7eb7c46477f341360eb025c Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Thu, 13 Dec 2018 13:13:01 +0000 Subject: [PATCH 29/59] Markdown functionality --- browser/components/MarkdownEditor.js | 5 +++-- browser/components/MarkdownSplitEditor.js | 9 +++++---- browser/lib/newNote.js | 3 ++- browser/main/Detail/MarkdownNoteDetail.js | 11 ++++++++++- browser/main/Detail/SnippetNoteDetail.js | 1 + browser/main/NoteList/index.js | 3 ++- browser/main/lib/dataApi/createNote.js | 1 + browser/main/lib/dataApi/migrateFromV5Storage.js | 3 ++- browser/main/lib/dataApi/updateNote.js | 6 +++++- 9 files changed, 31 insertions(+), 11 deletions(-) diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 20ce9451..009a6b76 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -232,7 +232,7 @@ class MarkdownEditor extends React.Component { } render () { - const {className, value, config, storageKey, noteKey} = this.props + const {className, value, config, storageKey, noteKey, linesHighlighted} = this.props let editorFontSize = parseInt(config.editor.fontSize, 10) if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14 @@ -275,7 +275,8 @@ class MarkdownEditor extends React.Component { noteKey={noteKey} fetchUrlTitle={config.editor.fetchUrlTitle} enableTableEditor={config.editor.enableTableEditor} - onChange={(e) => this.handleChange(e)} + linesHighlighted={linesHighlighted} + onChange={(e) => this.handleChange(e).bind(this)} onBlur={(e) => this.handleBlur(e)} spellCheck={config.editor.spellcheck} /> diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index 0ab52a56..0a6c812a 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -24,9 +24,9 @@ class MarkdownSplitEditor extends React.Component { this.refs.code.setValue(value) } - handleOnChange () { + handleOnChange (e) { this.value = this.refs.code.value - this.props.onChange() + this.props.onChange(e) } handleScroll (e) { @@ -136,7 +136,7 @@ class MarkdownSplitEditor extends React.Component { } render () { - const {config, value, storageKey, noteKey} = this.props + const {config, value, storageKey, noteKey, linesHighlighted} = this.props const storage = findStorage(storageKey) let editorFontSize = parseInt(config.editor.fontSize, 10) if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14 @@ -169,7 +169,8 @@ class MarkdownSplitEditor extends React.Component { enableTableEditor={config.editor.enableTableEditor} storageKey={storageKey} noteKey={noteKey} - onChange={this.handleOnChange.bind(this)} + linesHighlighted={linesHighlighted} + onChange={(e) => this.handleOnChange(e).bind(this)} onScroll={this.handleScroll.bind(this)} spellCheck={config.editor.spellcheck} /> diff --git a/browser/lib/newNote.js b/browser/lib/newNote.js index 2e407207..9511f847 100644 --- a/browser/lib/newNote.js +++ b/browser/lib/newNote.js @@ -18,7 +18,8 @@ export function createMarkdownNote (storage, folder, dispatch, location, params, folder: folder, title: '', tags, - content: '' + content: '', + linesHighlighted: [] }) .then(note => { const noteHash = note.key diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js index b4e7a5b3..34dae369 100755 --- a/browser/main/Detail/MarkdownNoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -39,12 +39,19 @@ class MarkdownNoteDetail extends React.Component { isMovingNote: false, note: Object.assign({ title: '', - content: '' + content: '', + linesHighlighted: [] }, props.note), isLockButtonShown: false, isLocked: false, editorType: props.config.editor.type } + + let lines = this.state.note.linesHighlighted + if (lines === undefined) { + lines = [] + } + this.dispatchTimer = null this.toggleLockButton = this.handleToggleLockButton.bind(this) @@ -361,6 +368,7 @@ class MarkdownNoteDetail extends React.Component { value={note.content} storageKey={note.storage} noteKey={note.key} + linesHighlighted={note.linesHighlighted} onChange={this.handleUpdateContent.bind(this)} ignorePreviewPointerEvents={ignorePreviewPointerEvents} /> @@ -371,6 +379,7 @@ class MarkdownNoteDetail extends React.Component { value={note.content} storageKey={note.storage} noteKey={note.key} + linesHighlighted={note.linesHighlighted} onChange={this.handleUpdateContent.bind(this)} ignorePreviewPointerEvents={ignorePreviewPointerEvents} /> diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 7f10cb0b..252bafc8 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -706,6 +706,7 @@ class SnippetNoteDetail extends React.Component { ? this.handleCodeChange(index)(e)} ref={'code-' + index} ignorePreviewPointerEvents={this.props.ignorePreviewPointerEvents} diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 08d7d2e2..9a152e14 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -707,7 +707,8 @@ class NoteList extends React.Component { type: firstNote.type, folder: folder.key, title: firstNote.title + ' ' + i18n.__('copy'), - content: firstNote.content + content: firstNote.content, + linesHighlighted: firstNote.linesHighlighted }) .then((note) => { attachmentManagement.cloneAttachments(firstNote, note) diff --git a/browser/main/lib/dataApi/createNote.js b/browser/main/lib/dataApi/createNote.js index 0b6e6fbe..5bfa2457 100644 --- a/browser/main/lib/dataApi/createNote.js +++ b/browser/main/lib/dataApi/createNote.js @@ -16,6 +16,7 @@ function validateInput (input) { switch (input.type) { case 'MARKDOWN_NOTE': if (!_.isString(input.content)) input.content = '' + if (!_.isArray(input.linesHighlighted)) input.linesHighlighted = [] break case 'SNIPPET_NOTE': if (!_.isString(input.description)) input.description = '' diff --git a/browser/main/lib/dataApi/migrateFromV5Storage.js b/browser/main/lib/dataApi/migrateFromV5Storage.js index eb3a9fb8..78d78746 100644 --- a/browser/main/lib/dataApi/migrateFromV5Storage.js +++ b/browser/main/lib/dataApi/migrateFromV5Storage.js @@ -69,7 +69,8 @@ function importAll (storage, data) { isStarred: false, title: article.title, content: '# ' + article.title + '\n\n' + article.content, - key: noteKey + key: noteKey, + linesHighlighted: article.linesHighlighted } notes.push(newNote) } else { diff --git a/browser/main/lib/dataApi/updateNote.js b/browser/main/lib/dataApi/updateNote.js index b7a28613..ce9fabcf 100644 --- a/browser/main/lib/dataApi/updateNote.js +++ b/browser/main/lib/dataApi/updateNote.js @@ -39,6 +39,9 @@ function validateInput (input) { if (input.content != null) { if (!_.isString(input.content)) validatedInput.content = '' else validatedInput.content = input.content + + if (!_.isArray(input.linesHighlighted)) validatedInput.linesHighlighted = [] + else validatedInput.linesHighlighted = input.linesHighlighted } return validatedInput case 'SNIPPET_NOTE': @@ -103,7 +106,8 @@ function updateNote (storageKey, noteKey, input) { } : { type: 'MARKDOWN_NOTE', - content: '' + content: '', + linesHighlighted: [] } noteData.title = '' if (storage.folders.length === 0) throw new Error('Failed to restore note: No folder exists.') From f2a0f59b0804289363b35b70939fb0e1d2ad0074 Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Thu, 13 Dec 2018 13:27:20 +0000 Subject: [PATCH 30/59] Fixed error on call to bind. --- browser/components/MarkdownEditor.js | 2 +- browser/components/MarkdownSplitEditor.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 009a6b76..6c91ea1d 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -276,7 +276,7 @@ class MarkdownEditor extends React.Component { fetchUrlTitle={config.editor.fetchUrlTitle} enableTableEditor={config.editor.enableTableEditor} linesHighlighted={linesHighlighted} - onChange={(e) => this.handleChange(e).bind(this)} + onChange={(e) => this.handleChange.bind(this)(e)} onBlur={(e) => this.handleBlur(e)} spellCheck={config.editor.spellcheck} /> diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index 0a6c812a..c1b9bd84 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -170,7 +170,7 @@ class MarkdownSplitEditor extends React.Component { storageKey={storageKey} noteKey={noteKey} linesHighlighted={linesHighlighted} - onChange={(e) => this.handleOnChange(e).bind(this)} + onChange={(e) => this.handleOnChange.bind(this)(e)} onScroll={this.handleScroll.bind(this)} spellCheck={config.editor.spellcheck} /> From ac1ce6043b7fcfd31b63590f4a899e2509eba3e4 Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Thu, 13 Dec 2018 20:19:02 +0000 Subject: [PATCH 31/59] Fixed legacy default Markdown/Snippet notes bug Fixed a bug where a note or snippet is created before the pull request and you ran Boostnote for the first time after the pr and you firstly created a note or markdown and only then returned to the old default notes and you couldn't highlight --- browser/components/CodeEditor.js | 4 ---- browser/main/Detail/MarkdownNoteDetail.js | 7 +------ browser/main/Detail/SnippetNoteDetail.js | 13 +++---------- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 4027b262..2f4f1c8a 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -520,10 +520,6 @@ export default class CodeEditor extends React.Component { handleHighlight (editor, changeObject) { const lines = editor.options.linesHighlighted - if (lines == null) { - return - } - if (!lines.includes(changeObject)) { lines.push(changeObject) editor.addLineClass(changeObject, 'text', 'CodeMirror-activeline-background') diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js index 34dae369..bc6cd499 100755 --- a/browser/main/Detail/MarkdownNoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -47,11 +47,6 @@ class MarkdownNoteDetail extends React.Component { editorType: props.config.editor.type } - let lines = this.state.note.linesHighlighted - if (lines === undefined) { - lines = [] - } - this.dispatchTimer = null this.toggleLockButton = this.handleToggleLockButton.bind(this) @@ -78,7 +73,7 @@ class MarkdownNoteDetail extends React.Component { if (!this.state.isMovingNote && (isNewNote || hasDeletedTags)) { if (this.saveQueue != null) this.saveNow() this.setState({ - note: Object.assign({}, nextProps.note) + note: Object.assign({linesHighlighted: []}, nextProps.note) }, () => { this.refs.content.reload() if (this.refs.tags) this.refs.tags.reset() diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 252bafc8..554e7c99 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -48,16 +48,10 @@ class SnippetNoteDetail extends React.Component { note: Object.assign({ description: '' }, props.note, { - snippets: props.note.snippets.map((snippet) => Object.assign({}, snippet)) + snippets: props.note.snippets.map((snippet) => Object.assign({linesHighlighted: []}, snippet)) }) } - this.state.note.snippets.forEach(function (s) { - if (s.linesHighlighted === undefined) { - s.linesHighlighted = [] - } - }) - this.scrollToNextTabThreshold = 0.7 this.generateToc = () => this.handleGenerateToc() } @@ -82,8 +76,9 @@ class SnippetNoteDetail extends React.Component { const nextNote = Object.assign({ description: '' }, nextProps.note, { - snippets: nextProps.note.snippets.map((snippet) => Object.assign({}, snippet)) + snippets: nextProps.note.snippets.map((snippet) => Object.assign({linesHighlighted: []}, snippet)) }) + this.setState({ snippetIndex: 0, note: nextNote @@ -694,10 +689,8 @@ class SnippetNoteDetail extends React.Component { const viewList = note.snippets.map((snippet, index) => { const isActive = this.state.snippetIndex === index - let syntax = CodeMirror.findModeByName(convertModeName(snippet.mode)) if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') - return
Date: Tue, 11 Dec 2018 15:07:07 -0500 Subject: [PATCH 32/59] Changing tabs from material design to traditional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Adding support for “default” interface theme. - Adding support for “white” interface theme. - Adding support for “dark” interface theme. --- browser/components/SnippetTab.styl | 109 ++++++++++----------- browser/main/Detail/SnippetNoteDetail.styl | 32 +++++- browser/styles/index.styl | 2 - 3 files changed, 80 insertions(+), 63 deletions(-) diff --git a/browser/components/SnippetTab.styl b/browser/components/SnippetTab.styl index a31b8594..9c785104 100644 --- a/browser/components/SnippetTab.styl +++ b/browser/components/SnippetTab.styl @@ -3,19 +3,30 @@ flex 1 min-width 70px overflow hidden + border-left 1px solid $ui-borderColor + border-top 1px solid $ui-borderColor &:hover + background-color alpha($ui-button--active-backgroundColor, 20%) .deleteButton - color $ui-inactive-text-color - &:hover - background-color darken($ui-backgroundColor, 15%) - &:active - color white - background-color $ui-active-color + color: $ui-text-color + visibility visible + transition 0.15s + .button + color: $ui-text-color + transition 0.15s .root--active @extend .root min-width 100px - border-bottom $ui-border + background-color alpha($ui-button--active-backgroundColor, 60%) + .deleteButton + visibility visible + color: $ui-text-color + transition 0.15s + .button + font-weight bold + color: $ui-text-color + transition 0.15s .button width 100% @@ -27,8 +38,7 @@ background-color transparent transition 0.15s border-left 4px solid transparent - &:hover - background-color $ui-button--hover-backgroundColor + color $ui-inactive-text-color .deleteButton position absolute @@ -42,6 +52,7 @@ color $ui-inactive-text-color background-color transparent border-radius 2px + visibility hidden .input height 29px @@ -50,73 +61,68 @@ width 100% outline none +body[data-theme="default"], body[data-theme="white"] + .root--active + &:hover + background-color alpha($ui-button--active-backgroundColor, 60%) + body[data-theme="dark"] .root - color $ui-dark-text-color - border-color $ui-dark-borderColor + border-left 1px solid $ui-dark-borderColor + border-top 1px solid $ui-dark-borderColor &:hover - background-color $ui-dark-button--hover-backgroundColor + background-color alpha($ui-dark-button--active-backgroundColor, 20%) + transition 0.15s + .button + color $ui-dark-text-color + transition 0.15s .deleteButton - color $ui-dark-inactive-text-color - &:hover - background-color darken($ui-dark-button--hover-backgroundColor, 15%) - &:active - color $ui-dark-text-color - background-color $ui-dark-button--active-backgroundColor + color $ui-dark-text-color + transition 0.15s .root--active - color $ui-dark-text-color - border-color $ui-dark-borderColor - &:hover - background-color $ui-dark-button--hover-backgroundColor - .deleteButton - color $ui-dark-inactive-text-color - &:hover - background-color darken($ui-dark-button--hover-backgroundColor, 15%) - &:active - color $ui-dark-text-color - background-color $ui-dark-button--active-backgroundColor + background-color $ui-dark-button--active-backgroundColor + border-left 1px solid $ui-dark-borderColor + border-top 1px solid $ui-dark-borderColor + .button + color $ui-dark-text-color + .deleteButton + color $ui-dark-text-color .button border none - color $ui-dark-text-color background-color transparent transition color background-color 0.15s border-left 4px solid transparent - &:hover - color $ui-dark-text-color - background-color $ui-dark-button--hover-backgroundColor .input - background-color $ui-dark-button--hover-backgroundColor + background-color $ui-dark-button--active-backgroundColor color $ui-dark-text-color - - .deleteButton - color alpha($ui-dark-text-color, 30%) + transition 0.15s body[data-theme="solarized-dark"] .root color $ui-solarized-dark-text-color - border-color $ui-dark-borderColor + border-left 1px solid $ui-solarized-dark-borderColor + border-top 1px solid $ui-solarized-dark-borderColor &:hover background-color $ui-solarized-dark-noteDetail-backgroundColor .deleteButton color $ui-solarized-dark-text-color - &:hover - background-color darken($ui-solarized-dark-noteDetail-backgroundColor, 15%) &:active color $ui-solarized-dark-text-color background-color $ui-dark-button--active-backgroundColor .root--active color $ui-solarized-dark-text-color - border-color $ui-solarized-dark-borderColor + background-color darken($ui-solarized-dark-noteDetail-backgroundColor, 20%) + border-left 1px solid $ui-solarized-dark-borderColor + border-top 1px solid $ui-solarized-dark-borderColor &:hover - background-color $ui-solarized-dark-noteDetail-backgroundColor .deleteButton color $ui-solarized-dark-text-color &:hover - background-color darken($ui-solarized-dark-noteDetail-backgroundColor, 15%) + // background-color darken($ui-dark-button--hover-backgroundColor, 15%) &:active color $ui-solarized-dark-text-color background-color $ui-dark-button--active-backgroundColor @@ -129,7 +135,6 @@ body[data-theme="solarized-dark"] border-left 4px solid transparent &:hover color $ui-solarized-dark-text-color - background-color $ui-solarized-dark-noteDetail-backgroundColor .input background-color $ui-solarized-dark-noteDetail-backgroundColor @@ -146,21 +151,16 @@ body[data-theme="monokai"] background-color $ui-monokai-noteDetail-backgroundColor .deleteButton color $ui-monokai-text-color - &:hover - background-color darken($ui-monokai-noteDetail-backgroundColor, 15%) &:active color $ui-monokai-text-color background-color $ui-dark-button--active-backgroundColor .root--active color $ui-monokai-text-color - border-color $ui-monokai-borderColor + background-color darken($ui-monokai-noteDetail-backgroundColor, 15%) &:hover - background-color $ui-monokai-noteDetail-backgroundColor .deleteButton color $ui-monokai-text-color - &:hover - background-color darken($ui-monokai-noteDetail-backgroundColor, 15%) &:active color $ui-monokai-text-color background-color $ui-dark-button--active-backgroundColor @@ -173,7 +173,6 @@ body[data-theme="monokai"] border-left 4px solid transparent &:hover color $ui-monokai-text-color - background-color $ui-monokai-noteDetail-backgroundColor .input background-color $ui-monokai-noteDetail-backgroundColor @@ -190,21 +189,16 @@ body[data-theme="dracula"] background-color $ui-dracula-noteDetail-backgroundColor .deleteButton color $ui-dracula-text-color - &:hover - background-color darken($ui-dracula-noteDetail-backgroundColor, 15%) &:active color $ui-dracula-text-color background-color $ui-dark-button--active-backgroundColor .root--active color $ui-dracula-text-color - border-color $ui-dracula-borderColor + background-color darken($ui-dracula-noteDetail-backgroundColor, 15%) &:hover - background-color $ui-dracula-noteDetail-backgroundColor .deleteButton color $ui-dracula-text-color - &:hover - background-color darken($ui-dracula-noteDetail-backgroundColor, 15%) &:active color $ui-dracula-text-color background-color $ui-dark-button--active-backgroundColor @@ -217,7 +211,6 @@ body[data-theme="dracula"] border-left 4px solid transparent &:hover color $ui-dracula-text-color - background-color $ui-dracula-noteDetail-backgroundColor .input background-color $ui-dracula-noteDetail-backgroundColor diff --git a/browser/main/Detail/SnippetNoteDetail.styl b/browser/main/Detail/SnippetNoteDetail.styl index e3bb31c6..4757d414 100644 --- a/browser/main/Detail/SnippetNoteDetail.styl +++ b/browser/main/Detail/SnippetNoteDetail.styl @@ -31,7 +31,7 @@ .tabList absolute left right - top 55px + top 70px height 30px display flex background-color $ui-noteDetail-backgroundColor @@ -57,6 +57,9 @@ .tabList .tabButton navWhiteButtonColor() width 30px + border-left 1px solid $ui-borderColor + border-top 1px solid $ui-borderColor + border-right 1px solid $ui-borderColor .tabView absolute left right bottom @@ -98,17 +101,36 @@ opacity 0 transition 0.1s -body[data-theme="white"] +body[data-theme="white"], body[data-theme="default"] .root box-shadow $note-detail-box-shadow border none + .tabButton + &:hover + background-color alpha($ui-button--active-backgroundColor, 20%) + color $ui-text-color + transition 0.15s + body[data-theme="dark"] .root border-left 1px solid $ui-dark-borderColor background-color $ui-dark-noteDetail-backgroundColor box-shadow none + .tabList .tabButton + border-left 1px solid $ui-dark-borderColor + border-top 1px solid $ui-dark-borderColor + border-right 1px solid $ui-dark-borderColor + &:hover + background-color alpha($ui-dark-button--active-backgroundColor, 20%) + + .tabButton + &:hover + background-color alpha($ui-dark-button--active-backgroundColor, 20%) + color $ui-dark-text-color + transition 0.15s + .body background-color $ui-dark-noteDetail-backgroundColor @@ -118,7 +140,6 @@ body[data-theme="dark"] border 1px solid $ui-dark-borderColor .tabList - background-color $ui-button--active-backgroundColor background-color $ui-dark-noteDetail-backgroundColor .tabList .list @@ -150,6 +171,11 @@ body[data-theme="solarized-dark"] color $ui-solarized-dark-text-color border 1px solid $ui-solarized-dark-borderColor + .tabList .tabButton + border-left 1px solid $ui-solarized-dark-borderColor + border-top 1px solid $ui-solarized-dark-borderColor + border-right 1px solid $ui-solarized-dark-borderColor + .tabList background-color $ui-solarized-dark-noteDetail-backgroundColor color $ui-solarized-dark-text-color diff --git a/browser/styles/index.styl b/browser/styles/index.styl index 56cb0eab..b9f9c41e 100644 --- a/browser/styles/index.styl +++ b/browser/styles/index.styl @@ -240,10 +240,8 @@ navWhiteButtonColor() &:hover background-color alpha($ui-button--active-backgroundColor, 20%) transition 0.15s - color $ui-text-color &:active, &:active:hover background-color $ui-button--active-backgroundColor - color $ui-text-color transition 0.15s // UI Button From 58fd2273ea3359904ce20697309090ee928719b7 Mon Sep 17 00:00:00 2001 From: John Ciprian Date: Tue, 11 Dec 2018 16:28:02 -0500 Subject: [PATCH 33/59] Adding support for solarized-dark interface theme --- browser/components/SnippetTab.styl | 34 +++++++++------------- browser/main/Detail/SnippetNoteDetail.styl | 6 ++++ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/browser/components/SnippetTab.styl b/browser/components/SnippetTab.styl index 9c785104..6f858d91 100644 --- a/browser/components/SnippetTab.styl +++ b/browser/components/SnippetTab.styl @@ -107,25 +107,23 @@ body[data-theme="solarized-dark"] border-top 1px solid $ui-solarized-dark-borderColor &:hover background-color $ui-solarized-dark-noteDetail-backgroundColor + transition 0.15s .deleteButton - color $ui-solarized-dark-text-color - &:active - color $ui-solarized-dark-text-color - background-color $ui-dark-button--active-backgroundColor + color $ui-solarized-dark-button--active-color + transition 0.15s + .button + color $ui-solarized-dark-button--active-color + transition 0.15s .root--active - color $ui-solarized-dark-text-color - background-color darken($ui-solarized-dark-noteDetail-backgroundColor, 20%) + color $ui-solarized-dark-button--active-color + background-color $ui-solarized-dark-button-backgroundColor border-left 1px solid $ui-solarized-dark-borderColor border-top 1px solid $ui-solarized-dark-borderColor - &:hover - .deleteButton - color $ui-solarized-dark-text-color - &:hover - // background-color darken($ui-dark-button--hover-backgroundColor, 15%) - &:active - color $ui-solarized-dark-text-color - background-color $ui-dark-button--active-backgroundColor + .deleteButton + color $ui-solarized-dark-button--active-color + .button + color $ui-solarized-dark-button--active-color .button border none @@ -133,15 +131,11 @@ body[data-theme="solarized-dark"] background-color transparent transition color background-color 0.15s border-left 4px solid transparent - &:hover - color $ui-solarized-dark-text-color .input background-color $ui-solarized-dark-noteDetail-backgroundColor - color $ui-solarized-dark-text-color - - .deleteButton - color alpha($ui-solarized-dark-text-color, 30%) + color $ui-solarized-dark-button--active-color + transition 0.15s body[data-theme="monokai"] .root diff --git a/browser/main/Detail/SnippetNoteDetail.styl b/browser/main/Detail/SnippetNoteDetail.styl index 4757d414..5a81c843 100644 --- a/browser/main/Detail/SnippetNoteDetail.styl +++ b/browser/main/Detail/SnippetNoteDetail.styl @@ -175,6 +175,12 @@ body[data-theme="solarized-dark"] border-left 1px solid $ui-solarized-dark-borderColor border-top 1px solid $ui-solarized-dark-borderColor border-right 1px solid $ui-solarized-dark-borderColor + + .tabButton + &:hover + color $ui-solarized-dark-button--active-color + background-color $ui-solarized-dark-noteDetail-backgroundColor + transition 0.15s .tabList background-color $ui-solarized-dark-noteDetail-backgroundColor From 29cf4769f5d4a9612aff5338e3b4a845700e7315 Mon Sep 17 00:00:00 2001 From: John Ciprian Date: Wed, 12 Dec 2018 19:41:02 -0500 Subject: [PATCH 34/59] Adding support for monokai interface theme --- browser/components/SnippetTab.styl | 36 ++++++++++------------ browser/main/Detail/SnippetNoteDetail.styl | 8 +++++ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/browser/components/SnippetTab.styl b/browser/components/SnippetTab.styl index 6f858d91..a76a1196 100644 --- a/browser/components/SnippetTab.styl +++ b/browser/components/SnippetTab.styl @@ -139,41 +139,37 @@ body[data-theme="solarized-dark"] body[data-theme="monokai"] .root - color $ui-monokai-text-color - border-color $ui-dark-borderColor + border-color $ui-monokai-borderColor &:hover background-color $ui-monokai-noteDetail-backgroundColor + transition 0.15s .deleteButton color $ui-monokai-text-color - &:active - color $ui-monokai-text-color - background-color $ui-dark-button--active-backgroundColor + transition 0.15s + .button + color $ui-monokai-text-color + transition 0.15s .root--active - color $ui-monokai-text-color - background-color darken($ui-monokai-noteDetail-backgroundColor, 15%) - &:hover - .deleteButton - color $ui-monokai-text-color - &:active - color $ui-monokai-text-color - background-color $ui-dark-button--active-backgroundColor - + color $ui-monokai-active-color + background-color $ui-monokai-button-backgroundColor + border-color $ui-monokai-borderColor + .deleteButton + color $ui-monokai-text-color + .button + color $ui-monokai-active-color + .button border none - color $ui-monokai-text-color + color $ui-inactive-text-color background-color transparent transition color background-color 0.15s border-left 4px solid transparent - &:hover - color $ui-monokai-text-color .input background-color $ui-monokai-noteDetail-backgroundColor color $ui-monokai-text-color - - .deleteButton - color alpha($ui-monokai-text-color, 30%) + transition 0.15s body[data-theme="dracula"] .root diff --git a/browser/main/Detail/SnippetNoteDetail.styl b/browser/main/Detail/SnippetNoteDetail.styl index 5a81c843..1366e869 100644 --- a/browser/main/Detail/SnippetNoteDetail.styl +++ b/browser/main/Detail/SnippetNoteDetail.styl @@ -199,6 +199,14 @@ body[data-theme="monokai"] color $ui-monokai-text-color border 1px solid $ui-monokai-borderColor + .tabList .tabButton + border-color $ui-monokai-borderColor + + .tabButton + &:hover + color $ui-monokai-text-color + background-color $ui-monokai-noteDetail-backgroundColor + .tabList background-color $ui-monokai-noteDetail-backgroundColor color $ui-monokai-text-color From cfcaa58b719b839d16b75bc99967bea9c4582b86 Mon Sep 17 00:00:00 2001 From: John Ciprian Date: Wed, 12 Dec 2018 19:43:01 -0500 Subject: [PATCH 35/59] Optimizing css for dark and solarized-dark themes --- browser/components/SnippetTab.styl | 9 +++------ browser/main/Detail/SnippetNoteDetail.styl | 8 ++------ 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/browser/components/SnippetTab.styl b/browser/components/SnippetTab.styl index a76a1196..03d6409b 100644 --- a/browser/components/SnippetTab.styl +++ b/browser/components/SnippetTab.styl @@ -68,7 +68,7 @@ body[data-theme="default"], body[data-theme="white"] body[data-theme="dark"] .root - border-left 1px solid $ui-dark-borderColor + border-color $ui-dark-borderColor border-top 1px solid $ui-dark-borderColor &:hover background-color alpha($ui-dark-button--active-backgroundColor, 20%) @@ -102,9 +102,7 @@ body[data-theme="dark"] body[data-theme="solarized-dark"] .root - color $ui-solarized-dark-text-color - border-left 1px solid $ui-solarized-dark-borderColor - border-top 1px solid $ui-solarized-dark-borderColor + border-color $ui-solarized-dark-borderColor &:hover background-color $ui-solarized-dark-noteDetail-backgroundColor transition 0.15s @@ -118,8 +116,7 @@ body[data-theme="solarized-dark"] .root--active color $ui-solarized-dark-button--active-color background-color $ui-solarized-dark-button-backgroundColor - border-left 1px solid $ui-solarized-dark-borderColor - border-top 1px solid $ui-solarized-dark-borderColor + border-color $ui-solarized-dark-borderColor .deleteButton color $ui-solarized-dark-button--active-color .button diff --git a/browser/main/Detail/SnippetNoteDetail.styl b/browser/main/Detail/SnippetNoteDetail.styl index 1366e869..15743051 100644 --- a/browser/main/Detail/SnippetNoteDetail.styl +++ b/browser/main/Detail/SnippetNoteDetail.styl @@ -119,9 +119,7 @@ body[data-theme="dark"] box-shadow none .tabList .tabButton - border-left 1px solid $ui-dark-borderColor - border-top 1px solid $ui-dark-borderColor - border-right 1px solid $ui-dark-borderColor + border-color $ui-dark-borderColor &:hover background-color alpha($ui-dark-button--active-backgroundColor, 20%) @@ -172,9 +170,7 @@ body[data-theme="solarized-dark"] border 1px solid $ui-solarized-dark-borderColor .tabList .tabButton - border-left 1px solid $ui-solarized-dark-borderColor - border-top 1px solid $ui-solarized-dark-borderColor - border-right 1px solid $ui-solarized-dark-borderColor + border-color $ui-solarized-dark-borderColor .tabButton &:hover From ec506e71a44d39f57e6b7fb2d339e387c7f145c3 Mon Sep 17 00:00:00 2001 From: John Ciprian Date: Wed, 12 Dec 2018 20:56:31 -0500 Subject: [PATCH 36/59] Adding support for dracula interface theme --- browser/components/SnippetTab.styl | 33 +++++++++------------- browser/main/Detail/SnippetNoteDetail.styl | 8 ++++++ 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/browser/components/SnippetTab.styl b/browser/components/SnippetTab.styl index 03d6409b..d101f318 100644 --- a/browser/components/SnippetTab.styl +++ b/browser/components/SnippetTab.styl @@ -170,38 +170,33 @@ body[data-theme="monokai"] body[data-theme="dracula"] .root - color $ui-dracula-text-color - border-color $ui-dark-borderColor + border-color $ui-dracula-borderColor &:hover background-color $ui-dracula-noteDetail-backgroundColor + transition 0.15s .deleteButton color $ui-dracula-text-color - &:active - color $ui-dracula-text-color - background-color $ui-dark-button--active-backgroundColor + transition 0.15s + .button + color $ui-dracula-text-color + transition 0.15s .root--active color $ui-dracula-text-color - background-color darken($ui-dracula-noteDetail-backgroundColor, 15%) - &:hover - .deleteButton - color $ui-dracula-text-color - &:active - color $ui-dracula-text-color - background-color $ui-dark-button--active-backgroundColor + background-color $ui-dracula-button-backgroundColor + border-color $ui-dracula-borderColor + .deleteButton + color $ui-dracula-text-color + .button + color $ui-dracula-active-color .button border none - color $ui-dracula-text-color + color $ui-inactive-text-color background-color transparent transition color background-color 0.15s border-left 4px solid transparent - &:hover - color $ui-dracula-text-color .input background-color $ui-dracula-noteDetail-backgroundColor - color $ui-dracula-text-color - - .deleteButton - color alpha($ui-dracula-text-color, 30%) \ No newline at end of file + color $ui-dracula-text-color \ No newline at end of file diff --git a/browser/main/Detail/SnippetNoteDetail.styl b/browser/main/Detail/SnippetNoteDetail.styl index 15743051..1af93645 100644 --- a/browser/main/Detail/SnippetNoteDetail.styl +++ b/browser/main/Detail/SnippetNoteDetail.styl @@ -220,6 +220,14 @@ body[data-theme="dracula"] color $ui-dracula-text-color border 1px solid $ui-dracula-borderColor + .tabList .tabButton + border-color $ui-dracula-borderColor + + .tabButton + &:hover + color $ui-dracula-text-color + background-color $ui-dracula-noteDetail-backgroundColor + .tabList background-color $ui-dracula-noteDetail-backgroundColor color $ui-dracula-text-color \ No newline at end of file From 4f9b37433c3f381a718cab268a52f1f606a15b03 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 15 Dec 2018 10:11:04 +0100 Subject: [PATCH 37/59] fix toc by sharing `slugify()` --- browser/lib/markdown-toc-generator.js | 63 +++++++++++---------------- browser/lib/markdown.js | 11 +---- browser/lib/slugify.js | 6 +++ 3 files changed, 33 insertions(+), 47 deletions(-) create mode 100644 browser/lib/slugify.js diff --git a/browser/lib/markdown-toc-generator.js b/browser/lib/markdown-toc-generator.js index eae448ec..a7bc79f3 100644 --- a/browser/lib/markdown-toc-generator.js +++ b/browser/lib/markdown-toc-generator.js @@ -6,47 +6,21 @@ import toc from 'markdown-toc' import diacritics from 'diacritics-map' import stripColor from 'strip-color' import mdlink from 'markdown-link' +import slugify from './slugify' const EOL = require('os').EOL +const hasProp = Object.prototype.hasOwnProperty + /** - * @caseSensitiveSlugify Custom slugify function - * Same implementation that the original used by markdown-toc (node_modules/markdown-toc/lib/utils.js), - * but keeps original case to properly handle https://github.com/BoostIO/Boostnote/issues/2067 + * From @enyaxu/markdown-it-anchor */ -function caseSensitiveSlugify (str) { - function replaceDiacritics (str) { - return str.replace(/[À-ž]/g, function (ch) { - return diacritics[ch] || ch - }) - } - - function getTitle (str) { - if (/^\[[^\]]+\]\(/.test(str)) { - var m = /^\[([^\]]+)\]/.exec(str) - if (m) return m[1] - } - return str - } - - str = getTitle(str) - str = stripColor(str) - // str = str.toLowerCase() //let's be case sensitive - - // `.split()` is often (but not always) faster than `.replace()` - str = str.split(' ').join('-') - str = str.split(/\t/).join('--') - str = str.split(/<\/?[^>]+>/).join('') - str = str.split(/[|$&`~=\\\/@+*!?({[\]})<>=.,;:'"^]/).join('') - str = str.split(/[。?!,、;:“”【】()〔〕[]﹃﹄“ ”‘’﹁﹂—…-~《》〈〉「」]/).join('') - str = replaceDiacritics(str) - return str -} - -function linkify (tok, text, slug, opts) { - var uniqeID = opts.num === 0 ? '' : '-' + opts.num - tok.content = mdlink(text, '#' + slug + uniqeID) - return tok +function uniqueSlug (slug, slugs, opts) { + let uniq = slug + let i = opts.uniqueSlugStartIndex + while (hasProp.call(slugs, uniq)) uniq = `${slug}-${i++}` + slugs[uniq] = true + return uniq } const TOC_MARKER_START = '' @@ -91,8 +65,21 @@ export function generateInEditor (editor) { * @returns generatedTOC String containing generated TOC */ export function generate (markdownText) { - const generatedToc = toc(markdownText, {slugify: caseSensitiveSlugify, linkify: linkify}) - return TOC_MARKER_START + EOL + EOL + generatedToc.content + EOL + EOL + TOC_MARKER_END + const slugs = {} + const opts = { + uniqueSlugStartIndex: 1 + } + + const tokens = toc(markdownText, { + slugify: title => { + return uniqueSlug(slugify(title), slugs, opts) + }, + linkify: false + }) + + const md = tokens.json.map(token => mdlink(token.content, '#' + token.slug)).join(EOL) + + return TOC_MARKER_START + EOL + EOL + md + EOL + EOL + TOC_MARKER_END } function wrapTocWithEol (toc, editor) { diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index 2a7b66b0..0f7d58b5 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -7,7 +7,6 @@ import _ from 'lodash' import ConfigManager from 'browser/main/lib/ConfigManager' import katex from 'katex' import { lastFindInArray } from './utils' -import anchor from '@enyaxu/markdown-it-anchor' function createGutter (str, firstLineNumber) { if (Number.isNaN(firstLineNumber)) firstLineNumber = 1 @@ -118,14 +117,8 @@ class Markdown { this.md.use(require('markdown-it-imsize')) this.md.use(require('markdown-it-footnote')) this.md.use(require('markdown-it-multimd-table')) - this.md.use(anchor, { - slugify: (title) => { - var slug = encodeURI(title.trim() - .replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '') - .replace(/\s+/g, '-')) - .replace(/\-+$/, '') - return slug - } + this.md.use(require('@enyaxu/markdown-it-anchor'), { + slugify: require('./slugify') }) this.md.use(require('markdown-it-kbd')) this.md.use(require('markdown-it-admonition'), {types: ['note', 'hint', 'attention', 'caution', 'danger', 'error']}) diff --git a/browser/lib/slugify.js b/browser/lib/slugify.js new file mode 100644 index 00000000..0ff21245 --- /dev/null +++ b/browser/lib/slugify.js @@ -0,0 +1,6 @@ +module.exports = function slugify (title) { + return encodeURI(title.trim() + .replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '') + .replace(/\s+/g, '-')) + .replace(/\-+$/, '') +} From d5a2aa6d6d8ab0434cd29da4a4b294e4c1839ae2 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sat, 15 Dec 2018 10:41:47 +0100 Subject: [PATCH 38/59] fix missing bullets --- browser/lib/markdown-toc-generator.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/browser/lib/markdown-toc-generator.js b/browser/lib/markdown-toc-generator.js index a7bc79f3..7a97f800 100644 --- a/browser/lib/markdown-toc-generator.js +++ b/browser/lib/markdown-toc-generator.js @@ -2,14 +2,13 @@ * @fileoverview Markdown table of contents generator */ +import { EOL } from 'os' import toc from 'markdown-toc' import diacritics from 'diacritics-map' import stripColor from 'strip-color' import mdlink from 'markdown-link' import slugify from './slugify' -const EOL = require('os').EOL - const hasProp = Object.prototype.hasOwnProperty /** @@ -23,6 +22,11 @@ function uniqueSlug (slug, slugs, opts) { return uniq } +function linkify (token) { + token.content = mdlink(token.content, '#' + token.slug) + return token +} + const TOC_MARKER_START = '' const TOC_MARKER_END = '' @@ -70,14 +74,16 @@ export function generate (markdownText) { uniqueSlugStartIndex: 1 } - const tokens = toc(markdownText, { + const result = toc(markdownText, { slugify: title => { return uniqueSlug(slugify(title), slugs, opts) }, linkify: false }) - const md = tokens.json.map(token => mdlink(token.content, '#' + token.slug)).join(EOL) + const md = toc.bullets(result.json.map(linkify), { + highest: result.highest + }) return TOC_MARKER_START + EOL + EOL + md + EOL + EOL + TOC_MARKER_END } From b99980fda1afcb12df5316337fb0b869a65b550e Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 16 Dec 2018 19:38:04 +0100 Subject: [PATCH 39/59] improve slug by replacing diacritics and removing unwanted characters --- browser/lib/slugify.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/browser/lib/slugify.js b/browser/lib/slugify.js index 0ff21245..3b6386de 100644 --- a/browser/lib/slugify.js +++ b/browser/lib/slugify.js @@ -1,6 +1,17 @@ -module.exports = function slugify (title) { - return encodeURI(title.trim() - .replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '') - .replace(/\s+/g, '-')) - .replace(/\-+$/, '') +import diacritics from 'diacritics-map' + +function replaceDiacritics(str) { + return str.replace(/[À-ž]/g, function(ch) { + return diacritics[ch] || ch; + }); +} + +module.exports = function slugify (title) { + let slug = title.trim() + + slug = replaceDiacritics(slug) + + slug = slug.replace(/[^\w\s-]/g, '').replace(/\s+/g, '-') + + return encodeURI(slug).replace(/\-+$/, '') } From 256653677e31302212aee0d252cdd34f16ad64e2 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 16 Dec 2018 19:45:18 +0100 Subject: [PATCH 40/59] fix lint errors and remove unused dependencies --- browser/lib/markdown-toc-generator.js | 2 -- browser/lib/slugify.js | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/browser/lib/markdown-toc-generator.js b/browser/lib/markdown-toc-generator.js index 7a97f800..af1c833f 100644 --- a/browser/lib/markdown-toc-generator.js +++ b/browser/lib/markdown-toc-generator.js @@ -4,8 +4,6 @@ import { EOL } from 'os' import toc from 'markdown-toc' -import diacritics from 'diacritics-map' -import stripColor from 'strip-color' import mdlink from 'markdown-link' import slugify from './slugify' diff --git a/browser/lib/slugify.js b/browser/lib/slugify.js index 3b6386de..a3447a90 100644 --- a/browser/lib/slugify.js +++ b/browser/lib/slugify.js @@ -1,9 +1,9 @@ import diacritics from 'diacritics-map' -function replaceDiacritics(str) { - return str.replace(/[À-ž]/g, function(ch) { - return diacritics[ch] || ch; - }); +function replaceDiacritics (str) { + return str.replace(/[À-ž]/g, function (ch) { + return diacritics[ch] || ch + }) } module.exports = function slugify (title) { From 64ca875cfd0c20614fe1a988a43bbc2ba0b3c6d1 Mon Sep 17 00:00:00 2001 From: Junyoung Choi Date: Mon, 17 Dec 2018 14:56:16 +0900 Subject: [PATCH 41/59] v0.11.12 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 128a21af..44e7c9f0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "boost", "productName": "Boostnote", - "version": "0.11.11", + "version": "0.11.12", "main": "index.js", "description": "Boostnote", "license": "GPL-3.0", From 72906b3ee78a10aec04c6e2ee3e07be47f697960 Mon Sep 17 00:00:00 2001 From: Duarte-Frazao Date: Wed, 19 Dec 2018 16:34:16 +0000 Subject: [PATCH 42/59] Corrections to make line highlighting robust, added tests Lines now save correctly with different inputs, making sure that different inputs like enter, delete, paste and where it's deleted stay consistent when saving. Included in the create/update snippet/note tests the structure from lines highlighting saved to the files. --- browser/components/CodeEditor.js | 69 +++++++++++++++++++++++++++++ tests/dataApi/createNote-test.js | 13 +++++- tests/dataApi/createSnippet-test.js | 1 + tests/dataApi/updateNote-test.js | 19 ++++++-- 4 files changed, 96 insertions(+), 6 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 2f4f1c8a..a438009f 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -511,12 +511,50 @@ export default class CodeEditor extends React.Component { handleChange (editor, changeObject) { spellcheck.handleChange(editor, changeObject) + + this.updateHighlight(editor, changeObject) + this.value = editor.getValue() if (this.props.onChange) { this.props.onChange(editor) } } + incrementLines (start, linesAdded, linesRemoved, editor) { + let highlightedLines = editor.options.linesHighlighted + + const totalHighlightedLines = highlightedLines.length + + let offset = linesAdded - linesRemoved + + // Store new items to be added as we're changing the lines + let newLines = [] + + let i = totalHighlightedLines + + while (i--) { + const lineNumber = highlightedLines[i] + + // Interval that will need to be updated + // Between start and (start + offset) remove highlight + if (lineNumber >= start) { + highlightedLines.splice(highlightedLines.indexOf(lineNumber), 1) + + // Lines that need to be relocated + if (lineNumber >= (start + linesRemoved)) { + newLines.push(lineNumber + offset) + } + } + } + + // Adding relocated lines + highlightedLines.push(...newLines) + + if (this.props.onChange) { + this.props.onChange(editor) + } + } + handleHighlight (editor, changeObject) { const lines = editor.options.linesHighlighted @@ -532,6 +570,37 @@ export default class CodeEditor extends React.Component { } } + updateHighlight (editor, changeObject) { + const linesAdded = changeObject.text.length - 1 + const linesRemoved = changeObject.removed.length - 1 + + // If no lines added or removed return + if (linesAdded === 0 && linesRemoved === 0) { + return + } + + let start = changeObject.from.line + + switch (changeObject.origin) { + case '+insert", "undo': + start += 1 + break + + case 'paste': + case '+delete': + case '+input': + if (changeObject.to.ch !== 0 || changeObject.from.ch !== 0) { + start += 1 + } + break + + default: + return + } + + this.incrementLines(start, linesAdded, linesRemoved, editor) + } + moveCursorTo (row, col) {} scrollToLine (event, num) { diff --git a/tests/dataApi/createNote-test.js b/tests/dataApi/createNote-test.js index 47446aab..3606dfd4 100644 --- a/tests/dataApi/createNote-test.js +++ b/tests/dataApi/createNote-test.js @@ -25,13 +25,16 @@ test.serial('Create a note', (t) => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key + const randLinesHighlightedArray = new Array(10).fill().map(() => Math.round(Math.random() * 10)) + const input1 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), snippets: [{ name: faker.system.fileName(), mode: 'text', - content: faker.lorem.lines() + content: faker.lorem.lines(), + linesHighlighted: randLinesHighlightedArray }], tags: faker.lorem.words().split(' '), folder: folderKey @@ -42,7 +45,8 @@ test.serial('Create a note', (t) => { type: 'MARKDOWN_NOTE', content: faker.lorem.lines(), tags: faker.lorem.words().split(' '), - folder: folderKey + folder: folderKey, + linesHighlighted: randLinesHighlightedArray } input2.title = input2.content.split('\n').shift() @@ -59,6 +63,7 @@ test.serial('Create a note', (t) => { t.is(storageKey, data1.storage) const jsonData1 = CSON.readFileSync(path.join(storagePath, 'notes', data1.key + '.cson')) + t.is(input1.title, data1.title) t.is(input1.title, jsonData1.title) t.is(input1.description, data1.description) @@ -71,6 +76,8 @@ test.serial('Create a note', (t) => { t.is(input1.snippets[0].content, jsonData1.snippets[0].content) t.is(input1.snippets[0].name, data1.snippets[0].name) t.is(input1.snippets[0].name, jsonData1.snippets[0].name) + t.deepEqual(input1.snippets[0].linesHighlighted, data1.snippets[0].linesHighlighted) + t.deepEqual(input1.snippets[0].linesHighlighted, jsonData1.snippets[0].linesHighlighted) t.is(storageKey, data2.storage) const jsonData2 = CSON.readFileSync(path.join(storagePath, 'notes', data2.key + '.cson')) @@ -80,6 +87,8 @@ test.serial('Create a note', (t) => { t.is(input2.content, jsonData2.content) t.is(input2.tags.length, data2.tags.length) t.is(input2.tags.length, jsonData2.tags.length) + t.deepEqual(input2.linesHighlighted, data2.linesHighlighted) + t.deepEqual(input2.linesHighlighted, jsonData2.linesHighlighted) }) }) diff --git a/tests/dataApi/createSnippet-test.js b/tests/dataApi/createSnippet-test.js index f06cf861..638b76ca 100644 --- a/tests/dataApi/createSnippet-test.js +++ b/tests/dataApi/createSnippet-test.js @@ -26,6 +26,7 @@ test.serial('Create a snippet', (t) => { t.is(snippet.name, data.name) t.deepEqual(snippet.prefix, data.prefix) t.is(snippet.content, data.content) + t.deepEqual(snippet.linesHighlighted, data.linesHighlighted) }) }) diff --git a/tests/dataApi/updateNote-test.js b/tests/dataApi/updateNote-test.js index 6043ee1e..da47c30c 100644 --- a/tests/dataApi/updateNote-test.js +++ b/tests/dataApi/updateNote-test.js @@ -26,13 +26,17 @@ test.serial('Update a note', (t) => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key + const randLinesHighlightedArray = new Array(10).fill().map(() => Math.round(Math.random() * 10)) + const randLinesHighlightedArray2 = new Array(15).fill().map(() => Math.round(Math.random() * 15)) + const input1 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), snippets: [{ name: faker.system.fileName(), mode: 'text', - content: faker.lorem.lines() + content: faker.lorem.lines(), + linesHighlighted: randLinesHighlightedArray }], tags: faker.lorem.words().split(' '), folder: folderKey @@ -43,7 +47,8 @@ test.serial('Update a note', (t) => { type: 'MARKDOWN_NOTE', content: faker.lorem.lines(), tags: faker.lorem.words().split(' '), - folder: folderKey + folder: folderKey, + linesHighlighted: randLinesHighlightedArray } input2.title = input2.content.split('\n').shift() @@ -53,7 +58,8 @@ test.serial('Update a note', (t) => { snippets: [{ name: faker.system.fileName(), mode: 'text', - content: faker.lorem.lines() + content: faker.lorem.lines(), + linesHighlighted: randLinesHighlightedArray2 }], tags: faker.lorem.words().split(' ') } @@ -62,7 +68,8 @@ test.serial('Update a note', (t) => { const input4 = { type: 'MARKDOWN_NOTE', content: faker.lorem.lines(), - tags: faker.lorem.words().split(' ') + tags: faker.lorem.words().split(' '), + linesHighlighted: randLinesHighlightedArray2 } input4.title = input4.content.split('\n').shift() @@ -99,6 +106,8 @@ test.serial('Update a note', (t) => { t.is(input3.snippets[0].content, jsonData1.snippets[0].content) t.is(input3.snippets[0].name, data1.snippets[0].name) t.is(input3.snippets[0].name, jsonData1.snippets[0].name) + t.deepEqual(input3.snippets[0].linesHighlighted, data1.snippets[0].linesHighlighted) + t.deepEqual(input3.snippets[0].linesHighlighted, jsonData1.snippets[0].linesHighlighted) const jsonData2 = CSON.readFileSync(path.join(storagePath, 'notes', data2.key + '.cson')) t.is(input4.title, data2.title) @@ -107,6 +116,8 @@ test.serial('Update a note', (t) => { t.is(input4.content, jsonData2.content) t.is(input4.tags.length, data2.tags.length) t.is(input4.tags.length, jsonData2.tags.length) + t.deepEqual(input4.linesHighlighted, data2.linesHighlighted) + t.deepEqual(input4.linesHighlighted, jsonData2.linesHighlighted) }) }) From b539ac6335ac156ab244c986ef44c9feb2bd0603 Mon Sep 17 00:00:00 2001 From: MiguelPedrosa Date: Wed, 19 Dec 2018 20:57:27 +0000 Subject: [PATCH 43/59] Fixed windows debug path --- .vscode/launch.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index a742a59e..9d1cc4ec 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -17,7 +17,7 @@ "${workspaceFolder}/index.js" ], "windows": { - "runtimeExecutable": "${workspaceFolder}/node_modeules/.bin/electron.cmd" + "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd" } }, { From 8d769d4c4b22596748067c7598960c93a856ce98 Mon Sep 17 00:00:00 2001 From: Christian Oliff Date: Sat, 22 Dec 2018 12:00:19 +0900 Subject: [PATCH 44/59] Https link --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index a4730cbf..8c5bd614 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# EditorConfig is awesome: http://EditorConfig.org +# EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file root = true From e93bf1cfe757821c8c5f618d5168cb50a092ad80 Mon Sep 17 00:00:00 2001 From: duartefrazao Date: Sat, 22 Dec 2018 11:44:30 +0000 Subject: [PATCH 45/59] Removed bind in MarkdownEditor and MarkdownSplitEditor --- browser/components/MarkdownEditor.js | 2 +- browser/components/MarkdownSplitEditor.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 6c91ea1d..0043ea5c 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -276,7 +276,7 @@ class MarkdownEditor extends React.Component { fetchUrlTitle={config.editor.fetchUrlTitle} enableTableEditor={config.editor.enableTableEditor} linesHighlighted={linesHighlighted} - onChange={(e) => this.handleChange.bind(this)(e)} + onChange={(e) => this.handleChange(e)} onBlur={(e) => this.handleBlur(e)} spellCheck={config.editor.spellcheck} /> diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index c1b9bd84..2f76b9fc 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -170,7 +170,7 @@ class MarkdownSplitEditor extends React.Component { storageKey={storageKey} noteKey={noteKey} linesHighlighted={linesHighlighted} - onChange={(e) => this.handleOnChange.bind(this)(e)} + onChange={(e) => this.handleOnChange(e)} onScroll={this.handleScroll.bind(this)} spellCheck={config.editor.spellcheck} /> From 692f6779d659e9b3e67a2a10f6167988c94a5754 Mon Sep 17 00:00:00 2001 From: roottool Date: Sun, 23 Dec 2018 01:13:57 +0900 Subject: [PATCH 46/59] fixed the checkboxes are too far right #2729 --- browser/components/markdown.styl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl index b7f219b8..71cf987b 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -183,6 +183,10 @@ ul display list-item &.taskListItem list-style none + &>input + margin-left -1.6em + &>p + margin-left -1.8em p margin 0 &>li>ul, &>li>ol From 30e262d8aca6ef5358d5ed93458802d4960eaf05 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Sun, 23 Dec 2018 17:01:46 +0100 Subject: [PATCH 47/59] fix min width of note list --- browser/main/Main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/main/Main.js b/browser/main/Main.js index c426f2bd..57f97358 100644 --- a/browser/main/Main.js +++ b/browser/main/Main.js @@ -234,8 +234,8 @@ class Main extends React.Component { if (this.state.isRightSliderFocused) { const offset = this.refs.body.getBoundingClientRect().left let newListWidth = e.pageX - offset - if (newListWidth < 10) { - newListWidth = 10 + if (newListWidth < 180) { + newListWidth = 180 } else if (newListWidth > 600) { newListWidth = 600 } From 5ccb9bde28951c2b18824e5cafc5ee8690b26299 Mon Sep 17 00:00:00 2001 From: kazup01 Date: Mon, 24 Dec 2018 14:10:52 +0900 Subject: [PATCH 48/59] Change IssueHunt image --- Backers.md | 72 ------------------- .../main/modals/PreferencesModal/InfoTab.js | 5 ++ readme.md | 2 +- 3 files changed, 6 insertions(+), 73 deletions(-) delete mode 100644 Backers.md diff --git a/Backers.md b/Backers.md deleted file mode 100644 index 18d221bf..00000000 --- a/Backers.md +++ /dev/null @@ -1,72 +0,0 @@ -

Sponsors & Backers

- -Boostnote is an open source project. It's an independent project with its ongoing development made possible entirely thanks to the support by these awesome backers. If you'd like to join them, please consider: - -- [Become a backer or sponsor on Open Collective.](https://opencollective.com/boostnoteio) - ---- - -## Backers via OpenCollective - -### [Gold Sponsors / $1,000 per month](https://opencollective.com/boostnoteio/order/2259) -- Get your logo on our Readme.md on GitHub and the frontpage of https://boostnote.io/. - -### [Silver Sponsors / $250 per month](https://opencollective.com/boostnoteio/order/2257) -- Get your logo on our Readme.md on GitHub and the frontpage of https://boostnote.io/. - -### [Bronze Sponsors / $50 per month](https://opencollective.com/boostnoteio/order/2258) -- Get your name and Url (or E-mail) on Readme.md on GitHub. - -### [Backers3 / $10 per month](https://opencollective.com/boostnoteio/order/2176) -- [Ralph03](https://opencollective.com/ralph03) - -- [Nikolas Dan](https://opencollective.com/nikolas-dan) - -### [Backers2 / $5 per month](https://opencollective.com/boostnoteio/order/2175) -- [Yeojong Kim](https://twitter.com/yeojoy) - -- [Scotia Draven](https://opencollective.com/scotia-draven) - -- [A. J. Vargas](https://opencollective.com/aj-vargas) - -### [Backers1](https://opencollective.com/boostnoteio/order/2563) and One-time sponsors -- Ryosuke Tamura - $30 - -- tatoosh11 - $10 - -- Alexander Borovkov - $10 - -- spoonhoop - $5 - -- Drew Williams - $2 - -- Andy Shaw - $2 - -- mysafesky -$2 - ---- - -## Backers via Bountysource -https://salt.bountysource.com/teams/boostnote - -- Kuzz - $65 - -- Intense Raiden - $45 - -- ravy22 - $25 - -- trentpolack - $20 - -- hikariru - $10 - -- kolchan11 - $10 - -- RonWalker22 - $10 - -- hocchuc - $5 - -- Adam - $5 - -- Steve - $5 - -- evmin - $5 diff --git a/browser/main/modals/PreferencesModal/InfoTab.js b/browser/main/modals/PreferencesModal/InfoTab.js index a6acc963..d618fa22 100644 --- a/browser/main/modals/PreferencesModal/InfoTab.js +++ b/browser/main/modals/PreferencesModal/InfoTab.js @@ -73,6 +73,11 @@ class InfoTab extends React.Component {
{i18n.__('Community')}
-
{i18n.__('Paste Smartly')}
+
{i18n.__('Paste HTML')}
this.handleHotkeyChange(e)} diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index becd4f54..964209b2 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -561,7 +561,7 @@ class UiTab extends React.Component { ref='enableSmartPaste' type='checkbox' />  - {i18n.__('Enable smart paste')} + {i18n.__('Enable HTML paste')}
From 6e8fe7308ca0a7460bb01ab01f31371d9b1a5109 Mon Sep 17 00:00:00 2001 From: vienai8d <31370233+vienai8d@users.noreply.github.com> Date: Sat, 15 Dec 2018 08:28:07 +0900 Subject: [PATCH 53/59] implement feature 'saveTagsAlphabeticall' --- browser/main/Detail/TagSelect.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/browser/main/Detail/TagSelect.js b/browser/main/Detail/TagSelect.js index 6ced475b..c5221f66 100644 --- a/browser/main/Detail/TagSelect.js +++ b/browser/main/Detail/TagSelect.js @@ -45,8 +45,14 @@ class TagSelect extends React.Component { value = _.isArray(value) ? value.slice() : [] - value.push(newTag) - value = _.uniq(value) + + if (!_.includes(value, newTag)) { + value.push(newTag) + } + + if (this.props.saveTagsAlphabetically) { + value = _.sortBy(value) + } this.setState({ newTag: '' From 0340402dc1430c53cc36762eb7e0e83042e3bd19 Mon Sep 17 00:00:00 2001 From: mpj <11813607+empeje@users.noreply.github.com> Date: Thu, 27 Dec 2018 11:12:36 +0700 Subject: [PATCH 54/59] Create FAQ.md --- FAQ.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 FAQ.md diff --git a/FAQ.md b/FAQ.md new file mode 100644 index 00000000..6393d8bb --- /dev/null +++ b/FAQ.md @@ -0,0 +1,31 @@ +# Frequently Asked Questions + + +
Allowing dangerous HTML tags + +Sometimes it is useful to allow dangerous HTML tags to add interactivity to your notebook. One of the example is to use details/summary as a way to expand/collaps your todo-list. + +* How to enable: + * Go to **Preferences** → **Interface** → **Sanitization** → **Allow dangerous html tags** +* Example note: Multiple todo-list + * Create new notes + * Paste the below code, and you'll see that you can expand/collaps the todo-list, and you can have multiple todo-list in your note. + +```html +
What I want to do + +- [x] Create an awesome feature X +- [ ] Do my homework + +
+``` + +
+ +
Other questions + +You can ask [here][ISSUES] + +
+ +[ISSUES]: https://github.com/BoostIO/Boostnote/issues From a06bdced8ab9978484ce83d1b1271505b6b8f743 Mon Sep 17 00:00:00 2001 From: mpj <11813607+empeje@users.noreply.github.com> Date: Thu, 27 Dec 2018 13:19:54 +0700 Subject: [PATCH 55/59] Update FAQ.md --- FAQ.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/FAQ.md b/FAQ.md index 6393d8bb..b1c16c8f 100644 --- a/FAQ.md +++ b/FAQ.md @@ -20,12 +20,8 @@ Sometimes it is useful to allow dangerous HTML tags to add interactivity to your ``` - - -
Other questions +# Other questions You can ask [here][ISSUES] -
- [ISSUES]: https://github.com/BoostIO/Boostnote/issues From 9ebf949890a4f5acde8f7c9a3ad08d31ebbbf45b Mon Sep 17 00:00:00 2001 From: mpj <11813607+empeje@users.noreply.github.com> Date: Thu, 27 Dec 2018 13:20:44 +0700 Subject: [PATCH 56/59] Update FAQ.md --- FAQ.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FAQ.md b/FAQ.md index b1c16c8f..3e1a577c 100644 --- a/FAQ.md +++ b/FAQ.md @@ -20,6 +20,8 @@ Sometimes it is useful to allow dangerous HTML tags to add interactivity to your ``` + + # Other questions You can ask [here][ISSUES] From 70e16d853e62f2dd5e66cbfabf5061cad94469a6 Mon Sep 17 00:00:00 2001 From: Junyoung Choi Date: Thu, 27 Dec 2018 15:36:20 +0900 Subject: [PATCH 57/59] Replace heading 1 with heading 2 --- FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FAQ.md b/FAQ.md index 3e1a577c..c7fc4016 100644 --- a/FAQ.md +++ b/FAQ.md @@ -22,7 +22,7 @@ Sometimes it is useful to allow dangerous HTML tags to add interactivity to your -# Other questions +## Other questions You can ask [here][ISSUES] From deb2cd0156b3bdc8ccb6fbaca3b2678e51f634b0 Mon Sep 17 00:00:00 2001 From: HarlanLuo Date: Tue, 1 Jan 2019 16:23:15 +0800 Subject: [PATCH 58/59] new feature: auto detect snippet language only try to detect after pasting and mode has not been set and default snippet language is "Auto Detect" --- browser/components/CodeEditor.js | 97 ++++++++++++++++++- browser/lib/newNote.js | 4 +- browser/main/Detail/SnippetNoteDetail.js | 8 +- browser/main/modals/PreferencesModal/UiTab.js | 1 + package.json | 1 + 5 files changed, 104 insertions(+), 7 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index e9d6badc..6243cf5c 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -2,6 +2,7 @@ import PropTypes from 'prop-types' import React from 'react' import _ from 'lodash' import CodeMirror from 'codemirror' +import hljs from 'highlight.js' import 'codemirror-mode-elixir' import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' import convertModeName from 'browser/lib/convertModeName' @@ -37,6 +38,85 @@ function translateHotkey (hotkey) { return hotkey.replace(/\s*\+\s*/g, '-').replace(/Command/g, 'Cmd').replace(/Control/g, 'Ctrl') } +const languageMaps = { + brainfuck: 'Brainfuck', + cpp: 'C++', + cs: 'C#', + clojure: 'Clojure', + 'clojure-repl': 'ClojureScript', + cmake: 'CMake', + coffeescript: 'CoffeeScript', + crystal: 'Crystal', + css: 'CSS', + d: 'D', + dart: 'Dart', + delphi: 'Pascal', + diff: 'Diff', + django: 'Django', + dockerfile: 'Dockerfile', + ebnf: 'EBNF', + elm: 'Elm', + erlang: 'Erlang', + 'erlang-repl': 'Erlang', + fortran: 'Fortran', + fsharp: 'F#', + gherkin: 'Gherkin', + go: 'Go', + groovy: 'Groovy', + haml: 'HAML', + haskell: 'Haskell', + haxe: 'Haxe', + http: 'HTTP', + ini: 'toml', + java: 'Java', + javascript: 'JavaScript', + json: 'JSON', + julia: 'Julia', + kotlin: 'Kotlin', + less: 'LESS', + livescript: 'LiveScript', + lua: 'Lua', + markdown: 'Markdown', + mathematica: 'Mathematica', + nginx: 'Nginx', + nsis: 'NSIS', + objectivec: 'Objective-C', + ocaml: 'Ocaml', + perl: 'Perl', + php: 'PHP', + powershell: 'PowerShell', + properties: 'Properties files', + protobuf: 'ProtoBuf', + python: 'Python', + puppet: 'Puppet', + q: 'Q', + r: 'R', + ruby: 'Ruby', + rust: 'Rust', + sas: 'SAS', + scala: 'Scala', + scheme: 'Scheme', + scss: 'SCSS', + shell: 'Shell', + smalltalk: 'Smalltalk', + sml: 'SML', + sql: 'SQL', + stylus: 'Stylus', + swift: 'Swift', + tcl: 'Tcl', + tex: 'LaTex', + typescript: 'TypeScript', + twig: 'Twig', + vbnet: 'VB.NET', + vbscript: 'VBScript', + verilog: 'Verilog', + vhdl: 'VHDL', + xml: 'HTML', + xquery: 'XQuery', + yaml: 'YAML', + elixir: 'Elixir' +} + export default class CodeEditor extends React.Component { constructor (props) { super(props) @@ -274,7 +354,11 @@ export default class CodeEditor extends React.Component { extraKeys: this.defaultKeyMap }) - this.setMode(this.props.mode) + if (!this.props.mode && this.props.value) { + this.autoDetectLanguage(this.props.value) + } else { + this.setMode(this.props.mode) + } this.editor.on('focus', this.focusHandler) this.editor.on('blur', this.blurHandler) @@ -644,7 +728,7 @@ export default class CodeEditor extends React.Component { } setMode (mode) { - let syntax = CodeMirror.findModeByName(convertModeName(mode)) + let syntax = CodeMirror.findModeByName(convertModeName(mode || 'text')) if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') this.editor.setOption('mode', syntax.mime) @@ -796,6 +880,11 @@ export default class CodeEditor extends React.Component { this.editor.replaceSelection(imageMd) } + autoDetectLanguage (content) { + const res = hljs.highlightAuto(content, Object.keys(languageMaps)) + this.setMode(languageMaps[res.language]) + } + handlePaste (editor, forceSmartPaste) { const { storageKey, noteKey, fetchUrlTitle, enableSmartPaste } = this.props @@ -892,6 +981,10 @@ export default class CodeEditor extends React.Component { this.handlePasteText(editor, pastedTxt) } } + + if (!this.props.mode) { + this.autoDetectLanguage(editor.doc.getValue()) + } } handleScroll (e) { diff --git a/browser/lib/newNote.js b/browser/lib/newNote.js index 9511f847..d8ef196f 100644 --- a/browser/lib/newNote.js +++ b/browser/lib/newNote.js @@ -46,6 +46,8 @@ export function createSnippetNote (storage, folder, dispatch, location, params, tags = params.tagname.split(' ') } + const defaultLanguage = config.editor.snippetDefaultLanguage === 'Auto Detect' ? null : config.editor.snippetDefaultLanguage + return dataApi .createNote(storage, { type: 'SNIPPET_NOTE', @@ -56,7 +58,7 @@ export function createSnippetNote (storage, folder, dispatch, location, params, snippets: [ { name: '', - mode: config.editor.snippetDefaultLanguage || 'text', + mode: defaultLanguage, content: '', linesHighlighted: [] } diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index d8b5798d..12545335 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -599,12 +599,14 @@ class SnippetNoteDetail extends React.Component { } addSnippet () { - const { config } = this.props + const { config: { editor: { snippetDefaultLanguage } } } = this.props const { note } = this.state + const defaultLanguage = snippetDefaultLanguage === 'Auto Detect' ? null : snippetDefaultLanguage + note.snippets = note.snippets.concat([{ name: '', - mode: config.editor.snippetDefaultLanguage || 'text', + mode: defaultLanguage, content: '', linesHighlighted: [] }]) @@ -696,8 +698,6 @@ class SnippetNoteDetail extends React.Component { const viewList = note.snippets.map((snippet, index) => { const isActive = this.state.snippetIndex === index - let syntax = CodeMirror.findModeByName(convertModeName(snippet.mode)) - if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') return
this.handleUIChange(e)} > + { _.sortBy(CodeMirror.modeInfo.map(mode => mode.name)).map(name => ()) } diff --git a/package.json b/package.json index f12360a0..578e2c7b 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "flowchart.js": "^1.6.5", "font-awesome": "^4.3.0", "fs-extra": "^5.0.0", + "highlight.js": "^9.13.1", "i18n-2": "^0.7.2", "iconv-lite": "^0.4.19", "immutable": "^3.8.1", From 082a078b511d96458dfe0b4a6e5c3a73cf0f3cf2 Mon Sep 17 00:00:00 2001 From: HarlanLuo Date: Fri, 4 Jan 2019 21:55:58 +0800 Subject: [PATCH 59/59] check config before auto detect language --- browser/components/CodeEditor.js | 6 ++++-- browser/main/Detail/SnippetNoteDetail.js | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 6243cf5c..48634993 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -354,7 +354,7 @@ export default class CodeEditor extends React.Component { extraKeys: this.defaultKeyMap }) - if (!this.props.mode && this.props.value) { + if (!this.props.mode && this.props.value && this.props.autoDetect) { this.autoDetectLanguage(this.props.value) } else { this.setMode(this.props.mode) @@ -982,7 +982,7 @@ export default class CodeEditor extends React.Component { } } - if (!this.props.mode) { + if (!this.props.mode && this.props.autoDetect) { this.autoDetectLanguage(editor.doc.getValue()) } } @@ -1184,6 +1184,7 @@ CodeEditor.propTypes = { onBlur: PropTypes.func, onChange: PropTypes.func, readOnly: PropTypes.bool, + autoDetect: PropTypes.bool, spellCheck: PropTypes.bool } @@ -1195,5 +1196,6 @@ CodeEditor.defaultProps = { fontFamily: 'Monaco, Consolas', indentSize: 4, indentType: 'space', + autoDetect: false, spellCheck: false } diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 12545335..e9b0ee37 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -674,6 +674,8 @@ class SnippetNoteDetail extends React.Component { const storageKey = note.storage const folderKey = note.folder + const autoDetect = config.editor.snippetDefaultLanguage === 'Auto Detect' + let editorFontSize = parseInt(config.editor.fontSize, 10) if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14 let editorIndentSize = parseInt(config.editor.indentSize, 10) @@ -713,7 +715,7 @@ class SnippetNoteDetail extends React.Component { storageKey={storageKey} /> : }