/* eslint-disable multiline-comment-style */

// =================================================================
// Command API types
// =================================================================

export interface Command {
	/**
	 * Name of command - must be globally unique
	 */
	name: string;

	/**
	 * Label to be displayed on menu items or keyboard shortcut editor for example.
	 * If it is missing, it's assumed it's a private command, to be called programmatically only.
	 * In that case the command will not appear in the shortcut editor or command panel, and logically
	 * should not be used as a menu item.
	 */
	label?: string;

	/**
	 * Icon to be used on toolbar buttons for example
	 */
	iconName?: string;

	/**
	 * Code to be ran when the command is executed. It may return a result.
	 */
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	execute(...args: any[]): Promise<any | void>;

	/**
	 * Defines whether the command should be enabled or disabled, which in turns
	 * affects the enabled state of any associated button or menu item.
	 *
	 * The condition should be expressed as a "when-clause" (as in Visual Studio
	 * Code). It's a simple boolean expression that evaluates to `true` or
	 * `false`. It supports the following operators:
	 *
	 * Operator | Symbol | Example
	 * -- | -- | --
	 * Equality | == | "editorType == markdown"
	 * Inequality | != | "currentScreen != config"
	 * Or | \|\| | "noteIsTodo \|\| noteTodoCompleted"
	 * And | && | "oneNoteSelected && !inConflictFolder"
	 *
	 * Joplin, unlike VSCode, also supports parentheses, which allows creating
	 * more complex expressions such as `cond1 || (cond2 && cond3)`. Only one
	 * level of parentheses is possible (nested ones aren't supported).
	 *
	 * Currently the supported context variables aren't documented, but you can
	 * find the list below:
	 *
	 * - [Global When Clauses](https://github.com/laurent22/joplin/blob/dev/packages/lib/services/commands/stateToWhenClauseContext.ts)
	 * - [Desktop app When Clauses](https://github.com/laurent22/joplin/blob/dev/packages/app-desktop/services/commands/stateToWhenClauseContext.ts)
	 *
	 * Note: Commands are enabled by default unless you use this property.
	 */
	enabledCondition?: string;
}

// =================================================================
// Interop API types
// =================================================================

export enum FileSystemItem {
	File = 'file',
	Directory = 'directory',
}

export enum ImportModuleOutputFormat {
	Markdown = 'md',
	Html = 'html',
}

/**
 * Used to implement a module to export data from Joplin. [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/json_export) for an example.
 *
 * In general, all the event handlers you'll need to implement take a `context` object as a first argument. This object will contain the export or import path as well as various optional properties, such as which notes or notebooks need to be exported.
 *
 * To get a better sense of what it will contain it can be useful to print it using `console.info(context)`.
 */
export interface ExportModule {
	/**
	 * The format to be exported, eg "enex", "jex", "json", etc.
	 */
	format: string;

	/**
	 * The description that will appear in the UI, for example in the menu item.
	 */
	description: string;

	/**
	 * Whether the module will export a single file or multiple files in a directory. It affects the open dialog that will be presented to the user when using your exporter.
	 */
	target: FileSystemItem;

	/**
	 * Only applies to single file exporters or importers
	 * It tells whether the format can package multiple notes into one file.
	 * For example JEX or ENEX can, but HTML cannot.
	 */
	isNoteArchive: boolean;

	/**
	 * The extensions of the files exported by your module. For example, it is `["htm", "html"]` for the HTML module, and just `["jex"]` for the JEX module.
	 */
	fileExtensions?: string[];

	/**
	 * Called when the export process starts.
	 */
	onInit(context: ExportContext): Promise<void>;

	/**
	 * Called when an item needs to be processed. An "item" can be any Joplin object, such as a note, a folder, a notebook, etc.
	 */
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	onProcessItem(context: ExportContext, itemType: number, item: any): Promise<void>;

	/**
	 * Called when a resource file needs to be exported.
	 */
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	onProcessResource(context: ExportContext, resource: any, filePath: string): Promise<void>;

	/**
	 * Called when the export process is done.
	 */
	onClose(context: ExportContext): Promise<void>;
}

export interface ImportModule {
	/**
	 * The format to be exported, eg "enex", "jex", "json", etc.
	 */
	format: string;

	/**
	 * The description that will appear in the UI, for example in the menu item.
	 */
	description: string;

	/**
	 * Only applies to single file exporters or importers
	 * It tells whether the format can package multiple notes into one file.
	 * For example JEX or ENEX can, but HTML cannot.
	 */
	isNoteArchive: boolean;

	/**
	 * The type of sources that are supported by the module. Tells whether the module can import files or directories or both.
	 */
	sources: FileSystemItem[];

	/**
	 * Tells the file extensions of the exported files.
	 */
	fileExtensions?: string[];

	/**
	 * Tells the type of notes that will be generated, either HTML or Markdown (default).
	 */
	outputFormat?: ImportModuleOutputFormat;

	/**
	 * Called when the import process starts. There is only one event handler within which you should import the complete data.
	 */
	onExec(context: ImportContext): Promise<void>;
}

export interface ExportOptions {
	format?: string;
	path?: string;
	sourceFolderIds?: string[];
	sourceNoteIds?: string[];
	// modulePath?: string;
	target?: FileSystemItem;
}

export interface ExportContext {
	destPath: string;
	options: ExportOptions;

	/**
	 * You can attach your own custom data using this property - it will then be passed to each event handler, allowing you to keep state from one event to the next.
	 */
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	userData?: any;
}

export interface ImportContext {
	sourcePath: string;
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	options: any;
	warnings: string[];
}

// =================================================================
// Misc types
// =================================================================

export interface Script {
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	onStart?(event: any): Promise<void>;
}

export interface Disposable {
	// dispose():void;
}

export enum ModelType {
	Note = 1,
	Folder = 2,
	Setting = 3,
	Resource = 4,
	Tag = 5,
	NoteTag = 6,
	Search = 7,
	Alarm = 8,
	MasterKey = 9,
	ItemChange = 10,
	NoteResource = 11,
	ResourceLocalState = 12,
	Revision = 13,
	Migration = 14,
	SmartFilter = 15,
	Command = 16,
}

export interface VersionInfo {
	version: string;
	profileVersion: number;
	syncVersion: number;

	platform: 'desktop'|'mobile';
}

// =================================================================
// Menu types
// =================================================================

export interface CreateMenuItemOptions {
	accelerator: string;
}

export enum MenuItemLocation {
	File = 'file',
	Edit = 'edit',
	View = 'view',
	Note = 'note',
	Tools = 'tools',
	Help = 'help',

	/**
	 * @deprecated Do not use - same as NoteListContextMenu
	 */
	Context = 'context',

	// If adding an item here, don't forget to update isContextMenuItemLocation()

	/**
	 * When a command is called from the note list context menu, the
	 * command will receive the following arguments:
	 *
	 * - `noteIds:string[]`: IDs of the notes that were right-clicked on.
	 */
	NoteListContextMenu = 'noteListContextMenu',

	EditorContextMenu = 'editorContextMenu',

	/**
	 * When a command is called from a folder context menu, the
	 * command will receive the following arguments:
	 *
	 * - `folderId:string`: ID of the folder that was right-clicked on
	 */
	FolderContextMenu = 'folderContextMenu',

	/**
	 * When a command is called from a tag context menu, the
	 * command will receive the following arguments:
	 *
	 * - `tagId:string`: ID of the tag that was right-clicked on
	 */
	TagContextMenu = 'tagContextMenu',
}

export function isContextMenuItemLocation(location: MenuItemLocation): boolean {
	return [
		MenuItemLocation.Context,
		MenuItemLocation.NoteListContextMenu,
		MenuItemLocation.EditorContextMenu,
		MenuItemLocation.FolderContextMenu,
		MenuItemLocation.TagContextMenu,
	].includes(location);
}

export interface MenuItem {
	/**
	 * Command that should be associated with the menu item. All menu item should
	 * have a command associated with them unless they are a sub-menu.
	 */
	commandName?: string;

	/**
	 * Arguments that should be passed to the command. They will be as rest
	 * parameters.
	 */
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	commandArgs?: any[];

	/**
	 * Set to "separator" to create a divider line
	 */
	type?: ('normal' | 'separator' | 'submenu' | 'checkbox' | 'radio');

	/**
	 * Accelerator associated with the menu item
	 */
	accelerator?: string;

	/**
	 * Menu items that should appear below this menu item. Allows creating a menu tree.
	 */
	submenu?: MenuItem[];

	/**
	 * Menu item label. If not specified, the command label will be used instead.
	 */
	label?: string;
}

// =================================================================
// View API types
// =================================================================

export interface ButtonSpec {
	id: ButtonId;
	title?: string;
	onClick?(): void;
}

export type ButtonId = string;

export enum ToolbarButtonLocation {
	/**
	 * This toolbar in the top right corner of the application. It applies to the note as a whole, including its metadata.
	 *
	 * <span class="platform-desktop">desktop</span>
	 */
	NoteToolbar = 'noteToolbar',

	/**
	 * This toolbar is right above the text editor. It applies to the note body only.
	 */
	EditorToolbar = 'editorToolbar',
}

export type ViewHandle = string;

export interface EditorCommand {
	name: string;
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	value?: any;
}

export interface DialogResult {
	id: ButtonId;
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	formData?: any;
}

export enum ToastType {
	Info = 'info',
	Success = 'success',
	Error = 'error',
}

export interface Toast {
	message: string;
	type?: ToastType;
	duration?: number;
	timestamp?: number;
}

export interface Size {
	width?: number;
	height?: number;
}

export interface Rectangle {
	x?: number;
	y?: number;
	width?: number;
	height?: number;
}

export interface EditorUpdateEvent {
	newBody: string;
	noteId: string;
}
export type UpdateCallback = (event: EditorUpdateEvent)=> Promise<void>;


export interface ActivationCheckEvent {
	handle: ViewHandle;
	noteId: string;
}
export type ActivationCheckCallback = (event: ActivationCheckEvent)=> Promise<boolean>;

/**
 * Required callbacks for creating an editor plugin.
 */
export interface EditorPluginCallbacks {
	/**
	 * Emitted when the editor can potentially be activated - this is for example when the current
	 * note is changed, or when the application is opened. At that point you should check the
	 * current note and decide whether your editor should be activated or not. If it should, return
	 * `true`, otherwise return `false`.
	 */
	onActivationCheck: ActivationCheckCallback;

	/**
	 * Emitted when an editor view is created. This happens, for example, when a new window containing
	 * a new editor is created.
	 *
	 * This callback should set the editor plugin's HTML using `editors.setHtml`, add scripts to the editor
	 * with `editors.addScript`, and optionally listen for external changes using `editors.onUpdate`.
	 */
	onSetup: (handle: ViewHandle)=> Promise<void>;
}

export type VisibleHandler = ()=> Promise<void>;

export interface EditContextMenuFilterObject {
	items: MenuItem[];
}

export interface EditorActivationCheckFilterObject {
	effectiveNoteId: string;
	windowId: string;
	activatedEditors: {
		pluginId: string;
		viewId: string;
		isActive: boolean;
	}[];
}

export type FilterHandler<T> = (object: T)=> Promise<T>;

export type CommandArgument = string|number|object|boolean|null;

export interface MenuTemplateItem {
	label?: string;
	command?: string;
	commandArgs?: CommandArgument[];
}

export interface WebviewApi {
	postMessage: (message: object)=> unknown;
	onMessage: (message: object)=> void;
	menuPopupFromTemplate: (template: MenuTemplateItem[])=> void;
}

// =================================================================
// Settings types
// =================================================================

export enum SettingItemType {
	Int = 1,
	String = 2,
	Bool = 3,
	Array = 4,
	Object = 5,
	Button = 6,
}

export enum SettingItemSubType {
	FilePathAndArgs = 'file_path_and_args',
	FilePath = 'file_path', // Not supported on mobile!
	DirectoryPath = 'directory_path', // Not supported on mobile!
}

export enum AppType {
	Desktop = 'desktop',
	Mobile = 'mobile',
	Cli = 'cli',
}

export enum SettingStorage {
	Database = 1,
	File = 2,
}

// Redefine a simplified interface to mask internal details
// and to remove function calls as they would have to be async.
export interface SettingItem {
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	value: any;
	type: SettingItemType;

	/**
	 * Currently only used to display a file or directory selector. Always set
	 * `type` to `SettingItemType.String` when using this property.
	 */
	subType?: SettingItemSubType;

	label: string;
	description?: string;

	/**
	 * A public setting will appear in the Configuration screen and will be
	 * modifiable by the user. A private setting however will not appear there,
	 * and can only be changed programmatically. You may use this to store some
	 * values that you do not want to directly expose.
	 */
	public: boolean;

	/**
	 * You would usually set this to a section you would have created
	 * specifically for the plugin.
	 */
	section?: string;

	/**
	 * To create a setting with multiple options, set this property to `true`.
	 * That setting will render as a dropdown list in the configuration screen.
	 */
	isEnum?: boolean;

	/**
	 * This property is required when `isEnum` is `true`. In which case, it
	 * should contain a map of value => label.
	 */
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	options?: Record<any, any>;

	/**
	 * Reserved property. Not used at the moment.
	 */
	appTypes?: AppType[];

	/**
	 * Set this to `true` to store secure data, such as passwords. Any such
	 * setting will be stored in the system keychain if one is available.
	 */
	secure?: boolean;

	/**
	 * An advanced setting will be moved under the "Advanced" button in the
	 * config screen.
	 */
	advanced?: boolean;

	/**
	 * Set the min, max and step values if you want to restrict an int setting
	 * to a particular range.
	 */
	minimum?: number;
	maximum?: number;
	step?: number;

	/**
	 * Either store the setting in the database or in settings.json. Defaults to database.
	 */
	storage?: SettingStorage;
}

export interface SettingSection {
	label: string;
	iconName?: string;
	description?: string;
	name?: string;
}

// =================================================================
// Data API types
// =================================================================

/**
 * An array of at least one element and at most three elements.
 *
 * - **[0]**: Resource name (eg. "notes", "folders", "tags", etc.)
 * - **[1]**: (Optional) Resource ID.
 * - **[2]**: (Optional) Resource link.
 */
export type Path = string[];

// =================================================================
// Clipboard API types
// =================================================================

/**
 * Represents content that can be written to the clipboard in multiple formats.
 */
export interface ClipboardContent {
	/**
	 * Plain text representation of the content
	 */
	text?: string;

	/**
	 * HTML representation of the content
	 */
	html?: string;

	/**
	 * Image in [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) format
	 */
	image?: string;
}

// =================================================================
// Content Script types
// =================================================================

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
export type PostMessageHandler = (message: any)=> Promise<any>;

/**
 * When a content script is initialised, it receives a `context` object.
 */
export interface ContentScriptContext {
	/**
	 * The plugin ID that registered this content script
	 */
	pluginId: string;

	/**
	 * The content script ID, which may be necessary to post messages
	 */
	contentScriptId: string;

	/**
	 * Can be used by CodeMirror content scripts to post a message to the plugin
	 */
	postMessage: PostMessageHandler;
}

export interface ContentScriptModuleLoadedEvent {
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	userData?: any;
}

export interface ContentScriptModule {
	onLoaded?: (event: ContentScriptModuleLoadedEvent)=> void;
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	plugin: ()=> any;
	assets?: ()=> void;
}

export interface MarkdownItContentScriptModule extends Omit<ContentScriptModule, 'plugin'> {
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	plugin: (markdownIt: any, options: any)=> any;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
type EditorCommandCallback = (...args: any[])=> any;

export interface CodeMirrorControl {
	/** Points to a CodeMirror 6 EditorView instance. */
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	editor: any;
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	cm6: any;

	/** `extension` should be a [CodeMirror 6 extension](https://codemirror.net/docs/ref/#state.Extension). */
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	addExtension(extension: any|any[]): void;

	supportsCommand(name: string): boolean;
	// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
	execCommand(name: string, ...args: any[]): any;
	registerCommand(name: string, callback: EditorCommandCallback): void;

	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 [languageData-based autocompletion](https://codemirror.net/docs/ref/#autocomplete.autocompletion^config.override).
		 *
		 * Using `autocompletion({ override: [ ... ]})` causes errors when done by multiple plugins.
		 */
		// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
		completionSource(completionSource: any): any;

		/**
		 * Creates an extension that enables or disables [`languageData`-based autocompletion](https://codemirror.net/docs/ref/#autocomplete.autocompletion^config.override).
		 */
		// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
		enableLanguageDataAutocomplete: { of: (enabled: boolean)=> any };

		/**
		 * A CodeMirror [facet](https://codemirror.net/docs/ref/#state.EditorState.facet) that contains
		 * the ID of the note currently open in the editor.
		 *
		 * Access the value of this facet using
		 * ```ts
		 * const noteIdFacet = editorControl.joplinExtensions.noteIdFacet;
		 * const editorState = editorControl.editor.state;
		 * const noteId = editorState.facet(noteIdFacet);
		 * ```
		 */
		// eslint-disable-next-line @typescript-eslint/no-explicit-any -- No better type available
		noteIdFacet: any;
		/**
		 * A CodeMirror [StateEffect](https://codemirror.net/docs/ref/#state.StateEffect) that is
		 * included in a [Transaction](https://codemirror.net/docs/ref/#state.Transaction) when the
		 * note ID changes.
		 */
		// eslint-disable-next-line @typescript-eslint/no-explicit-any -- No better type available
		setNoteIdEffect: any;
	};
}

export interface MarkdownEditorContentScriptModule extends Omit<ContentScriptModule, 'plugin'> {
	plugin: (editorControl: CodeMirrorControl)=> void;
}

export enum ContentScriptType {
	/**
	 * Registers a new Markdown-It plugin, which should follow the template
	 * below.
	 *
	 * ```javascript
	 * module.exports = {
	 *     default: function(context) {
	 *         return {
	 *             plugin: function(markdownIt, pluginOptions) {
	 *                 // ...
	 *             },
	 *             assets: {
	 *                 // ...
	 *             },
	 *         }
	 *     }
	 * }
	 * ```
	 *
	 * See [the
	 * demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
	 * for a simple Markdown-it plugin example.
	 *
	 * ## Exported members
	 *
	 * - The `context` argument is currently unused but could be used later on
	 *   to provide access to your own plugin so that the content script and
	 *   plugin can communicate.
	 *
	 * - The **required** `plugin` key is the actual Markdown-It plugin - check
	 *   the [official doc](https://github.com/markdown-it/markdown-it) for more
	 *   information.
	 *
	 * - Using the **optional** `assets` key you may specify assets such as JS
	 *   or CSS that should be loaded in the rendered HTML document. Check for
	 *   example the Joplin [Mermaid
	 *   plugin](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts)
	 *   to see how the data should be structured.
	 *
	 * ## Supporting the Rich Text Editor
	 *
	 * Joplin's Rich Text Editor works with rendered HTML, which is converted back
	 * to markdown when saving. To prevent the original markdown for your plugin from
	 * being lost, Joplin needs additional metadata.
	 *
	 * To provide this,
	 * 1. Wrap the HTML generated by your plugin in an element with class `joplin-editable`.
	 *    For example,
	 *    ```html
	 *    <div class="joplin-editable">
	 *        ...your html...
	 *    </div>
	 *    ```
	 * 2. Add a child with class `joplin-source` that contains the original markdown that
	 *    was rendered by your plugin. Include `data-joplin-source-open`, `data-joplin-source-close`,
	 *    and `data-joplin-language` attributes.
	 *    For example, if your plugin rendered the following code block,
	 *    ````
	 *    ```foo
	 *    ... original source here ...
	 *    ```
	 *    ````
	 *    then it should render to
	 *    ```html
	 *    <div class="joplin-editable">
	 *        <pre
	 *            class="joplin-source"
	 *            data-joplin-language="foo"
	 *            data-joplin-source-open="```foo&NewLine;"
	 *            data-joplin-source-close="```"
	 *        > ... original source here ... </pre>
	 *        ... rendered HTML here ...
	 *    </div>
	 *    ```
	 *
	 * See [the demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
	 * for a complete example.
	 *
	 * ## Getting the settings from the renderer
	 *
	 * You can access your plugin settings from the renderer by calling
	 * `pluginOptions.settingValue("your-setting-key')`.
	 *
	 * ## Posting messages from the content script to your plugin
	 *
	 * The application provides the following function to allow executing
	 * commands from the rendered HTML code:
	 *
	 * ```javascript
	 * const response = await webviewApi.postMessage(contentScriptId, message);
	 * ```
	 *
	 * - `contentScriptId` is the ID you've defined when you registered the
	 *   content script. You can retrieve it from the
	 *   {@link ContentScriptContext | context}.
	 * - `message` can be any basic JavaScript type (number, string, plain
	 *   object), but it cannot be a function or class instance.
	 *
	 * When you post a message, the plugin can send back a `response` thus
	 * allowing two-way communication:
	 *
	 * ```javascript
	 * await joplin.contentScripts.onMessage(contentScriptId, (message) => {
	 *     // Process message
	 *     return response; // Can be any object, string or number
	 * });
	 * ```
	 *
	 * See {@link JoplinContentScripts.onMessage} for more details, as well as
	 * the [postMessage
	 * demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages).
	 *
	 * ## Registering an existing Markdown-it plugin
	 *
	 * To include a regular Markdown-It plugin, that doesn't make use of any
	 * Joplin-specific features, you would simply create a file such as this:
	 *
	 * ```javascript
	 * module.exports = {
	 *     default: function(context) {
	 *         return {
	 *             plugin: require('markdown-it-toc-done-right');
	 *         }
	 *     }
	 * }
	 * ```
	 */
	MarkdownItPlugin = 'markdownItPlugin',

	/**
	 * Registers a new CodeMirror plugin, which should follow the template
	 * below.
	 *
	 * ```javascript
	 * module.exports = {
	 *     default: function(context) {
	 *         return {
	 *             plugin: function(CodeMirror) {
	 *                 // ...
	 *             },
	 *             codeMirrorResources: [],
	 *             codeMirrorOptions: {
	 *                                  // ...
	 *                       },
	 *             assets: {
	 *                 // ...
	 *             },
	 *         }
	 *     }
	 * }
	 * ```
	 *
	 * - The `context` argument allows communicating with other parts of
	 *   your plugin (see below).
	 *
	 * - The `plugin` key is your CodeMirror plugin. This is where you can
	 *   register new commands with CodeMirror or interact with the CodeMirror
	 *   instance as needed.
	 *
	 * - **CodeMirror 5 only**: The `codeMirrorResources` key is an array of CodeMirror resources that
	 *   will be loaded and attached to the CodeMirror module. These are made up
	 *   of addons, keymaps, and modes. For example, for a plugin that want's to
	 *   enable clojure highlighting in code blocks. `codeMirrorResources` would
	 *   be set to `['mode/clojure/clojure']`.
	 *   This field is ignored on mobile and when the desktop beta editor is enabled.
	 *
	 * - **CodeMirror 5 only**: The `codeMirrorOptions` key contains all the
	 *   [CodeMirror](https://codemirror.net/doc/manual.html#config) options
	 *   that will be set or changed by this plugin. New options can alse be
	 *   declared via
	 *   [`CodeMirror.defineOption`](https://codemirror.net/doc/manual.html#defineOption),
	 *   and then have their value set here. For example, a plugin that enables
	 *   line numbers would set `codeMirrorOptions` to `{'lineNumbers': true}`.
	 *
	 * - Using the **optional** `assets` key you may specify **only** CSS assets
	 *   that should be loaded in the rendered HTML document. Check for example
	 *   the Joplin [Mermaid
	 *   plugin](https://github.com/laurent22/joplin/blob/dev/packages/renderer/MdToHtml/rules/mermaid.ts)
	 *   to see how the data should be structured.
	 *
	 * One of the `plugin`, `codeMirrorResources`, or `codeMirrorOptions` keys
	 * must be provided for the plugin to be valid. Having multiple or all
	 * provided is also okay.
	 *
	 * See also:
	 * - The [demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/codemirror_content_script)
	 *   for an example of all these keys being used in one plugin.
	 * - See [the editor plugin tutorial](https://joplinapp.org/help/api/tutorials/cm6_plugin)
	 *   for how to develop a plugin for the mobile editor and the desktop beta markdown editor.
	 *
	 * ## Posting messages from the content script to your plugin
	 *
	 * In order to post messages to the plugin, you can use the postMessage
	 * function passed to the {@link ContentScriptContext | context}.
	 *
	 * ```javascript
	 * const response = await context.postMessage('messageFromCodeMirrorContentScript');
	 * ```
	 *
	 * When you post a message, the plugin can send back a `response` thus
	 * allowing two-way communication:
	 *
	 * ```javascript
	 * await joplin.contentScripts.onMessage(contentScriptId, (message) => {
	 *     // Process message
	 *     return response; // Can be any object, string or number
	 * });
	 * ```
	 *
	 * See {@link JoplinContentScripts.onMessage} for more details, as well as
	 * the [postMessage
	 * demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/post_messages).
	 *
	 */
	CodeMirrorPlugin = 'codeMirrorPlugin',
}
