You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-29 22:48:10 +02:00
Merge branch 'dev' of github.com:laurent22/joplin into dev
This commit is contained in:
@@ -164,6 +164,18 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
replaceSelection: (value: any) => {
|
||||
return editorRef.current.replaceSelection(value);
|
||||
},
|
||||
textCopy: () => {
|
||||
editorCopyText();
|
||||
},
|
||||
textCut: () => {
|
||||
editorCutText();
|
||||
},
|
||||
textPaste: () => {
|
||||
editorPaste();
|
||||
},
|
||||
textSelectAll: () => {
|
||||
return editorRef.current.execCommand('selectAll');
|
||||
},
|
||||
textBold: () => wrapSelectionWithStrings('**', '**', _('strong text')),
|
||||
textItalic: () => wrapSelectionWithStrings('*', '*', _('emphasised text')),
|
||||
textLink: async () => {
|
||||
@@ -210,6 +222,8 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
|
||||
if (commands[cmd.name]) {
|
||||
commandOutput = commands[cmd.name](cmd.value);
|
||||
} else if (editorRef.current.supportsCommand(cmd)) {
|
||||
commandOutput = editorRef.current.execCommandFromJoplinCommand(cmd);
|
||||
} else {
|
||||
reg.logger().warn('CodeMirror: unsupported Joplin command: ', cmd);
|
||||
}
|
||||
@@ -255,6 +269,17 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const editorPaste = useCallback(() => {
|
||||
const clipboardText = clipboard.readText();
|
||||
|
||||
if (clipboardText) {
|
||||
editorPasteText();
|
||||
} else {
|
||||
// To handle pasting images
|
||||
void onEditorPaste();
|
||||
}
|
||||
}, [editorPasteText, onEditorPaste]);
|
||||
|
||||
const loadScript = async (script: any) => {
|
||||
return new Promise((resolve) => {
|
||||
let element: any = document.createElement('script');
|
||||
@@ -598,7 +623,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
const menu = new Menu();
|
||||
|
||||
const hasSelectedText = editorRef.current && !!editorRef.current.getSelection() ;
|
||||
const clipboardText = clipboard.readText();
|
||||
|
||||
menu.append(
|
||||
new MenuItem({
|
||||
@@ -625,12 +649,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
label: _('Paste'),
|
||||
enabled: true,
|
||||
click: async () => {
|
||||
if (clipboardText) {
|
||||
editorPasteText();
|
||||
} else {
|
||||
// To handle pasting images
|
||||
void onEditorPaste();
|
||||
}
|
||||
editorPaste();
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
@@ -105,8 +105,8 @@ function Editor(props: EditorProps, ref: any) {
|
||||
useLineSorting(CodeMirror);
|
||||
useEditorSearch(CodeMirror);
|
||||
useJoplinMode(CodeMirror);
|
||||
useKeymap(CodeMirror);
|
||||
const pluginOptions: any = useExternalPlugins(CodeMirror, props.plugins);
|
||||
useKeymap(CodeMirror);
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
return editor;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { useEffect } from 'react';
|
||||
import CommandService from '@joplin/lib/services/CommandService';
|
||||
import KeymapService, { KeymapItem } from '@joplin/lib/services/KeymapService';
|
||||
import { EditorCommand } from '../../../utils/types';
|
||||
import shim from '@joplin/lib/shim';
|
||||
const { reg } = require('@joplin/lib/registry.js');
|
||||
|
||||
export default function useKeymap(CodeMirror: any) {
|
||||
|
||||
@@ -23,6 +26,77 @@ export default function useKeymap(CodeMirror: any) {
|
||||
CodeMirror.Vim.mapCommand('o', 'action', 'insertListElement', { after: true }, { context: 'normal', isEdit: true, interlaceInsertRepeat: true });
|
||||
}
|
||||
|
||||
function isEditorCommand(command: string) {
|
||||
return command.startsWith('editor.');
|
||||
}
|
||||
|
||||
// Converts a command of the form editor.command to just command
|
||||
function editorCommandToCodeMirror(command: String) {
|
||||
return command.slice(7); // 7 is the length of editor.
|
||||
}
|
||||
|
||||
// CodeMirror and Electron register accelerators slightly different
|
||||
// CodeMirror requires a - between keys while Electron want's a +
|
||||
// CodeMirror doesn't recognize Option (it uses Alt instead)
|
||||
// This function uses simple regex to translate the Electron
|
||||
// accelerator to a CodeMirror accelerator
|
||||
function normalizeAccelerator(accelerator: String) {
|
||||
return accelerator.replace(/\+/g, '-').replace('Option', 'Alt');
|
||||
}
|
||||
|
||||
// Because there is sometimes a clash between these keybindings and the Joplin window ones
|
||||
// (This specifically can happen with the Ctrl-B and Ctrl-I keybindings when
|
||||
// codemirror is in contenteditable mode)
|
||||
// we will register all keypresses with the codemirror editor to guarentee they
|
||||
// work no matter where the focus is
|
||||
function registerJoplinCommand(key: KeymapItem) {
|
||||
if (!key.command || !key.accelerator) return;
|
||||
|
||||
let command = '';
|
||||
if (isEditorCommand(key.command)) {
|
||||
command = editorCommandToCodeMirror(key.command);
|
||||
} else {
|
||||
// We need to register Joplin commands with codemirror
|
||||
command = `joplin${key.command}`;
|
||||
// Not all commands are registered with the command service
|
||||
// (for example, the Quit command)
|
||||
// This check will ensure that codemirror only takesover the commands that are
|
||||
// see gui/KeymapConfig/getLabel.ts for more information
|
||||
const commandNames = CommandService.instance().commandNames();
|
||||
if (commandNames.includes(key.command)) {
|
||||
CodeMirror.commands[command] = () => {
|
||||
void CommandService.instance().execute(key.command);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// CodeMirror and Electron have slightly different formats for defining accelerators
|
||||
const acc = normalizeAccelerator(key.accelerator);
|
||||
|
||||
CodeMirror.keyMap.default[acc] = command;
|
||||
}
|
||||
|
||||
// Called on initialization, and whenever the keymap changes
|
||||
function registerKeymap() {
|
||||
const keymapItems = KeymapService.instance().getKeymapItems();
|
||||
// Register all commands with the codemirror editor
|
||||
keymapItems.forEach((key) => { registerJoplinCommand(key); });
|
||||
}
|
||||
|
||||
CodeMirror.defineExtension('supportsCommand', function(cmd: EditorCommand) {
|
||||
return isEditorCommand(cmd.name) && editorCommandToCodeMirror(cmd.name) in CodeMirror.commands;
|
||||
});
|
||||
|
||||
// Used when an editor command is executed using the CommandService.instance().execute
|
||||
// function (rather than being initiated by a keypress in the editor)
|
||||
CodeMirror.defineExtension('execCommandFromJoplin', function(cmd: EditorCommand) {
|
||||
if (cmd.value) {
|
||||
reg.logger().warn('CodeMirror commands cannot accept a value:', cmd);
|
||||
}
|
||||
|
||||
return this.execCommand(editorCommandToCodeMirror(cmd.name));
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// This enables the special modes (emacs and vim) to initiate sync by the save action
|
||||
CodeMirror.commands.save = save;
|
||||
@@ -46,61 +120,57 @@ export default function useKeymap(CodeMirror: any) {
|
||||
'Esc': 'singleSelection',
|
||||
};
|
||||
|
||||
// Some keybindings are added here and not to the global registry because users
|
||||
// often expect multiple keys to bind to the same command for example, redo is mapped to
|
||||
// both Ctrl+Shift+Z AND Ctrl+Y
|
||||
// Doing this mapping here will make those commands available but will allow users to
|
||||
// override them using the KeymapService
|
||||
CodeMirror.keyMap.default = {
|
||||
// Windows / Linux
|
||||
'Ctrl-Z': 'undo',
|
||||
'Shift-Ctrl-Z': 'redo',
|
||||
'Ctrl-Y': 'redo',
|
||||
'Ctrl-Up': 'goLineUp',
|
||||
'Ctrl-Down': 'go,ineDown',
|
||||
'Ctrl+Home': 'goDocStart',
|
||||
'Ctrl+End': 'goDocEnd',
|
||||
'Ctrl+Left': 'goGroupLeft',
|
||||
'Ctrl+Right': 'goGroupRight',
|
||||
'Alt+Left': 'goLineStart',
|
||||
'Alt+Right': 'goLineEnd',
|
||||
'Ctrl+Backspace': 'delGroupBefore',
|
||||
'Ctrl+Delete': 'delGroupAfter',
|
||||
|
||||
'fallthrough': 'basic',
|
||||
};
|
||||
if (shim.isMac()) {
|
||||
CodeMirror.keyMap.default = {
|
||||
// MacOS
|
||||
'Cmd-A': 'selectAll',
|
||||
'Cmd-D': 'deleteLine',
|
||||
'Cmd-Z': 'undo',
|
||||
'Shift-Cmd-Z': 'redo',
|
||||
'Cmd-Y': 'redo',
|
||||
'Cmd-Home': 'goDocStart',
|
||||
'Cmd-Up': 'goDocStart',
|
||||
'Cmd-End': 'goDocEnd',
|
||||
'Cmd-Down': 'goDocEnd',
|
||||
'Cmd-Left': 'goLineLeft',
|
||||
'Cmd-Right': 'goLineRight',
|
||||
'Alt-Left': 'goGroupLeft',
|
||||
'Alt-Right': 'goGroupRight',
|
||||
'Alt-Backspace': 'delGroupBefore',
|
||||
'Alt-Delete': 'delGroupAfter',
|
||||
'Cmd-[': 'indentLess',
|
||||
'Cmd-]': 'indentMore',
|
||||
'Cmd-/': 'toggleComment',
|
||||
'Cmd-Opt-S': 'sortSelectedLines',
|
||||
'Opt-Up': 'swapLineUp',
|
||||
'Opt-Down': 'swapLineDown',
|
||||
|
||||
'fallthrough': 'basic',
|
||||
};
|
||||
} else {
|
||||
CodeMirror.keyMap.default = {
|
||||
// Windows/linux
|
||||
'Ctrl-A': 'selectAll',
|
||||
'Ctrl-D': 'deleteLine',
|
||||
'Ctrl-Z': 'undo',
|
||||
'Shift-Ctrl-Z': 'redo',
|
||||
'Ctrl-Y': 'redo',
|
||||
'Ctrl-Home': 'goDocStart',
|
||||
'Ctrl-End': 'goDocEnd',
|
||||
'Ctrl-Up': 'goLineUp',
|
||||
'Ctrl-Down': 'goLineDown',
|
||||
'Ctrl-Left': 'goGroupLeft',
|
||||
'Ctrl-Right': 'goGroupRight',
|
||||
'Alt-Left': 'goLineStart',
|
||||
'Alt-Right': 'goLineEnd',
|
||||
'Ctrl-Backspace': 'delGroupBefore',
|
||||
'Ctrl-Delete': 'delGroupAfter',
|
||||
'Ctrl-[': 'indentLess',
|
||||
'Ctrl-]': 'indentMore',
|
||||
'Ctrl-/': 'toggleComment',
|
||||
'Ctrl-Alt-S': 'sortSelectedLines',
|
||||
'Alt-Up': 'swapLineUp',
|
||||
'Alt-Down': 'swapLineDown',
|
||||
'Cmd-Home': 'goDocStart',
|
||||
'Cmd-Up': 'goDocStart',
|
||||
'Ctrl-D': 'delCharAfter',
|
||||
'Cmd+Home': 'goDocStart',
|
||||
'Cmd+End': 'goDocEnd',
|
||||
'Cmd+Left': 'goGroupLeft',
|
||||
'Cmd+Right': 'goGroupRight',
|
||||
'Ctrl+A': 'goLineStart',
|
||||
'Ctrl+E': 'goLineEnd',
|
||||
'Alt+Backspace': 'delGroupBefore',
|
||||
'Alt+Delete': 'delGroupAfter',
|
||||
|
||||
'fallthrough': 'basic',
|
||||
};
|
||||
}
|
||||
|
||||
const keymapService = KeymapService.instance();
|
||||
|
||||
registerKeymap();
|
||||
keymapService.on('keymapChange', registerKeymap);
|
||||
|
||||
setupEmacs();
|
||||
setupVim();
|
||||
}, []);
|
||||
|
||||
@@ -83,6 +83,42 @@ const declarations: CommandDeclaration[] = [
|
||||
label: () => _('Insert Date Time'),
|
||||
iconName: 'icon-add-date',
|
||||
},
|
||||
{
|
||||
name: 'editor.deleteLine',
|
||||
label: _('Delete line'),
|
||||
},
|
||||
{
|
||||
name: 'editor.undo',
|
||||
label: _('Undo'),
|
||||
},
|
||||
{
|
||||
name: 'editor.redo',
|
||||
label: _('Redo'),
|
||||
},
|
||||
{
|
||||
name: 'editor.indentLess',
|
||||
label: _('Indent less'),
|
||||
},
|
||||
{
|
||||
name: 'editor.indentMore',
|
||||
label: _('Indent more'),
|
||||
},
|
||||
{
|
||||
name: 'editor.toggleComment',
|
||||
label: _('Toggle comment'),
|
||||
},
|
||||
{
|
||||
name: 'editor.sortSelectedLines',
|
||||
label: _('Sort selected lines'),
|
||||
},
|
||||
{
|
||||
name: 'editor.swapLineUp',
|
||||
label: _('Swap line up'),
|
||||
},
|
||||
{
|
||||
name: 'editor.swapLineDown',
|
||||
label: _('Swap line down'),
|
||||
},
|
||||
{
|
||||
name: 'selectedText',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user