mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-21 09:38:01 +02:00
Desktop: Resolves #9964: Beta editor plugins: Allow fixing conflicts between plugins that add autocompletions (#9965)
This commit is contained in:
parent
4a61ff2df3
commit
f1a833ef21
@ -605,10 +605,14 @@ packages/editor/CodeMirror/markdown/markdownReformatter.test.js
|
||||
packages/editor/CodeMirror/markdown/markdownReformatter.js
|
||||
packages/editor/CodeMirror/pluginApi/PluginLoader.js
|
||||
packages/editor/CodeMirror/pluginApi/codeMirrorRequire.js
|
||||
packages/editor/CodeMirror/pluginApi/customEditorCompletion.test.js
|
||||
packages/editor/CodeMirror/pluginApi/customEditorCompletion.js
|
||||
packages/editor/CodeMirror/testUtil/createEditorControl.js
|
||||
packages/editor/CodeMirror/testUtil/createEditorSettings.js
|
||||
packages/editor/CodeMirror/testUtil/createTestEditor.js
|
||||
packages/editor/CodeMirror/testUtil/forceFullParse.js
|
||||
packages/editor/CodeMirror/testUtil/loadLanguages.js
|
||||
packages/editor/CodeMirror/testUtil/typeText.js
|
||||
packages/editor/CodeMirror/theme.js
|
||||
packages/editor/CodeMirror/util/isInSyntaxNode.js
|
||||
packages/editor/CodeMirror/util/setupVim.js
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -585,10 +585,14 @@ packages/editor/CodeMirror/markdown/markdownReformatter.test.js
|
||||
packages/editor/CodeMirror/markdown/markdownReformatter.js
|
||||
packages/editor/CodeMirror/pluginApi/PluginLoader.js
|
||||
packages/editor/CodeMirror/pluginApi/codeMirrorRequire.js
|
||||
packages/editor/CodeMirror/pluginApi/customEditorCompletion.test.js
|
||||
packages/editor/CodeMirror/pluginApi/customEditorCompletion.js
|
||||
packages/editor/CodeMirror/testUtil/createEditorControl.js
|
||||
packages/editor/CodeMirror/testUtil/createEditorSettings.js
|
||||
packages/editor/CodeMirror/testUtil/createTestEditor.js
|
||||
packages/editor/CodeMirror/testUtil/forceFullParse.js
|
||||
packages/editor/CodeMirror/testUtil/loadLanguages.js
|
||||
packages/editor/CodeMirror/testUtil/typeText.js
|
||||
packages/editor/CodeMirror/theme.js
|
||||
packages/editor/CodeMirror/util/isInSyntaxNode.js
|
||||
packages/editor/CodeMirror/util/setupVim.js
|
||||
|
@ -533,6 +533,40 @@ export interface MarkdownItContentScriptModule extends Omit<ContentScriptModule,
|
||||
plugin: (markdownIt: any, options: any)=> any;
|
||||
}
|
||||
|
||||
export interface CodeMirrorControl {
|
||||
/** Points to a CodeMirror 6 EditorView instance. */
|
||||
editor: any;
|
||||
cm6: any;
|
||||
|
||||
/** `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;
|
||||
|
||||
joplinExtensions: {
|
||||
/**
|
||||
* Returns a [CodeMirror 6 extension](https://codemirror.net/docs/ref/#state.Extension) that
|
||||
* registers the given [CompletionSource](https://codemirror.net/docs/ref/#autocomplete.CompletionSource).
|
||||
*
|
||||
* Use this extension rather than the built-in CodeMirror [`autocompletion`](https://codemirror.net/docs/ref/#autocomplete.autocompletion)
|
||||
* if you don't want to use [langaugeData-based autocompletion](https://codemirror.net/docs/ref/#autocomplete.autocompletion^config.override).
|
||||
*
|
||||
* Using `autocompletion({ override: [ ... ]})` causes errors when done by multiple plugins.
|
||||
*/
|
||||
completionSource(completionSource: any): any;
|
||||
|
||||
/**
|
||||
* Creates an extension that enables or disables [`languageData`-based autocompletion](https://codemirror.net/docs/ref/#autocomplete.autocompletion^config.override).
|
||||
*/
|
||||
enableLanguageDataAutocomplete: { of: (enabled: boolean)=> any };
|
||||
};
|
||||
}
|
||||
|
||||
export interface CodeMirrorContentScriptModule extends Omit<ContentScriptModule, 'plugin'> {
|
||||
plugin: (codeMirrorControl: CodeMirrorControl)=> void;
|
||||
}
|
||||
|
||||
export enum ContentScriptType {
|
||||
/**
|
||||
* Registers a new Markdown-It plugin, which should follow the template
|
||||
|
@ -9,6 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@codemirror/autocomplete": "6.12.0",
|
||||
"@codemirror/view": "6.22.2",
|
||||
"@joplin/lib": "~2.9",
|
||||
"@types/node": "^18.7.13",
|
||||
@ -915,10 +916,53 @@
|
||||
"integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@codemirror/autocomplete": {
|
||||
"version": "6.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.12.0.tgz",
|
||||
"integrity": "sha512-r4IjdYFthwbCQyvqnSlx0WBHRHi8nBvU+WjJxFUij81qsBfhNudf/XKKmmC2j3m0LaOYUQTf3qiEK1J8lO1sdg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.17.0",
|
||||
"@lezer/common": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language": {
|
||||
"version": "6.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz",
|
||||
"integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.23.0",
|
||||
"@lezer/common": "^1.1.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.0.0",
|
||||
"style-mod": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language/node_modules/@codemirror/view": {
|
||||
"version": "6.24.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.24.1.tgz",
|
||||
"integrity": "sha512-sBfP4rniPBRQzNakwuQEqjEuiJDWJyF2kqLLqij4WXRoVwPPJfjx966Eq3F7+OPQxDtMt/Q9MWLoZLWjeveBlg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.4.0",
|
||||
"style-mod": "^4.1.0",
|
||||
"w3c-keyname": "^2.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/state": {
|
||||
"version": "6.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.3.3.tgz",
|
||||
"integrity": "sha512-0wufKcTw2dEwEaADajjHf6hBy1sh3M6V0e+q4JKIhLuiMSe5td5HOWpUdvKth1fT1M9VYOboajoBHpkCd7PG7A==",
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz",
|
||||
"integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@codemirror/view": {
|
||||
@ -1517,6 +1561,30 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/common": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz",
|
||||
"integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@lezer/highlight": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz",
|
||||
"integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/lr": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz",
|
||||
"integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
|
||||
|
@ -26,6 +26,7 @@
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"@joplin/lib": "~2.9",
|
||||
"@codemirror/view": "6.22.2"
|
||||
"@codemirror/view": "6.22.2",
|
||||
"@codemirror/autocomplete": "6.12.0"
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
// the editor to not work properly.
|
||||
//
|
||||
import { lineNumbers, highlightActiveLineGutter, } from '@codemirror/view';
|
||||
import { completeFromList } from '@codemirror/autocomplete';
|
||||
import { CodeMirrorContentScriptModule, CodeMirrorControl } 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).
|
||||
@ -13,7 +15,7 @@ import { lineNumbers, highlightActiveLineGutter, } from '@codemirror/view';
|
||||
// const { lineNumbers } = joplin.require('@codemirror/view');
|
||||
|
||||
|
||||
export default (_context: { contentScriptId: string }) => {
|
||||
export default (_context: { contentScriptId: string }): 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.
|
||||
@ -29,6 +31,20 @@ export default (_context: { contentScriptId: string }) => {
|
||||
|
||||
// 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.
|
||||
codeMirrorWrapper.addExtension([
|
||||
codeMirrorWrapper.joplinExtensions.completionSource(
|
||||
completeFromList(['# Example completion'])
|
||||
),
|
||||
|
||||
// Joplin also exposes a Facet that allows enabling or disabling CodeMirror's
|
||||
// built-in autocompletions. These apply, for example, to HTML tags.
|
||||
codeMirrorWrapper.joplinExtensions.enableLanguageDataAutocomplete.of(true),
|
||||
]);
|
||||
},
|
||||
|
||||
// There are two main ways to style the CodeMirror editor:
|
||||
|
@ -1,18 +1,5 @@
|
||||
import { ViewPlugin } from '@codemirror/view';
|
||||
import createEditor from './createEditor';
|
||||
import createEditorSettings from './testUtil/createEditorSettings';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
|
||||
const createEditorControl = (initialText: string) => {
|
||||
const editorSettings = createEditorSettings(Setting.THEME_LIGHT);
|
||||
|
||||
return createEditor(document.body, {
|
||||
initialText,
|
||||
settings: editorSettings,
|
||||
onEvent: _event => {},
|
||||
onLogMessage: _message => {},
|
||||
});
|
||||
};
|
||||
import createEditorControl from './testUtil/createEditorControl';
|
||||
|
||||
describe('CodeMirrorControl', () => {
|
||||
it('clearHistory should clear the undo/redo history', () => {
|
||||
|
@ -6,6 +6,8 @@ import { EditorSelection, Extension, StateEffect } from '@codemirror/state';
|
||||
import { updateLink } from './markdown/markdownCommands';
|
||||
import { SearchQuery, setSearchQuery } from '@codemirror/search';
|
||||
import PluginLoader from './pluginApi/PluginLoader';
|
||||
import customEditorCompletion, { editorCompletionSource, enableLanguageDataAutocomplete } from './pluginApi/customEditorCompletion';
|
||||
import { CompletionSource } from '@codemirror/autocomplete';
|
||||
|
||||
interface Callbacks {
|
||||
onUndoRedo(): void;
|
||||
@ -26,6 +28,8 @@ export default class CodeMirrorControl extends CodeMirror5Emulation implements E
|
||||
super(editor, _callbacks.onLogMessage);
|
||||
|
||||
this._pluginControl = new PluginLoader(this, _callbacks.onLogMessage);
|
||||
|
||||
this.addExtension(customEditorCompletion());
|
||||
}
|
||||
|
||||
public supportsCommand(name: string) {
|
||||
@ -149,6 +153,16 @@ export default class CodeMirrorControl extends CodeMirror5Emulation implements E
|
||||
// CodeMirror-specific methods
|
||||
//
|
||||
|
||||
public joplinExtensions = {
|
||||
// Some plugins want to enable autocompletion from *just* that plugin, without also
|
||||
// enabling autocompletion for text within code blocks (and other built-in completion
|
||||
// sources).
|
||||
// To support this, we need to provide extensions that wrap the built-in autocomplete.
|
||||
// See https://discuss.codemirror.net/t/autocompletion-merging-override-in-config/7853
|
||||
completionSource: (completionSource: CompletionSource) => editorCompletionSource.of(completionSource),
|
||||
enableLanguageDataAutocomplete: enableLanguageDataAutocomplete,
|
||||
};
|
||||
|
||||
public addExtension(extension: Extension) {
|
||||
this.editor.dispatch({
|
||||
effects: StateEffect.appendConfig.of([extension]),
|
||||
|
@ -0,0 +1,87 @@
|
||||
import { EditorState, StateEffect } from '@codemirror/state';
|
||||
import createEditorControl from '../testUtil/createEditorControl';
|
||||
import { EditorView } from '@codemirror/view';
|
||||
import { completeFromList, completionStatus, currentCompletions } from '@codemirror/autocomplete';
|
||||
import typeText from '../testUtil/typeText';
|
||||
|
||||
const waitForShownCompletionsToContain = (editor: EditorView, completionLabels: string[]) => {
|
||||
return new Promise<string[]>(resolve => {
|
||||
let resolved = false;
|
||||
const checkCompletions = (state: EditorState) => {
|
||||
if (resolved) {
|
||||
return;
|
||||
}
|
||||
|
||||
const completions = currentCompletions(state).map(completion => completion.label);
|
||||
|
||||
for (const result of completionLabels) {
|
||||
if (!completions.includes(result)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
resolve(completions);
|
||||
resolved = true;
|
||||
};
|
||||
checkCompletions(editor.state);
|
||||
|
||||
editor.dispatch({
|
||||
effects: StateEffect.appendConfig.of(EditorState.transactionExtender.of(transaction => {
|
||||
checkCompletions(transaction.state);
|
||||
return null;
|
||||
})),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
describe('customEditorCompletion', () => {
|
||||
test('should not show completions when no completion sources have been registered', async () => {
|
||||
const editorControl = createEditorControl('');
|
||||
|
||||
const completion = completeFromList(['test']);
|
||||
editorControl.addExtension([
|
||||
EditorState.languageData.of(() => [{ autocomplete: completion }]),
|
||||
]);
|
||||
|
||||
const editor = editorControl.editor;
|
||||
typeText(editor, 'tes');
|
||||
|
||||
// Would be 'pending' or 'active' if trying to autocomplete.
|
||||
expect(completionStatus(editor.state)).toBe(null);
|
||||
});
|
||||
|
||||
test('should show langaugeData completions when languageData-based autocomplete is enabled', async () => {
|
||||
const editorControl = createEditorControl('');
|
||||
|
||||
const completion = completeFromList(['function']);
|
||||
editorControl.addExtension([
|
||||
EditorState.languageData.of(() => [{ autocomplete: completion }]),
|
||||
editorControl.joplinExtensions.enableLanguageDataAutocomplete.of(true),
|
||||
]);
|
||||
|
||||
const editor = editorControl.editor;
|
||||
typeText(editor, 'fun');
|
||||
await waitForShownCompletionsToContain(editor, ['function']);
|
||||
});
|
||||
|
||||
test.each([
|
||||
{ useLanguageData: false },
|
||||
{ useLanguageData: true },
|
||||
])('should show completions from all registered sources (%j)', async ({ useLanguageData }) => {
|
||||
const editorControl = createEditorControl('');
|
||||
const completion1 = completeFromList(['completion1-test', 'completion1-test2']);
|
||||
const completion2 = completeFromList(['completion2-test', 'completion2-test2']);
|
||||
|
||||
const joplinExtensions = editorControl.joplinExtensions;
|
||||
editorControl.addExtension([
|
||||
joplinExtensions.completionSource(completion1),
|
||||
joplinExtensions.completionSource(completion2),
|
||||
joplinExtensions.enableLanguageDataAutocomplete.of(useLanguageData),
|
||||
]);
|
||||
|
||||
const editor = editorControl.editor;
|
||||
typeText(editor, 'com');
|
||||
|
||||
await waitForShownCompletionsToContain(editor, ['completion1-test', 'completion2-test']);
|
||||
});
|
||||
});
|
@ -0,0 +1,63 @@
|
||||
import { EditorState, Facet, Compartment, Extension } from '@codemirror/state';
|
||||
import { CompletionSource, autocompletion } from '@codemirror/autocomplete';
|
||||
|
||||
// CodeMirror 6's built-in autocomplete functionality is difficult to work with
|
||||
// unless you want to enable languageData-based autocompletion.
|
||||
//
|
||||
// This file wraps CodeMirror's built-in `autocompletion` extension and allows plugins
|
||||
// to provide completions that work regardless of whether languageData-based autocomplete
|
||||
// is enabled.
|
||||
//
|
||||
// See https://discuss.codemirror.net/t/autocompletion-merging-override-in-config/7853
|
||||
|
||||
export const editorCompletionSource = Facet.define<CompletionSource, CompletionSource[]>();
|
||||
export const enableLanguageDataAutocomplete = Facet.define<boolean, boolean[]>();
|
||||
|
||||
// Provides languageData OR override autocompletions based on the value of
|
||||
// the enableLanguageDataAutocomplete facet.
|
||||
const customEditorCompletion = () => {
|
||||
const compartment = new Compartment();
|
||||
|
||||
return [
|
||||
compartment.of([]),
|
||||
EditorState.transactionExtender.of(transaction => {
|
||||
const lastCompletions = transaction.startState.facet(editorCompletionSource) ?? [];
|
||||
const completions = transaction.state.facet(editorCompletionSource) ?? [];
|
||||
|
||||
|
||||
const currentExtension = compartment.get(transaction.state) as Extension[];
|
||||
const missingExtension = currentExtension.length === 0 && completions.length > 0;
|
||||
|
||||
const lastEnableLangDataComplete = transaction.startState.facet(enableLanguageDataAutocomplete)[0];
|
||||
const enableLangDataComplete = transaction.state.facet(enableLanguageDataAutocomplete)[0];
|
||||
|
||||
const completionsChanged = lastCompletions.length !== completions.length || completions.some((val, idx) => lastCompletions[idx] !== val);
|
||||
const useLangDataChanged = lastEnableLangDataComplete !== enableLangDataComplete;
|
||||
|
||||
// Update the autocompletion extension based on the editorCompletionSource
|
||||
// facet.
|
||||
if (missingExtension || completionsChanged || useLangDataChanged) {
|
||||
if (completions.length > 0 || enableLangDataComplete) {
|
||||
return {
|
||||
effects: compartment.reconfigure([
|
||||
completions.map(completion =>
|
||||
EditorState.languageData.of(() => [{ autocomplete: completion }]),
|
||||
),
|
||||
autocompletion(enableLangDataComplete ? {} : {
|
||||
override: [...completions],
|
||||
}),
|
||||
]),
|
||||
};
|
||||
} else if (!missingExtension) {
|
||||
return {
|
||||
effects: compartment.reconfigure([]),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}),
|
||||
];
|
||||
};
|
||||
|
||||
export default customEditorCompletion;
|
16
packages/editor/CodeMirror/testUtil/createEditorControl.ts
Normal file
16
packages/editor/CodeMirror/testUtil/createEditorControl.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import createEditor from '../createEditor';
|
||||
import createEditorSettings from './createEditorSettings';
|
||||
|
||||
const createEditorControl = (initialText: string) => {
|
||||
const editorSettings = createEditorSettings(Setting.THEME_LIGHT);
|
||||
|
||||
return createEditor(document.body, {
|
||||
initialText,
|
||||
settings: editorSettings,
|
||||
onEvent: _event => {},
|
||||
onLogMessage: _message => {},
|
||||
});
|
||||
};
|
||||
|
||||
export default createEditorControl;
|
16
packages/editor/CodeMirror/testUtil/typeText.ts
Normal file
16
packages/editor/CodeMirror/testUtil/typeText.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { EditorView } from '@codemirror/view';
|
||||
|
||||
const typeText = (editor: EditorView, text: string) => {
|
||||
// How CodeMirror does this in their tests:
|
||||
// https://github.com/codemirror/autocomplete/blob/fb1c899464df4d36528331412cdd316548134cb2/test/webtest-autocomplete.ts#L116
|
||||
// The important part is the userEvent: input.type.
|
||||
|
||||
const selection = editor.state.selection;
|
||||
editor.dispatch({
|
||||
changes: [{ from: selection.main.head, insert: text }],
|
||||
selection: { anchor: selection.main.head + text.length },
|
||||
userEvent: 'input.type',
|
||||
});
|
||||
};
|
||||
|
||||
export default typeText;
|
@ -533,6 +533,40 @@ export interface MarkdownItContentScriptModule extends Omit<ContentScriptModule,
|
||||
plugin: (markdownIt: any, options: any)=> any;
|
||||
}
|
||||
|
||||
export interface CodeMirrorControl {
|
||||
/** Points to a CodeMirror 6 EditorView instance. */
|
||||
editor: any;
|
||||
cm6: any;
|
||||
|
||||
/** `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;
|
||||
|
||||
joplinExtensions: {
|
||||
/**
|
||||
* Returns a [CodeMirror 6 extension](https://codemirror.net/docs/ref/#state.Extension) that
|
||||
* registers the given [CompletionSource](https://codemirror.net/docs/ref/#autocomplete.CompletionSource).
|
||||
*
|
||||
* Use this extension rather than the built-in CodeMirror [`autocompletion`](https://codemirror.net/docs/ref/#autocomplete.autocompletion)
|
||||
* if you don't want to use [langaugeData-based autocompletion](https://codemirror.net/docs/ref/#autocomplete.autocompletion^config.override).
|
||||
*
|
||||
* Using `autocompletion({ override: [ ... ]})` causes errors when done by multiple plugins.
|
||||
*/
|
||||
completionSource(completionSource: any): any;
|
||||
|
||||
/**
|
||||
* Creates an extension that enables or disables [`languageData`-based autocompletion](https://codemirror.net/docs/ref/#autocomplete.autocompletion^config.override).
|
||||
*/
|
||||
enableLanguageDataAutocomplete: { of: (enabled: boolean)=> any };
|
||||
};
|
||||
}
|
||||
|
||||
export interface CodeMirrorContentScriptModule extends Omit<ContentScriptModule, 'plugin'> {
|
||||
plugin: (codeMirrorControl: CodeMirrorControl)=> void;
|
||||
}
|
||||
|
||||
export enum ContentScriptType {
|
||||
/**
|
||||
* Registers a new Markdown-It plugin, which should follow the template
|
||||
|
Loading…
Reference in New Issue
Block a user