From 24f5b607c75d331abfc3310685096bbb9d0d5f21 Mon Sep 17 00:00:00 2001 From: Kat Hagan Date: Wed, 19 Aug 2020 00:28:21 -0700 Subject: [PATCH 01/11] disable keybindings Monaco is overriding --- lib/note-content-editor.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/note-content-editor.tsx b/lib/note-content-editor.tsx index f483dbeb1..25a4286c6 100644 --- a/lib/note-content-editor.tsx +++ b/lib/note-content-editor.tsx @@ -200,6 +200,22 @@ class NoteContentEditor extends Component { this.editor = editor; this.monaco = monaco; + // remove keybindings; see https://github.com/microsoft/monaco-editor/issues/287 + const shortcutsToDisable = [ + 'editor.action.commentLine', // meta+/ + 'editor.action.transposeLetters', // ctrl+T + 'editor.action.triggerSuggest', // ctrl+space + 'expandLineSelection', + // search shortcuts + 'actions.find', + 'actions.findWithSelection', + 'editor.action.addSelectionToNextFindMatch', + 'editor.action.nextMatchFindAction', + ]; + shortcutsToDisable.forEach(function (action) { + editor._standaloneKeybindingService.addDynamicKeybinding('-' + action); + }); + window.electron?.receive('editorCommand', (command) => { switch (command.action) { case 'redo': From 23477601bef4243e9118b4fe56e14bdf17755098 Mon Sep 17 00:00:00 2001 From: Kat Hagan Date: Wed, 19 Aug 2020 00:36:54 -0700 Subject: [PATCH 02/11] also remove selectHighlights --- lib/note-content-editor.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/note-content-editor.tsx b/lib/note-content-editor.tsx index 25a4286c6..a2365d4f1 100644 --- a/lib/note-content-editor.tsx +++ b/lib/note-content-editor.tsx @@ -211,6 +211,7 @@ class NoteContentEditor extends Component { 'actions.findWithSelection', 'editor.action.addSelectionToNextFindMatch', 'editor.action.nextMatchFindAction', + 'editor.action.selectHighlights', ]; shortcutsToDisable.forEach(function (action) { editor._standaloneKeybindingService.addDynamicKeybinding('-' + action); From b27d7fc1bb9c4caa52aa8b6296a60fc9e212e584 Mon Sep 17 00:00:00 2001 From: Kat Hagan Date: Wed, 19 Aug 2020 11:24:33 -0700 Subject: [PATCH 03/11] remove cursorUndo, jumpToBracket --- lib/note-content-editor.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/note-content-editor.tsx b/lib/note-content-editor.tsx index a2365d4f1..123ea2470 100644 --- a/lib/note-content-editor.tsx +++ b/lib/note-content-editor.tsx @@ -202,10 +202,12 @@ class NoteContentEditor extends Component { // remove keybindings; see https://github.com/microsoft/monaco-editor/issues/287 const shortcutsToDisable = [ + 'cursorUndo', // meta+U 'editor.action.commentLine', // meta+/ + 'editor.action.jumpToBracket', // shift+meta+\ 'editor.action.transposeLetters', // ctrl+T 'editor.action.triggerSuggest', // ctrl+space - 'expandLineSelection', + 'expandLineSelection', // meta+L // search shortcuts 'actions.find', 'actions.findWithSelection', From 0e1ea3a3f8e88a4fa740a990adaf8efbf820e344 Mon Sep 17 00:00:00 2001 From: Kat Hagan Date: Wed, 19 Aug 2020 14:28:55 -0700 Subject: [PATCH 04/11] first hack at checkbox insertion --- lib/note-content-editor.tsx | 55 +++++++++++++++++++++++++------- lib/state/electron/middleware.ts | 4 --- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/lib/note-content-editor.tsx b/lib/note-content-editor.tsx index 123ea2470..b39a62ea2 100644 --- a/lib/note-content-editor.tsx +++ b/lib/note-content-editor.tsx @@ -86,7 +86,6 @@ class NoteContentEditor extends Component { componentDidMount() { const { noteId } = this.props; - window.addEventListener('keydown', this.handleKeys, true); this.bootTimer = setTimeout(() => { if (noteId === this.props.noteId) { this.setState({ @@ -102,7 +101,7 @@ class NoteContentEditor extends Component { clearTimeout(this.bootTimer); } window.electron?.removeListener('editorCommand'); - window.removeEventListener('keydown', this.handleKeys, true); + window.electron?.removeListener('appCommand'); } componentDidUpdate(prevProps: Props) { @@ -147,22 +146,34 @@ class NoteContentEditor extends Component { } } - handleKeys = (event: KeyboardEvent) => { - if (!this.props.keyboardShortcuts) { + insertOrRemoveCheckboxes = (editor: Editor.IStandaloneCodeEditor) => { + // todo: we're not disabling this if !this.props.keyboardShortcuts, do we want to? + const model = editor.getModel(); + if (!model) { return; } - const { code, ctrlKey, metaKey, shiftKey } = event; - const cmdOrCtrl = ctrlKey || metaKey; + const position = editor.getPosition(); + if (!position) { + return; + } + const lineNumber = position.lineNumber; + const thisLine = model.getLineContent(lineNumber); + + let range = new this.monaco.Range(lineNumber, 0, lineNumber, 0); + let text = '\ue000 '; - if (cmdOrCtrl && shiftKey && 'KeyC' === code) { - this.props.insertTask(); - event.stopPropagation(); - event.preventDefault(); - return false; + // if line already starts with a checkbox, remove it + if (thisLine.startsWith('\ue000 ') || thisLine.startsWith('\ue001 ')) { + range = new this.monaco.Range(lineNumber, 0, lineNumber, 3); + text = ''; } - return true; + const identifier = { major: 1, minor: 1 }; + const op = { identifier, range, text, forceMoveMarkers: true }; + editor.executeEdits('insertOrRemoveCheckboxes', [op]); + + this.props.insertTask(); }; editorInit: EditorWillMount = () => { @@ -219,6 +230,18 @@ class NoteContentEditor extends Component { editor._standaloneKeybindingService.addDynamicKeybinding('-' + action); }); + editor.addAction({ + id: 'insertChecklist', + label: 'Insert Checklist', + // we don't need to add keybindings for Electron since it is handled by appCommand + keybindings: window.electron + ? [] + : [monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KEY_C], + contextMenuGroupId: '1_modification', + contextMenuOrder: 2.5, + run: this.insertOrRemoveCheckboxes, + }); + window.electron?.receive('editorCommand', (command) => { switch (command.action) { case 'redo': @@ -233,6 +256,14 @@ class NoteContentEditor extends Component { } }); + window.electron?.receive('appCommand', (command) => { + switch (command.action) { + case 'insertChecklist': + editor.trigger('appCommand', 'insertChecklist', null); + return; + } + }); + const titleDecoration = (line: number) => ({ range: new monaco.Range(line, 1, line, 1), options: { diff --git a/lib/state/electron/middleware.ts b/lib/state/electron/middleware.ts index 58b19da0d..1d660d191 100644 --- a/lib/state/electron/middleware.ts +++ b/lib/state/electron/middleware.ts @@ -29,10 +29,6 @@ export const middleware: S.Middleware = ({ dispatch, getState }) => { dispatch(actions.ui.focusSearchField()); return; - case 'insertChecklist': - dispatch({ type: 'INSERT_TASK' }); - return; - case 'showDialog': dispatch(actions.ui.showDialog(command.dialog)); return; From 67618dc760a6da3df2f8531d3de364a8cc01bd0e Mon Sep 17 00:00:00 2001 From: Kat Hagan Date: Wed, 19 Aug 2020 14:52:17 -0700 Subject: [PATCH 05/11] change tag toggle back to a toggle --- lib/app.tsx | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/app.tsx b/lib/app.tsx index 2538066a1..c7e47d572 100644 --- a/lib/app.tsx +++ b/lib/app.tsx @@ -36,7 +36,6 @@ type DispatchProps = { closeNote: () => any; createNote: () => any; focusSearchField: () => any; - openTagList: () => any; setLineLength: (length: T.LineLength) => any; setNoteDisplay: (displayMode: T.ListDisplayMode) => any; setSortType: (sortType: T.SortType) => any; @@ -45,6 +44,7 @@ type DispatchProps = { toggleSortOrder: () => any; toggleSortTagsAlpha: () => any; toggleSpellCheck: () => any; + toggleTagList: () => any; }; type Props = OwnProps & StateProps & DispatchProps; @@ -76,13 +76,8 @@ class AppComponent extends Component { const cmdOrCtrl = (ctrlKey || metaKey) && ctrlKey !== metaKey; // open tag list - if ( - cmdOrCtrl && - shiftKey && - 'KeyU' === code && - !this.props.showNavigation - ) { - this.props.openTagList(); + if (cmdOrCtrl && shiftKey && 'KeyU' === code) { + this.props.toggleTagList(); event.stopPropagation(); event.preventDefault(); @@ -186,7 +181,6 @@ const mapDispatchToProps: S.MapDispatch = (dispatch) => { closeNote: () => dispatch(closeNote()), createNote: () => dispatch(createNote()), focusSearchField: () => dispatch(actions.ui.focusSearchField()), - openTagList: () => dispatch(toggleNavigation()), setLineLength: (length) => dispatch(settingsActions.setLineLength(length)), setNoteDisplay: (displayMode) => dispatch(settingsActions.setNoteDisplay(displayMode)), @@ -197,6 +191,7 @@ const mapDispatchToProps: S.MapDispatch = (dispatch) => { toggleSortOrder: () => dispatch(settingsActions.toggleSortOrder()), toggleSortTagsAlpha: () => dispatch(settingsActions.toggleSortTagsAlpha()), toggleSpellCheck: () => dispatch(settingsActions.toggleSpellCheck()), + toggleTagList: () => dispatch(toggleNavigation()), }; }; From 2df3880ff54ac371d4725bfe156ca2ca5e5c3a22 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 20 Aug 2020 16:37:25 -0500 Subject: [PATCH 06/11] Expand detection of tasks and bullets for various line contents --- lib/note-content-editor.tsx | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/note-content-editor.tsx b/lib/note-content-editor.tsx index b39a62ea2..f41149950 100644 --- a/lib/note-content-editor.tsx +++ b/lib/note-content-editor.tsx @@ -160,15 +160,29 @@ class NoteContentEditor extends Component { const lineNumber = position.lineNumber; const thisLine = model.getLineContent(lineNumber); - let range = new this.monaco.Range(lineNumber, 0, lineNumber, 0); - let text = '\ue000 '; - - // if line already starts with a checkbox, remove it - if (thisLine.startsWith('\ue000 ') || thisLine.startsWith('\ue001 ')) { - range = new this.monaco.Range(lineNumber, 0, lineNumber, 3); - text = ''; + // "(1)A line without leading space" + // "(1 )A line with leading space" + // "(1 )(3\ue000 )A line with a task and leading space" + // "(1 )(2- )A line with a bullet" + // "(1 )(2* )(3\ue001 )Bulleted task" + const match = /^(\s*)([-+*\u2022]\s*)?([\ue000\ue001]\s+)?/.exec(thisLine); + if (!match) { + // this shouldn't be able to fail since it requires no characters + return; } + const [fullMatch, prefixSpace, bullet, existingTask] = match; + const hasTask = 'undefined' !== typeof existingTask; + + const lineOffset = prefixSpace.length + (bullet?.length ?? 0) + 1; + const text = hasTask ? '' : '\ue000 '; + const range = new this.monaco.Range( + lineNumber, + lineOffset, + lineNumber, + lineOffset + (existingTask?.length ?? 0) + ); + const identifier = { major: 1, minor: 1 }; const op = { identifier, range, text, forceMoveMarkers: true }; editor.executeEdits('insertOrRemoveCheckboxes', [op]); From 9003556f25882b06f0359f0890d048e94403885c Mon Sep 17 00:00:00 2001 From: Kat Hagan Date: Thu, 20 Aug 2020 21:18:42 -0700 Subject: [PATCH 07/11] fix cmd+A select all --- desktop/menus/edit-menu.js | 1 + 1 file changed, 1 insertion(+) diff --git a/desktop/menus/edit-menu.js b/desktop/menus/edit-menu.js index 41e7fb1e6..c183928dd 100644 --- a/desktop/menus/edit-menu.js +++ b/desktop/menus/edit-menu.js @@ -33,6 +33,7 @@ const buildEditMenu = (settings, isAuthenticated) => { { label: '&Select All', click: editorCommandSender({ action: 'selectAll' }), + role: 'selectAll', }, { type: 'separator' }, { From 344b826d538df741fdfe2bbf64f9bca289b57cdf Mon Sep 17 00:00:00 2001 From: Kat Hagan Date: Thu, 20 Aug 2020 22:10:02 -0700 Subject: [PATCH 08/11] fix tag/editor toggle --- lib/note-content-editor.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/note-content-editor.tsx b/lib/note-content-editor.tsx index f41149950..e075702dd 100644 --- a/lib/note-content-editor.tsx +++ b/lib/note-content-editor.tsx @@ -19,6 +19,11 @@ import * as T from './types'; const SPEED_DELAY = 120; +type OwnProps = { + storeFocusEditor: (focusSetter: () => any) => any; + storeHasFocus: (focusGetter: () => boolean) => any; +}; + type StateProps = { editorSelection: [number, number, 'RTL' | 'LTR']; fontSize: number; @@ -42,7 +47,7 @@ type DispatchProps = { ) => any; }; -type Props = StateProps & DispatchProps; +type Props = OwnProps & StateProps & DispatchProps; type OwnState = { content: string; @@ -94,6 +99,8 @@ class NoteContentEditor extends Component { }); } }, SPEED_DELAY); + this.props.storeFocusEditor(this.focusEditor); + this.props.storeHasFocus(this.hasFocus); } componentWillUnmount() { @@ -146,6 +153,10 @@ class NoteContentEditor extends Component { } } + focusEditor = () => this.editor?.focus(); + + hasFocus = () => this.editor?.hasTextFocus(); + insertOrRemoveCheckboxes = (editor: Editor.IStandaloneCodeEditor) => { // todo: we're not disabling this if !this.props.keyboardShortcuts, do we want to? const model = editor.getModel(); From cf05fa64066bc03302b72e6f9398c60540b57c98 Mon Sep 17 00:00:00 2001 From: Kat Hagan Date: Thu, 20 Aug 2020 23:20:01 -0700 Subject: [PATCH 09/11] use a context key to always show keyboard hint in the context menu --- lib/note-content-editor.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/note-content-editor.tsx b/lib/note-content-editor.tsx index e075702dd..c44107f51 100644 --- a/lib/note-content-editor.tsx +++ b/lib/note-content-editor.tsx @@ -255,15 +255,22 @@ class NoteContentEditor extends Component { editor._standaloneKeybindingService.addDynamicKeybinding('-' + action); }); + // disable editor keybindings for Electron since it is handled by appCommand + // doing it this way will always show the keyboard hint in the context menu! + editor.createContextKey( + 'allowBrowserKeybinding', + window.electron ? false : true + ); + editor.addAction({ id: 'insertChecklist', label: 'Insert Checklist', - // we don't need to add keybindings for Electron since it is handled by appCommand - keybindings: window.electron - ? [] - : [monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KEY_C], - contextMenuGroupId: '1_modification', - contextMenuOrder: 2.5, + keybindings: [ + monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KEY_C, + ], + contextMenuGroupId: '9_cutcopypaste', + contextMenuOrder: 9, + keybindingContext: 'allowBrowserKeybinding', run: this.insertOrRemoveCheckboxes, }); From d4488c88e810cb0daa84064599cec315c4f08359 Mon Sep 17 00:00:00 2001 From: Kat Hagan Date: Fri, 21 Aug 2020 14:15:15 -0700 Subject: [PATCH 10/11] use editorCommand instead of appCommand for insertChecklist --- desktop/menus/format-menu.js | 4 ++-- lib/note-content-editor.tsx | 14 ++++---------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/desktop/menus/format-menu.js b/desktop/menus/format-menu.js index 79becb6d9..ff03cad46 100644 --- a/desktop/menus/format-menu.js +++ b/desktop/menus/format-menu.js @@ -1,4 +1,4 @@ -const { appCommandSender } = require('./utils'); +const { editorCommandSender } = require('./utils'); const buildFormatMenu = (isAuthenticated) => { isAuthenticated = isAuthenticated || false; @@ -6,7 +6,7 @@ const buildFormatMenu = (isAuthenticated) => { { label: 'Insert &Checklist', accelerator: 'CommandOrControl+Shift+C', - click: appCommandSender({ action: 'insertChecklist' }), + click: editorCommandSender({ action: 'insertChecklist' }), }, ]; diff --git a/lib/note-content-editor.tsx b/lib/note-content-editor.tsx index c44107f51..dbd57672f 100644 --- a/lib/note-content-editor.tsx +++ b/lib/note-content-editor.tsx @@ -108,7 +108,6 @@ class NoteContentEditor extends Component { clearTimeout(this.bootTimer); } window.electron?.removeListener('editorCommand'); - window.electron?.removeListener('appCommand'); } componentDidUpdate(prevProps: Props) { @@ -255,7 +254,7 @@ class NoteContentEditor extends Component { editor._standaloneKeybindingService.addDynamicKeybinding('-' + action); }); - // disable editor keybindings for Electron since it is handled by appCommand + // disable editor keybindings for Electron since it is handled by editorCommand // doing it this way will always show the keyboard hint in the context menu! editor.createContextKey( 'allowBrowserKeybinding', @@ -276,6 +275,9 @@ class NoteContentEditor extends Component { window.electron?.receive('editorCommand', (command) => { switch (command.action) { + case 'insertChecklist': + editor.trigger('editorCommand', 'insertChecklist', null); + return; case 'redo': editor.trigger('', 'redo'); return; @@ -288,14 +290,6 @@ class NoteContentEditor extends Component { } }); - window.electron?.receive('appCommand', (command) => { - switch (command.action) { - case 'insertChecklist': - editor.trigger('appCommand', 'insertChecklist', null); - return; - } - }); - const titleDecoration = (line: number) => ({ range: new monaco.Range(line, 1, line, 1), options: { From 71a4a8143932ec6e5b41950e6ff904d9ae181e30 Mon Sep 17 00:00:00 2001 From: Kat Hagan Date: Fri, 21 Aug 2020 14:30:38 -0700 Subject: [PATCH 11/11] fix Electron menu items --- desktop/menus/edit-menu.js | 5 ++++- lib/note-content-editor.tsx | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/desktop/menus/edit-menu.js b/desktop/menus/edit-menu.js index c183928dd..dd34e79e6 100644 --- a/desktop/menus/edit-menu.js +++ b/desktop/menus/edit-menu.js @@ -10,10 +10,12 @@ const buildEditMenu = (settings, isAuthenticated) => { { label: '&Undo', click: editorCommandSender({ action: 'undo' }), + accelerator: 'CommandOrControl+Z', }, { label: '&Redo', click: editorCommandSender({ action: 'redo' }), + accelerator: 'CommandOrControl+Shift+Z', }, { type: 'separator', @@ -33,7 +35,7 @@ const buildEditMenu = (settings, isAuthenticated) => { { label: '&Select All', click: editorCommandSender({ action: 'selectAll' }), - role: 'selectAll', + accelerator: 'CommandOrControl+A', }, { type: 'separator' }, { @@ -46,6 +48,7 @@ const buildEditMenu = (settings, isAuthenticated) => { label: 'Search &Notes…', visible: isAuthenticated, click: appCommandSender({ action: 'focusSearchField' }), + accelerator: 'CommandOrControl+Shift+S', }, { type: 'separator' }, { diff --git a/lib/note-content-editor.tsx b/lib/note-content-editor.tsx index dbd57672f..d8ebad446 100644 --- a/lib/note-content-editor.tsx +++ b/lib/note-content-editor.tsx @@ -250,6 +250,10 @@ class NoteContentEditor extends Component { 'editor.action.nextMatchFindAction', 'editor.action.selectHighlights', ]; + // let Electron menus trigger these + if (window.electron) { + shortcutsToDisable.push('undo', 'redo', 'editor.action.selectAll'); + } shortcutsToDisable.forEach(function (action) { editor._standaloneKeybindingService.addDynamicKeybinding('-' + action); }); @@ -279,13 +283,13 @@ class NoteContentEditor extends Component { editor.trigger('editorCommand', 'insertChecklist', null); return; case 'redo': - editor.trigger('', 'redo'); + editor.trigger('editorCommand', 'redo', null); return; case 'selectAll': editor.setSelection(editor.getModel().getFullModelRange()); return; case 'undo': - editor.trigger('', 'undo'); + editor.trigger('editorCommand', 'undo', null); return; } });