You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-07-13 00:10:37 +02:00
Desktop: Plugins: Allow commands registered within the beta markdown editor to take arguments (#9963)
This commit is contained in:
@ -533,6 +533,8 @@ export interface MarkdownItContentScriptModule extends Omit<ContentScriptModule,
|
|||||||
plugin: (markdownIt: any, options: any)=> any;
|
plugin: (markdownIt: any, options: any)=> any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EditorCommandCallback = (...args: any[])=> any;
|
||||||
|
|
||||||
export interface CodeMirrorControl {
|
export interface CodeMirrorControl {
|
||||||
/** Points to a CodeMirror 6 EditorView instance. */
|
/** Points to a CodeMirror 6 EditorView instance. */
|
||||||
editor: any;
|
editor: any;
|
||||||
@ -541,8 +543,9 @@ export interface CodeMirrorControl {
|
|||||||
/** `extension` should be a [CodeMirror 6 extension](https://codemirror.net/docs/ref/#state.Extension). */
|
/** `extension` should be a [CodeMirror 6 extension](https://codemirror.net/docs/ref/#state.Extension). */
|
||||||
addExtension(extension: any|any[]): void;
|
addExtension(extension: any|any[]): void;
|
||||||
|
|
||||||
execCommand(name: string): any;
|
|
||||||
supportsCommand(name: string): boolean;
|
supportsCommand(name: string): boolean;
|
||||||
|
execCommand(name: string, ...args: any[]): any;
|
||||||
|
registerCommand(name: string, callback: EditorCommandCallback): void;
|
||||||
|
|
||||||
joplinExtensions: {
|
joplinExtensions: {
|
||||||
/**
|
/**
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
// This is necessary. Having multiple copies of the CodeMirror libraries loaded can cause
|
// This is necessary. Having multiple copies of the CodeMirror libraries loaded can cause
|
||||||
// the editor to not work properly.
|
// 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 { 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
|
// 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).
|
// to package.json. (For the type information only).
|
||||||
@ -15,7 +15,7 @@ import { CodeMirrorContentScriptModule, CodeMirrorControl } from 'api/types';
|
|||||||
// const { lineNumbers } = joplin.require('@codemirror/view');
|
// const { lineNumbers } = joplin.require('@codemirror/view');
|
||||||
|
|
||||||
|
|
||||||
export default (_context: { contentScriptId: string }): CodeMirrorContentScriptModule => {
|
export default (_context: ContentScriptContext): CodeMirrorContentScriptModule => {
|
||||||
return {
|
return {
|
||||||
// - codeMirrorWrapper: A thin wrapper around CodeMirror 6, designed to be similar to the
|
// - 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.
|
// CodeMirror 5 API. If running in CodeMirror 5, a CodeMirror object is provided instead.
|
||||||
@ -27,11 +27,10 @@ export default (_context: { contentScriptId: string }): CodeMirrorContentScriptM
|
|||||||
|
|
||||||
// We can include multiple extensions here:
|
// We can include multiple extensions here:
|
||||||
highlightActiveLineGutter(),
|
highlightActiveLineGutter(),
|
||||||
]);
|
|
||||||
|
|
||||||
// See https://codemirror.net/ for more built-in extensions and configuration
|
// See https://codemirror.net/ for more built-in extensions and configuration
|
||||||
// options.
|
// options.
|
||||||
|
]);
|
||||||
|
|
||||||
// Joplin also exposes extensions for autocompletion.
|
// Joplin also exposes extensions for autocompletion.
|
||||||
// CodeMirror's built-in `autocompletion(...)` doesn't work if multiple plugins
|
// CodeMirror's built-in `autocompletion(...)` doesn't work if multiple plugins
|
||||||
@ -45,6 +44,28 @@ export default (_context: { contentScriptId: string }): CodeMirrorContentScriptM
|
|||||||
// built-in autocompletions. These apply, for example, to HTML tags.
|
// built-in autocompletions. These apply, for example, to HTML tags.
|
||||||
codeMirrorWrapper.joplinExtensions.enableLanguageDataAutocomplete.of(true),
|
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:
|
// There are two main ways to style the CodeMirror editor:
|
||||||
|
@ -8,5 +8,17 @@ joplin.plugins.register({
|
|||||||
'cm6-example',
|
'cm6-example',
|
||||||
'./contentScript.js',
|
'./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' ],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -107,8 +107,8 @@ const useEditorCommands = (props: Props) => {
|
|||||||
if ((editorRef.current as any)[value.name]) {
|
if ((editorRef.current as any)[value.name]) {
|
||||||
const result = (editorRef.current as any)[value.name](...value.args);
|
const result = (editorRef.current as any)[value.name](...value.args);
|
||||||
return result;
|
return result;
|
||||||
} else if (editorRef.current.commandExists(value.name)) {
|
} else if (editorRef.current.supportsCommand(value.name)) {
|
||||||
const result = editorRef.current.execCommand(value.name);
|
const result = editorRef.current.execCommand(value.name, ...value.args);
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
logger.warn('CodeMirror execCommand: unsupported command: ', value.name);
|
logger.warn('CodeMirror execCommand: unsupported command: ', value.name);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { EditorView } from '@codemirror/view';
|
import { EditorView } from '@codemirror/view';
|
||||||
import { EditorCommandType, EditorControl, EditorSettings, LogMessageCallback, ContentScriptData, SearchState } from '../types';
|
import { EditorCommandType, EditorControl, EditorSettings, LogMessageCallback, ContentScriptData, SearchState } from '../types';
|
||||||
import CodeMirror5Emulation from './CodeMirror5Emulation/CodeMirror5Emulation';
|
import CodeMirror5Emulation from './CodeMirror5Emulation/CodeMirror5Emulation';
|
||||||
import editorCommands, { EditorCommandFunction } from './editorCommands/editorCommands';
|
import editorCommands from './editorCommands/editorCommands';
|
||||||
import { EditorSelection, Extension, StateEffect } from '@codemirror/state';
|
import { EditorSelection, Extension, StateEffect } from '@codemirror/state';
|
||||||
import { updateLink } from './markdown/markdownCommands';
|
import { updateLink } from './markdown/markdownCommands';
|
||||||
import { SearchQuery, setSearchQuery } from '@codemirror/search';
|
import { SearchQuery, setSearchQuery } from '@codemirror/search';
|
||||||
@ -17,9 +17,11 @@ interface Callbacks {
|
|||||||
onLogMessage: LogMessageCallback;
|
onLogMessage: LogMessageCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EditorUserCommand = (...args: any[])=> any;
|
||||||
|
|
||||||
export default class CodeMirrorControl extends CodeMirror5Emulation implements EditorControl {
|
export default class CodeMirrorControl extends CodeMirror5Emulation implements EditorControl {
|
||||||
private _pluginControl: PluginLoader;
|
private _pluginControl: PluginLoader;
|
||||||
private _userCommands: Map<string, EditorCommandFunction> = new Map();
|
private _userCommands: Map<string, EditorUserCommand> = new Map();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
editor: EditorView,
|
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);
|
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;
|
let commandOutput;
|
||||||
if (this._userCommands.has(name)) {
|
if (this._userCommands.has(name)) {
|
||||||
commandOutput = this._userCommands.get(name)(this.editor);
|
commandOutput = this._userCommands.get(name)(...args);
|
||||||
} else if (name in editorCommands) {
|
} else if (name in editorCommands) {
|
||||||
commandOutput = editorCommands[name as EditorCommandType](this.editor);
|
commandOutput = editorCommands[name as EditorCommandType](this.editor);
|
||||||
} else if (super.commandExists(name)) {
|
} else if (super.commandExists(name)) {
|
||||||
@ -53,7 +55,7 @@ export default class CodeMirrorControl extends CodeMirror5Emulation implements E
|
|||||||
return commandOutput;
|
return commandOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
public registerCommand(name: string, command: EditorCommandFunction) {
|
public registerCommand(name: string, command: EditorUserCommand) {
|
||||||
this._userCommands.set(name, command);
|
this._userCommands.set(name, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,6 +533,8 @@ export interface MarkdownItContentScriptModule extends Omit<ContentScriptModule,
|
|||||||
plugin: (markdownIt: any, options: any)=> any;
|
plugin: (markdownIt: any, options: any)=> any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EditorCommandCallback = (...args: any[])=> any;
|
||||||
|
|
||||||
export interface CodeMirrorControl {
|
export interface CodeMirrorControl {
|
||||||
/** Points to a CodeMirror 6 EditorView instance. */
|
/** Points to a CodeMirror 6 EditorView instance. */
|
||||||
editor: any;
|
editor: any;
|
||||||
@ -541,8 +543,9 @@ export interface CodeMirrorControl {
|
|||||||
/** `extension` should be a [CodeMirror 6 extension](https://codemirror.net/docs/ref/#state.Extension). */
|
/** `extension` should be a [CodeMirror 6 extension](https://codemirror.net/docs/ref/#state.Extension). */
|
||||||
addExtension(extension: any|any[]): void;
|
addExtension(extension: any|any[]): void;
|
||||||
|
|
||||||
execCommand(name: string): any;
|
|
||||||
supportsCommand(name: string): boolean;
|
supportsCommand(name: string): boolean;
|
||||||
|
execCommand(name: string, ...args: any[]): any;
|
||||||
|
registerCommand(name: string, callback: EditorCommandCallback): void;
|
||||||
|
|
||||||
joplinExtensions: {
|
joplinExtensions: {
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user