1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-11 18:24:43 +02:00

Desktop: Plugins: Allow commands registered within the beta markdown editor to take arguments (#9963)

This commit is contained in:
Henry Heino 2024-02-22 13:30:48 -08:00 committed by GitHub
parent e5e191110c
commit fbeaf31a14
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 57 additions and 16 deletions

View File

@ -533,6 +533,8 @@ export interface MarkdownItContentScriptModule extends Omit<ContentScriptModule,
plugin: (markdownIt: any, options: any)=> any;
}
type EditorCommandCallback = (...args: any[])=> any;
export interface CodeMirrorControl {
/** Points to a CodeMirror 6 EditorView instance. */
editor: any;
@ -541,8 +543,9 @@ export interface CodeMirrorControl {
/** `extension` should be a [CodeMirror 6 extension](https://codemirror.net/docs/ref/#state.Extension). */
addExtension(extension: any|any[]): void;
execCommand(name: string): any;
supportsCommand(name: string): boolean;
execCommand(name: string, ...args: any[]): any;
registerCommand(name: string, callback: EditorCommandCallback): void;
joplinExtensions: {
/**

View File

@ -4,9 +4,9 @@
// This is necessary. Having multiple copies of the CodeMirror libraries loaded can cause
// the editor to not work properly.
//
import { lineNumbers, highlightActiveLineGutter, } from '@codemirror/view';
import { lineNumbers, highlightActiveLineGutter, EditorView } from '@codemirror/view';
import { completeFromList } from '@codemirror/autocomplete';
import { CodeMirrorContentScriptModule, CodeMirrorControl } from 'api/types';
import { CodeMirrorContentScriptModule, ContentScriptContext } from 'api/types';
//
// For the above import to work, you may also need to add @codemirror/view as a dev dependency
// to package.json. (For the type information only).
@ -15,7 +15,7 @@ import { CodeMirrorContentScriptModule, CodeMirrorControl } from 'api/types';
// const { lineNumbers } = joplin.require('@codemirror/view');
export default (_context: { contentScriptId: string }): CodeMirrorContentScriptModule => {
export default (_context: ContentScriptContext): CodeMirrorContentScriptModule => {
return {
// - codeMirrorWrapper: A thin wrapper around CodeMirror 6, designed to be similar to the
// CodeMirror 5 API. If running in CodeMirror 5, a CodeMirror object is provided instead.
@ -27,12 +27,11 @@ export default (_context: { contentScriptId: string }): CodeMirrorContentScriptM
// We can include multiple extensions here:
highlightActiveLineGutter(),
// See https://codemirror.net/ for more built-in extensions and configuration
// options.
]);
// See https://codemirror.net/ for more built-in extensions and configuration
// options.
// Joplin also exposes extensions for autocompletion.
// CodeMirror's built-in `autocompletion(...)` doesn't work if multiple plugins
// try to use its `override` option.
@ -45,6 +44,28 @@ export default (_context: { contentScriptId: string }): CodeMirrorContentScriptM
// built-in autocompletions. These apply, for example, to HTML tags.
codeMirrorWrapper.joplinExtensions.enableLanguageDataAutocomplete.of(true),
]);
// We can also register editor commands. These commands can be later executed with:
// joplin.commands.execute('editor.execCommand', { name: 'name-here', args: [] })
codeMirrorWrapper.registerCommand('wrap-selection-with-tag', (tagName: string) => {
const editor: EditorView = codeMirrorWrapper.editor;
// See https://codemirror.net/examples/change/
editor.dispatch(editor.state.changeByRange(range => {
const insertBefore = `<${tagName}>`;
const insertAfter = `</${tagName}>`;
return {
changes: [
{from: range.from, insert: insertBefore},
{from: range.to, insert: insertAfter},
],
range: range.extend(
range.from,
range.to + insertBefore.length + insertAfter.length,
),
};
}));
});
},
// There are two main ways to style the CodeMirror editor:

View File

@ -8,5 +8,17 @@ joplin.plugins.register({
'cm6-example',
'./contentScript.js',
);
// Calls an editor command registered in contentScript.ts.
joplin.commands.register({
name: 'underlineSelection',
label: 'Underline selected text',
execute: async () => {
joplin.commands.execute('editor.execCommand', {
name: 'wrap-selection-with-tag',
args: [ 'u' ],
});
},
});
},
});

View File

@ -107,8 +107,8 @@ const useEditorCommands = (props: Props) => {
if ((editorRef.current as any)[value.name]) {
const result = (editorRef.current as any)[value.name](...value.args);
return result;
} else if (editorRef.current.commandExists(value.name)) {
const result = editorRef.current.execCommand(value.name);
} else if (editorRef.current.supportsCommand(value.name)) {
const result = editorRef.current.execCommand(value.name, ...value.args);
return result;
} else {
logger.warn('CodeMirror execCommand: unsupported command: ', value.name);

View File

@ -1,7 +1,7 @@
import { EditorView } from '@codemirror/view';
import { EditorCommandType, EditorControl, EditorSettings, LogMessageCallback, ContentScriptData, SearchState } from '../types';
import CodeMirror5Emulation from './CodeMirror5Emulation/CodeMirror5Emulation';
import editorCommands, { EditorCommandFunction } from './editorCommands/editorCommands';
import editorCommands from './editorCommands/editorCommands';
import { EditorSelection, Extension, StateEffect } from '@codemirror/state';
import { updateLink } from './markdown/markdownCommands';
import { SearchQuery, setSearchQuery } from '@codemirror/search';
@ -17,9 +17,11 @@ interface Callbacks {
onLogMessage: LogMessageCallback;
}
type EditorUserCommand = (...args: any[])=> any;
export default class CodeMirrorControl extends CodeMirror5Emulation implements EditorControl {
private _pluginControl: PluginLoader;
private _userCommands: Map<string, EditorCommandFunction> = new Map();
private _userCommands: Map<string, EditorUserCommand> = new Map();
public constructor(
editor: EditorView,
@ -36,10 +38,10 @@ export default class CodeMirrorControl extends CodeMirror5Emulation implements E
return name in editorCommands || this._userCommands.has(name) || super.commandExists(name);
}
public override execCommand(name: string) {
public override execCommand(name: string, ...args: any[]) {
let commandOutput;
if (this._userCommands.has(name)) {
commandOutput = this._userCommands.get(name)(this.editor);
commandOutput = this._userCommands.get(name)(...args);
} else if (name in editorCommands) {
commandOutput = editorCommands[name as EditorCommandType](this.editor);
} else if (super.commandExists(name)) {
@ -53,7 +55,7 @@ export default class CodeMirrorControl extends CodeMirror5Emulation implements E
return commandOutput;
}
public registerCommand(name: string, command: EditorCommandFunction) {
public registerCommand(name: string, command: EditorUserCommand) {
this._userCommands.set(name, command);
}

View File

@ -533,6 +533,8 @@ export interface MarkdownItContentScriptModule extends Omit<ContentScriptModule,
plugin: (markdownIt: any, options: any)=> any;
}
type EditorCommandCallback = (...args: any[])=> any;
export interface CodeMirrorControl {
/** Points to a CodeMirror 6 EditorView instance. */
editor: any;
@ -541,8 +543,9 @@ export interface CodeMirrorControl {
/** `extension` should be a [CodeMirror 6 extension](https://codemirror.net/docs/ref/#state.Extension). */
addExtension(extension: any|any[]): void;
execCommand(name: string): any;
supportsCommand(name: string): boolean;
execCommand(name: string, ...args: any[]): any;
registerCommand(name: string, callback: EditorCommandCallback): void;
joplinExtensions: {
/**