You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-06 09:19:22 +02:00
Plugins: Add support for external CodeMirror plugins (#4015)
This commit is contained in:
@@ -637,6 +637,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
readOnly={props.visiblePanes.indexOf('editor') < 0}
|
||||
autoMatchBraces={Setting.value('editor.autoMatchingBraces')}
|
||||
keyMap={props.keyboardMode}
|
||||
plugins={props.plugins}
|
||||
onChange={codeMirror_change}
|
||||
onScroll={editor_scroll}
|
||||
onEditorContextMenu={onEditorContextMenu}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { useEffect, useImperativeHandle, useState, useRef, useCallback, forwardRef } from 'react';
|
||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
|
||||
import * as CodeMirror from 'codemirror';
|
||||
|
||||
@@ -18,6 +19,7 @@ import useLineSorting from './utils/useLineSorting';
|
||||
import useEditorSearch from './utils/useEditorSearch';
|
||||
import useJoplinMode from './utils/useJoplinMode';
|
||||
import useKeymap from './utils/useKeymap';
|
||||
import useExternalPlugins from './utils/useExternalPlugins';
|
||||
|
||||
import 'codemirror/keymap/emacs';
|
||||
import 'codemirror/keymap/vim';
|
||||
@@ -75,18 +77,19 @@ for (let i = 0; i < topLanguages.length; i++) {
|
||||
}
|
||||
|
||||
export interface EditorProps {
|
||||
value: string;
|
||||
searchMarkers: any;
|
||||
mode: string;
|
||||
style: any;
|
||||
codeMirrorTheme: any;
|
||||
readOnly: boolean;
|
||||
autoMatchBraces: boolean;
|
||||
keyMap: string;
|
||||
onChange: any;
|
||||
onScroll: any;
|
||||
onEditorContextMenu: any;
|
||||
onEditorPaste: any;
|
||||
value: string,
|
||||
searchMarkers: any,
|
||||
mode: string,
|
||||
style: any,
|
||||
codeMirrorTheme: any,
|
||||
readOnly: boolean,
|
||||
autoMatchBraces: boolean,
|
||||
keyMap: string,
|
||||
plugins: PluginStates,
|
||||
onChange: any,
|
||||
onScroll: any,
|
||||
onEditorContextMenu: any,
|
||||
onEditorPaste: any,
|
||||
}
|
||||
|
||||
function Editor(props: EditorProps, ref: any) {
|
||||
@@ -102,6 +105,7 @@ function Editor(props: EditorProps, ref: any) {
|
||||
useEditorSearch(CodeMirror);
|
||||
useJoplinMode(CodeMirror);
|
||||
useKeymap(CodeMirror);
|
||||
const pluginOptions: any = useExternalPlugins(CodeMirror, props.plugins);
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
return editor;
|
||||
@@ -238,6 +242,14 @@ function Editor(props: EditorProps, ref: any) {
|
||||
}
|
||||
}, [props.keyMap]);
|
||||
|
||||
useEffect(() => {
|
||||
if (editor) {
|
||||
for (const option in pluginOptions) {
|
||||
editor.setOption(option, pluginOptions[option]);
|
||||
}
|
||||
}
|
||||
}, [pluginOptions, editor]);
|
||||
|
||||
return <div style={props.style} ref={editorParent} />;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
import { contentScriptsToCodeMirrorPlugin } from '@joplin/lib/services/plugins/utils/loadContentScripts';
|
||||
import { extname } from 'path';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import uuid from '@joplin/lib/uuid';
|
||||
|
||||
const { reg } = require('@joplin/lib/registry.js');
|
||||
|
||||
export default function useExternalPlugins(CodeMirror: any, plugins: PluginStates) {
|
||||
|
||||
const [options, setOptions] = useState({});
|
||||
useEffect(() => {
|
||||
let newOptions = {};
|
||||
|
||||
const contentScripts = contentScriptsToCodeMirrorPlugin(plugins);
|
||||
|
||||
for (const contentScript of contentScripts) {
|
||||
try {
|
||||
const mod = contentScript.module;
|
||||
|
||||
if (mod.codeMirrorResources) {
|
||||
for (const asset of mod.codeMirrorResources) {
|
||||
try {
|
||||
require(`codemirror/${asset}`);
|
||||
} catch (error) {
|
||||
error.message = `${asset} is not a valid CodeMirror asset, keymap or mode. You can find a list of valid assets here: https://codemirror.net/doc/manual.html#addons`;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mod.codeMirrorOptions) {
|
||||
newOptions = Object.assign({}, newOptions, mod.codeMirrorOptions);
|
||||
}
|
||||
|
||||
if (mod.assets) {
|
||||
const cssStrings = [];
|
||||
|
||||
for (const asset of mod.assets()) {
|
||||
let mime = asset.mime;
|
||||
|
||||
if (!mime && asset.inline) throw new Error('Mime type is required for inline assets');
|
||||
|
||||
if (!mime && asset.name) {
|
||||
const ext = extname(asset.name).toLowerCase();
|
||||
if (ext === '.css') mime = 'text/css';
|
||||
}
|
||||
|
||||
if (mime !== 'text/css') throw new Error('Only css assets are supported for CodeMirror plugins');
|
||||
|
||||
if (asset.inline) {
|
||||
cssStrings.push(asset.text);
|
||||
} else {
|
||||
addScript(shim.fsDriver().resolveRelativePathWithinDir(contentScript.assetPath, asset.name), contentScript.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (cssStrings.length > 0) {
|
||||
addInlineCss(cssStrings, contentScript.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (mod.plugin) {
|
||||
mod.plugin(CodeMirror);
|
||||
}
|
||||
} catch (error) {
|
||||
reg.logger().error(error.toString());
|
||||
}
|
||||
}
|
||||
setOptions(newOptions);
|
||||
}, [plugins]);
|
||||
|
||||
function addInlineCss(cssStrings: string[], id: string) {
|
||||
const element = document.createElement('style');
|
||||
element.setAttribute('id', `content-script-${id}-inline-${uuid.createNano()}`);
|
||||
document.head.appendChild(element);
|
||||
element.appendChild(document.createTextNode(cssStrings.join('\n')));
|
||||
}
|
||||
|
||||
function addScript(path: string, id: string) {
|
||||
const element = document.createElement('link');
|
||||
element.setAttribute('id', `content-script-${id}-link-${uuid.createNano()}`);
|
||||
element.setAttribute('rel', 'stylesheet');
|
||||
element.setAttribute('href', path);
|
||||
document.head.appendChild(element);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
import contentScriptsToRendererRules from '@joplin/lib/services/plugins/utils/contentScriptsToRendererRules';
|
||||
import { contentScriptsToRendererRules } from '@joplin/lib/services/plugins/utils/loadContentScripts';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { ResourceInfos } from './types';
|
||||
import markupLanguageUtils from '@joplin/lib/markupLanguageUtils';
|
||||
|
||||
Reference in New Issue
Block a user