From 61032cb7450dd95977ff3d2a7b62c76606500391 Mon Sep 17 00:00:00 2001 From: Erik Michelson Date: Sun, 12 Mar 2023 18:35:44 +0100 Subject: [PATCH] feat(editor): add basic codemirror autocompletion suggestions Signed-off-by: Erik Michelson --- frontend/locales/en.json | 16 ++++++++++++ .../autocompletions/basic-completion.ts | 25 ++++++++++++++++++ .../autocompletions/regex-completion.ts | 26 +++++++++++++++++++ .../editor-page/editor-pane/editor-pane.tsx | 7 +++-- ...e-code-mirror-autocompletions-extension.ts | 20 ++++++++++++++ .../basic-markdown-syntax-app-extension.ts | 7 +++++ .../bootstrap-icon-app-extension.ts | 18 +++++++++++++ .../extensions/emoji/emoji-app-extension.ts | 16 ++++++++++++ .../extensions/emoji/mapping.ts | 2 ++ .../image-placeholder-app-extension.ts | 14 ++++++++++ .../table-of-contents-app-extension.ts | 7 +++++ frontend/src/extensions/base/app-extension.ts | 5 ++++ .../abcjs/abcjs-app-extension.ts | 9 +++++++ .../alert/alert-app-extension.ts | 14 ++++++++++ .../blockquote/blockquote-app-extension.ts | 13 ++++++++++ .../csv/csv-table-app-extension.ts | 9 +++++++ .../flowchart/flowchart-app-extension.ts | 9 +++++++ .../graphviz/graphviz-app-extension.ts | 9 +++++++ .../highlighted-code-fence-app-extension.ts | 22 ++++++++++++++++ .../mermaid/mermaid-app-extension.ts | 9 +++++++ .../plantuml/plantuml-app-extension.ts | 9 +++++++ .../spoiler/spoiler-app-extension.ts | 11 ++++++++ .../vega-lite/vega-lite-app-extension.ts | 9 +++++++ 23 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 frontend/src/components/editor-page/editor-pane/autocompletions/basic-completion.ts create mode 100644 frontend/src/components/editor-page/editor-pane/autocompletions/regex-completion.ts create mode 100644 frontend/src/components/editor-page/editor-pane/hooks/codemirror-extensions/use-code-mirror-autocompletions-extension.ts diff --git a/frontend/locales/en.json b/frontend/locales/en.json index 29813983b..7f462df48 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -373,6 +373,22 @@ "clipboard": "Clipboard", "file": "Markdown file" }, + "autocompletions": { + "link": "Link", + "icon": "Icon", + "emoji": "Emoji", + "image": "Image", + "imageWithDimensions": "Image with dimensions", + "toc": "Table of contents", + "successBox": "Success box", + "infoBox": "Info box", + "warningBox": "Warning box", + "errorBox": "Error box", + "tagName": "Name tag", + "tagColor": "Color tag", + "tagTime": "Time tag", + "spoiler": "Spoiler" + }, "modal": { "snippetImport": { "title": "Import from Snippet", diff --git a/frontend/src/components/editor-page/editor-pane/autocompletions/basic-completion.ts b/frontend/src/components/editor-page/editor-pane/autocompletions/basic-completion.ts new file mode 100644 index 000000000..2af6b2b28 --- /dev/null +++ b/frontend/src/components/editor-page/editor-pane/autocompletions/basic-completion.ts @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { regexCompletion } from './regex-completion' +import type { CompletionContext, CompletionResult } from '@codemirror/autocomplete' + +export const codeFenceRegex = /(?:^|\s)`(?:`|``|``\w+)?$/ + +/** + * Returns a {@link CompletionSource} for a regex-matching autocompletion with a single completion entry. + * + * @param regexToMatch The regex to match in front of the cursor + * @param replace The string to insert as completion + * @param description An optional description to show besides the suggestion + * @return A function to test and perform the configured completion. + */ +export const basicCompletion = ( + regexToMatch: RegExp, + replace: string, + description?: string +): ((_: CompletionContext) => CompletionResult | null) => { + return regexCompletion(regexToMatch, [{ label: replace, detail: description }]) +} diff --git a/frontend/src/components/editor-page/editor-pane/autocompletions/regex-completion.ts b/frontend/src/components/editor-page/editor-pane/autocompletions/regex-completion.ts new file mode 100644 index 000000000..26cf4c508 --- /dev/null +++ b/frontend/src/components/editor-page/editor-pane/autocompletions/regex-completion.ts @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete' + +/** + * Returns a {@link CompletionSource} for a regex-matching autocompletion. + * + * @param regexToMatch The regex to match in front of the cursor + * @param options The options to return + * @return A function to test and perform the configured completions. + */ +export const regexCompletion = + (regexToMatch: RegExp, options: Completion[]) => + (context: CompletionContext): CompletionResult | null => { + const match = context.matchBefore(regexToMatch) + if (!match || (match.from === match.to && !context.explicit)) { + return null + } + return { + from: match.from, + options + } + } diff --git a/frontend/src/components/editor-page/editor-pane/editor-pane.tsx b/frontend/src/components/editor-page/editor-pane/editor-pane.tsx index 72ad77c34..352208d59 100644 --- a/frontend/src/components/editor-page/editor-pane/editor-pane.tsx +++ b/frontend/src/components/editor-page/editor-pane/editor-pane.tsx @@ -10,6 +10,7 @@ import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute' import { findLanguageByCodeBlockName } from '../../markdown-renderer/extensions/base/code-block-markdown-extension/find-language-by-code-block-name' import type { ScrollProps } from '../synced-scroll/scroll-props' import styles from './extended-codemirror/codemirror.module.scss' +import { useCodeMirrorAutocompletionsExtension } from './hooks/codemirror-extensions/use-code-mirror-autocompletions-extension' import { useCodeMirrorFileInsertExtension } from './hooks/codemirror-extensions/use-code-mirror-file-insert-extension' import { useCodeMirrorRemoteCursorsExtension } from './hooks/codemirror-extensions/use-code-mirror-remote-cursor-extensions' import { useCodeMirrorScrollWatchExtension } from './hooks/codemirror-extensions/use-code-mirror-scroll-watch-extension' @@ -31,7 +32,6 @@ import { useLinter } from './linter/linter' import { MaxLengthWarning } from './max-length-warning/max-length-warning' import { StatusBar } from './status-bar/status-bar' import { ToolBar } from './tool-bar/tool-bar' -import { autocompletion } from '@codemirror/autocomplete' import { markdown, markdownLanguage } from '@codemirror/lang-markdown' import { languages } from '@codemirror/language-data' import { lintGutter } from '@codemirror/lint' @@ -62,6 +62,8 @@ export const EditorPane: React.FC = ({ scrollState, onScroll, o const fileInsertExtension = useCodeMirrorFileInsertExtension() const spellCheckExtension = useCodeMirrorSpellCheckExtension() const cursorActivityExtension = useCursorActivityCallback() + const autoCompletionExtension = useCodeMirrorAutocompletionsExtension() + const updateViewContextExtension = useUpdateCodeMirrorReference() const remoteCursorsExtension = useCodeMirrorRemoteCursorsExtension(messageTransporter) @@ -90,7 +92,7 @@ export const EditorPane: React.FC = ({ scrollState, onScroll, o editorScrollExtension, tablePasteExtensions, fileInsertExtension, - autocompletion(), + autoCompletionExtension, cursorActivityExtension, updateViewContextExtension, yjsExtension, @@ -99,6 +101,7 @@ export const EditorPane: React.FC = ({ scrollState, onScroll, o [ linterExtension, remoteCursorsExtension, + autoCompletionExtension, editorScrollExtension, tablePasteExtensions, fileInsertExtension, diff --git a/frontend/src/components/editor-page/editor-pane/hooks/codemirror-extensions/use-code-mirror-autocompletions-extension.ts b/frontend/src/components/editor-page/editor-pane/hooks/codemirror-extensions/use-code-mirror-autocompletions-extension.ts new file mode 100644 index 000000000..cdff6b005 --- /dev/null +++ b/frontend/src/components/editor-page/editor-pane/hooks/codemirror-extensions/use-code-mirror-autocompletions-extension.ts @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { optionalAppExtensions } from '../../../../../extensions/extra-integrations/optional-app-extensions' +import { autocompletion } from '@codemirror/autocomplete' +import type { Extension } from '@codemirror/state' +import { useMemo } from 'react' + +/** + * Returns a configured autocompletion extension that uses the autocompletions from the app extensions. + */ +export const useCodeMirrorAutocompletionsExtension = (): Extension => { + return useMemo(() => { + return autocompletion({ + override: optionalAppExtensions.flatMap((extension) => extension.buildAutocompletion()) + }) + }, []) +} diff --git a/frontend/src/components/markdown-renderer/extensions/basic-markdown-syntax/basic-markdown-syntax-app-extension.ts b/frontend/src/components/markdown-renderer/extensions/basic-markdown-syntax/basic-markdown-syntax-app-extension.ts index be0bf1f99..c1ea81dd0 100644 --- a/frontend/src/components/markdown-renderer/extensions/basic-markdown-syntax/basic-markdown-syntax-app-extension.ts +++ b/frontend/src/components/markdown-renderer/extensions/basic-markdown-syntax/basic-markdown-syntax-app-extension.ts @@ -5,8 +5,11 @@ */ import { AppExtension } from '../../../../extensions/base/app-extension' import type { CheatsheetExtension } from '../../../editor-page/cheatsheet/cheatsheet-extension' +import { basicCompletion } from '../../../editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../base/markdown-renderer-extension' import { BasicMarkdownSyntaxMarkdownExtension } from './basic-markdown-syntax-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' +import { t } from 'i18next' export class BasicMarkdownSyntaxAppExtension extends AppExtension { buildMarkdownRendererExtensions(): MarkdownRendererExtension[] { @@ -60,4 +63,8 @@ export class BasicMarkdownSyntaxAppExtension extends AppExtension { } ] } + + buildAutocompletion(): CompletionSource[] { + return [basicCompletion(/(^|\s)\[/, '[](https://)', t('editor.autocompletions.link') ?? undefined)] + } } diff --git a/frontend/src/components/markdown-renderer/extensions/bootstrap-icons/bootstrap-icon-app-extension.ts b/frontend/src/components/markdown-renderer/extensions/bootstrap-icons/bootstrap-icon-app-extension.ts index 24703ea87..cc0ba6c7f 100644 --- a/frontend/src/components/markdown-renderer/extensions/bootstrap-icons/bootstrap-icon-app-extension.ts +++ b/frontend/src/components/markdown-renderer/extensions/bootstrap-icons/bootstrap-icon-app-extension.ts @@ -4,9 +4,15 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import { AppExtension } from '../../../../extensions/base/app-extension' +import { BootstrapLazyIcons } from '../../../common/icons/bootstrap-icons' import type { CheatsheetExtension } from '../../../editor-page/cheatsheet/cheatsheet-extension' +import { regexCompletion } from '../../../editor-page/editor-pane/autocompletions/regex-completion' import type { MarkdownRendererExtension } from '../base/markdown-renderer-extension' import { BootstrapIconMarkdownExtension } from './bootstrap-icon-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' +import { t } from 'i18next' + +const bootstrapIconNames = Object.keys(BootstrapLazyIcons) export class BootstrapIconAppExtension extends AppExtension { buildMarkdownRendererExtensions(): MarkdownRendererExtension[] { @@ -16,4 +22,16 @@ export class BootstrapIconAppExtension extends AppExtension { buildCheatsheetExtensions(): CheatsheetExtension[] { return [{ i18nKey: 'bootstrapIcon', readMoreUrl: new URL('https://icons.getbootstrap.com/') }] } + + buildAutocompletion(): CompletionSource[] { + return [ + regexCompletion( + /:(?:[\w-]+:?)?/, + bootstrapIconNames.map((icon) => ({ + detail: t('editor.autocompletions.icon') ?? undefined, + label: `:bi-${icon}:` + })) + ) + ] + } } diff --git a/frontend/src/components/markdown-renderer/extensions/emoji/emoji-app-extension.ts b/frontend/src/components/markdown-renderer/extensions/emoji/emoji-app-extension.ts index 91dadab4c..ff2d801be 100644 --- a/frontend/src/components/markdown-renderer/extensions/emoji/emoji-app-extension.ts +++ b/frontend/src/components/markdown-renderer/extensions/emoji/emoji-app-extension.ts @@ -5,8 +5,12 @@ */ import { AppExtension } from '../../../../extensions/base/app-extension' import type { CheatsheetExtension } from '../../../editor-page/cheatsheet/cheatsheet-extension' +import { regexCompletion } from '../../../editor-page/editor-pane/autocompletions/regex-completion' import type { MarkdownRendererExtension } from '../base/markdown-renderer-extension' import { EmojiMarkdownExtension } from './emoji-markdown-extension' +import { emojiShortcodes } from './mapping' +import type { CompletionSource } from '@codemirror/autocomplete' +import { t } from 'i18next' export class EmojiAppExtension extends AppExtension { buildMarkdownRendererExtensions(): MarkdownRendererExtension[] { @@ -21,4 +25,16 @@ export class EmojiAppExtension extends AppExtension { } ] } + + buildAutocompletion(): CompletionSource[] { + return [ + regexCompletion( + /:(?:[\w-+]+:?)?/, + emojiShortcodes.map((shortcode) => ({ + detail: t('editor.autocompletions.emoji') ?? undefined, + label: `:${shortcode}:` + })) + ) + ] + } } diff --git a/frontend/src/components/markdown-renderer/extensions/emoji/mapping.ts b/frontend/src/components/markdown-renderer/extensions/emoji/mapping.ts index ff5651212..0e93a2db3 100644 --- a/frontend/src/components/markdown-renderer/extensions/emoji/mapping.ts +++ b/frontend/src/components/markdown-renderer/extensions/emoji/mapping.ts @@ -31,3 +31,5 @@ export const combinedEmojiData = { ...shortCodeMap, ...emojiSkinToneModifierMap } + +export const emojiShortcodes = Object.keys(combinedEmojiData) diff --git a/frontend/src/components/markdown-renderer/extensions/image-placeholder/image-placeholder-app-extension.ts b/frontend/src/components/markdown-renderer/extensions/image-placeholder/image-placeholder-app-extension.ts index e958a2360..2568f6563 100644 --- a/frontend/src/components/markdown-renderer/extensions/image-placeholder/image-placeholder-app-extension.ts +++ b/frontend/src/components/markdown-renderer/extensions/image-placeholder/image-placeholder-app-extension.ts @@ -5,8 +5,11 @@ */ import { AppExtension } from '../../../../extensions/base/app-extension' import type { CheatsheetExtension } from '../../../editor-page/cheatsheet/cheatsheet-extension' +import { basicCompletion } from '../../../editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../base/markdown-renderer-extension' import { ImagePlaceholderMarkdownExtension } from './image-placeholder-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' +import { t } from 'i18next' export class ImagePlaceholderAppExtension extends AppExtension { buildMarkdownRendererExtensions(): MarkdownRendererExtension[] { @@ -20,4 +23,15 @@ export class ImagePlaceholderAppExtension extends AppExtension { } ] } + + buildAutocompletion(): CompletionSource[] { + return [ + basicCompletion(/(^|\s)!\[?/, '![alt text](https://)', t('editor.autocompletions.image') ?? undefined), + basicCompletion( + /(^|\s)!\[?/, + '![alt text](https:// =200x500)', + t('editor.autocompletions.imageWithDimensions') ?? undefined + ) + ] + } } diff --git a/frontend/src/components/markdown-renderer/extensions/table-of-contents/table-of-contents-app-extension.ts b/frontend/src/components/markdown-renderer/extensions/table-of-contents/table-of-contents-app-extension.ts index 0e84b1e6f..b4a33c51b 100644 --- a/frontend/src/components/markdown-renderer/extensions/table-of-contents/table-of-contents-app-extension.ts +++ b/frontend/src/components/markdown-renderer/extensions/table-of-contents/table-of-contents-app-extension.ts @@ -5,9 +5,12 @@ */ import { AppExtension } from '../../../../extensions/base/app-extension' import type { CheatsheetExtension } from '../../../editor-page/cheatsheet/cheatsheet-extension' +import { basicCompletion } from '../../../editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../base/markdown-renderer-extension' import { TableOfContentsMarkdownExtension } from './table-of-contents-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' import type EventEmitter2 from 'eventemitter2' +import { t } from 'i18next' export class TableOfContentsAppExtension extends AppExtension { buildMarkdownRendererExtensions(eventEmitter?: EventEmitter2): MarkdownRendererExtension[] { @@ -29,4 +32,8 @@ export class TableOfContentsAppExtension extends AppExtension { } ] } + + buildAutocompletion(): CompletionSource[] { + return [basicCompletion(/\[(?:t|to|toc)?/, '[toc]', t('editor.autocompletions.toc') ?? undefined)] + } } diff --git a/frontend/src/extensions/base/app-extension.ts b/frontend/src/extensions/base/app-extension.ts index 29025b5c4..93ec1ff9e 100644 --- a/frontend/src/extensions/base/app-extension.ts +++ b/frontend/src/extensions/base/app-extension.ts @@ -6,6 +6,7 @@ import type { CheatsheetExtension } from '../../components/editor-page/cheatsheet/cheatsheet-extension' import type { Linter } from '../../components/editor-page/editor-pane/linter/linter' import type { MarkdownRendererExtension } from '../../components/markdown-renderer/extensions/base/markdown-renderer-extension' +import type { CompletionSource } from '@codemirror/autocomplete' import type { EventEmitter2 } from 'eventemitter2' import type React from 'react' import { Fragment } from 'react' @@ -27,4 +28,8 @@ export abstract class AppExtension { public buildCheatsheetExtensions(): CheatsheetExtension[] { return [] } + + public buildAutocompletion(): CompletionSource[] { + return [] + } } diff --git a/frontend/src/extensions/extra-integrations/abcjs/abcjs-app-extension.ts b/frontend/src/extensions/extra-integrations/abcjs/abcjs-app-extension.ts index c8136ede2..587ddf8e0 100644 --- a/frontend/src/extensions/extra-integrations/abcjs/abcjs-app-extension.ts +++ b/frontend/src/extensions/extra-integrations/abcjs/abcjs-app-extension.ts @@ -4,9 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' +import { + basicCompletion, + codeFenceRegex +} from '../../../components/editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension' import { AppExtension } from '../../base/app-extension' import { AbcjsMarkdownExtension } from './abcjs-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' export class AbcjsAppExtension extends AppExtension { buildMarkdownRendererExtensions(): MarkdownRendererExtension[] { @@ -16,4 +21,8 @@ export class AbcjsAppExtension extends AppExtension { buildCheatsheetExtensions(): CheatsheetExtension[] { return [{ i18nKey: 'abcjs', categoryI18nKey: 'charts', readMoreUrl: new URL('https://www.abcjs.net/') }] } + + buildAutocompletion(): CompletionSource[] { + return [basicCompletion(codeFenceRegex, '```abc\n\n```')] + } } diff --git a/frontend/src/extensions/extra-integrations/alert/alert-app-extension.ts b/frontend/src/extensions/extra-integrations/alert/alert-app-extension.ts index 07a087ce0..ff416de87 100644 --- a/frontend/src/extensions/extra-integrations/alert/alert-app-extension.ts +++ b/frontend/src/extensions/extra-integrations/alert/alert-app-extension.ts @@ -4,9 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' +import { basicCompletion } from '../../../components/editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension' import { AppExtension } from '../../base/app-extension' import { AlertMarkdownExtension } from './alert-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' +import { t } from 'i18next' + +const alertRegex = /(?:^|\s):(?::|::|::\w+)?/ /** * Adds alert boxes to the markdown rendering. @@ -19,4 +24,13 @@ export class AlertAppExtension extends AppExtension { buildCheatsheetExtensions(): CheatsheetExtension[] { return [{ i18nKey: 'alert' }] } + + buildAutocompletion(): CompletionSource[] { + return [ + basicCompletion(alertRegex, ':::success\n\n:::', t('editor.autocompletions.successBox') ?? undefined), + basicCompletion(alertRegex, ':::info\n\n:::', t('editor.autocompletions.infoBox') ?? undefined), + basicCompletion(alertRegex, ':::warning\n\n:::', t('editor.autocompletions.warningBox') ?? undefined), + basicCompletion(alertRegex, ':::danger\n\n:::', t('editor.autocompletions.errorBox') ?? undefined) + ] + } } diff --git a/frontend/src/extensions/extra-integrations/blockquote/blockquote-app-extension.ts b/frontend/src/extensions/extra-integrations/blockquote/blockquote-app-extension.ts index 130aab3a8..bdec2bcd7 100644 --- a/frontend/src/extensions/extra-integrations/blockquote/blockquote-app-extension.ts +++ b/frontend/src/extensions/extra-integrations/blockquote/blockquote-app-extension.ts @@ -4,9 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' +import { basicCompletion } from '../../../components/editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension' import { AppExtension } from '../../base/app-extension' import { BlockquoteExtraTagMarkdownExtension } from './blockquote-extra-tag-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' +import { t } from 'i18next' + +const blockquoteTagRegex = /(?:^|\s)\[(?:\w+)?/ /** * Adds support for generic blockquote extra tags and blockquote color extra tags. @@ -19,4 +24,12 @@ export class BlockquoteAppExtension extends AppExtension { buildCheatsheetExtensions(): CheatsheetExtension[] { return [{ i18nKey: 'blockquoteTags', entries: [{ i18nKey: 'name' }, { i18nKey: 'color' }, { i18nKey: 'time' }] }] } + + buildAutocompletion(): CompletionSource[] { + return [ + basicCompletion(blockquoteTagRegex, '[name=]', t('editor.autocompletions.tagName') ?? undefined), + basicCompletion(blockquoteTagRegex, '[time=]', t('editor.autocompletions.tagTime') ?? undefined), + basicCompletion(blockquoteTagRegex, '[color=]', t('editor.autocompletions.tagColor') ?? undefined) + ] + } } diff --git a/frontend/src/extensions/extra-integrations/csv/csv-table-app-extension.ts b/frontend/src/extensions/extra-integrations/csv/csv-table-app-extension.ts index 62c725679..75df77cd9 100644 --- a/frontend/src/extensions/extra-integrations/csv/csv-table-app-extension.ts +++ b/frontend/src/extensions/extra-integrations/csv/csv-table-app-extension.ts @@ -4,9 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' +import { + basicCompletion, + codeFenceRegex +} from '../../../components/editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension' import { AppExtension } from '../../base/app-extension' import { CsvTableMarkdownExtension } from './csv-table-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' /** * Adds support for csv tables to the markdown rendering. @@ -19,4 +24,8 @@ export class CsvTableAppExtension extends AppExtension { buildCheatsheetExtensions(): CheatsheetExtension[] { return [{ i18nKey: 'csv', entries: [{ i18nKey: 'table' }, { i18nKey: 'header' }] }] } + + buildAutocompletion(): CompletionSource[] { + return [basicCompletion(codeFenceRegex, '```csv delimiter=;\n\n```')] + } } diff --git a/frontend/src/extensions/extra-integrations/flowchart/flowchart-app-extension.ts b/frontend/src/extensions/extra-integrations/flowchart/flowchart-app-extension.ts index 0e7c8cef2..2758a508e 100644 --- a/frontend/src/extensions/extra-integrations/flowchart/flowchart-app-extension.ts +++ b/frontend/src/extensions/extra-integrations/flowchart/flowchart-app-extension.ts @@ -4,9 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' +import { + basicCompletion, + codeFenceRegex +} from '../../../components/editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension' import { AppExtension } from '../../base/app-extension' import { FlowchartMarkdownExtension } from './flowchart-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' /** * Adds support for flow charts to the markdown rendering. @@ -19,4 +24,8 @@ export class FlowchartAppExtension extends AppExtension { buildCheatsheetExtensions(): CheatsheetExtension[] { return [{ i18nKey: 'flowchart', categoryI18nKey: 'charts', readMoreUrl: new URL('https://flowchart.js.org/') }] } + + buildAutocompletion(): CompletionSource[] { + return [basicCompletion(codeFenceRegex, '```flow\n\n```')] + } } diff --git a/frontend/src/extensions/extra-integrations/graphviz/graphviz-app-extension.ts b/frontend/src/extensions/extra-integrations/graphviz/graphviz-app-extension.ts index 1587ba3c4..20a2ed83f 100644 --- a/frontend/src/extensions/extra-integrations/graphviz/graphviz-app-extension.ts +++ b/frontend/src/extensions/extra-integrations/graphviz/graphviz-app-extension.ts @@ -4,9 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' +import { + basicCompletion, + codeFenceRegex +} from '../../../components/editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension' import { AppExtension } from '../../base/app-extension' import { GraphvizMarkdownExtension } from './graphviz-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' /** * Adds support for graphviz to the markdown rendering. @@ -19,4 +24,8 @@ export class GraphvizAppExtension extends AppExtension { buildCheatsheetExtensions(): CheatsheetExtension[] { return [{ i18nKey: 'graphviz', categoryI18nKey: 'charts', readMoreUrl: new URL('https://graphviz.org/') }] } + + buildAutocompletion(): CompletionSource[] { + return [basicCompletion(codeFenceRegex, '```graphviz\n\n```')] + } } diff --git a/frontend/src/extensions/extra-integrations/highlighted-code-fence/highlighted-code-fence-app-extension.ts b/frontend/src/extensions/extra-integrations/highlighted-code-fence/highlighted-code-fence-app-extension.ts index 18dc6034b..4f356849c 100644 --- a/frontend/src/extensions/extra-integrations/highlighted-code-fence/highlighted-code-fence-app-extension.ts +++ b/frontend/src/extensions/extra-integrations/highlighted-code-fence/highlighted-code-fence-app-extension.ts @@ -4,9 +4,13 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' +import { codeFenceRegex } from '../../../components/editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension' import { AppExtension } from '../../base/app-extension' import { HighlightedCodeMarkdownExtension } from './highlighted-code-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' +import type { CompletionContext, CompletionResult } from '@codemirror/autocomplete' +import { languages } from '@codemirror/language-data' /** * Adds code highlighting to the markdown rendering. @@ -24,4 +28,22 @@ export class HighlightedCodeFenceAppExtension extends AppExtension { } ] } + + buildAutocompletion(): CompletionSource[] { + return [ + (context: CompletionContext): CompletionResult | null => { + const match = context.matchBefore(codeFenceRegex) + if (!match || (match.from === match.to && !context.explicit)) { + return null + } + return { + from: match.from, + options: languages.map((lang) => ({ + detail: lang.name, + label: '```' + lang.alias[0] + '\n\n```' + })) + } + } + ] + } } diff --git a/frontend/src/extensions/extra-integrations/mermaid/mermaid-app-extension.ts b/frontend/src/extensions/extra-integrations/mermaid/mermaid-app-extension.ts index e3612e041..aa033a8f1 100644 --- a/frontend/src/extensions/extra-integrations/mermaid/mermaid-app-extension.ts +++ b/frontend/src/extensions/extra-integrations/mermaid/mermaid-app-extension.ts @@ -4,9 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' +import { + basicCompletion, + codeFenceRegex +} from '../../../components/editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension' import { AppExtension } from '../../base/app-extension' import { MermaidMarkdownExtension } from './mermaid-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' /** * Adds support for chart rendering using mermaid to the markdown renderer. @@ -19,4 +24,8 @@ export class MermaidAppExtension extends AppExtension { buildCheatsheetExtensions(): CheatsheetExtension[] { return [{ i18nKey: 'mermaid', categoryI18nKey: 'charts', readMoreUrl: new URL('https://mermaid.js.org/') }] } + + buildAutocompletion(): CompletionSource[] { + return [basicCompletion(codeFenceRegex, '```mermaid\n\n```')] + } } diff --git a/frontend/src/extensions/extra-integrations/plantuml/plantuml-app-extension.ts b/frontend/src/extensions/extra-integrations/plantuml/plantuml-app-extension.ts index bcbba6c12..b0ee8aa21 100644 --- a/frontend/src/extensions/extra-integrations/plantuml/plantuml-app-extension.ts +++ b/frontend/src/extensions/extra-integrations/plantuml/plantuml-app-extension.ts @@ -4,9 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' +import { + basicCompletion, + codeFenceRegex +} from '../../../components/editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension' import { AppExtension } from '../../base/app-extension' import { PlantumlMarkdownExtension } from './plantuml-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' /** * Adds support for chart rendering using plantuml to the markdown renderer. @@ -21,4 +26,8 @@ export class PlantumlAppExtension extends AppExtension { buildCheatsheetExtensions(): CheatsheetExtension[] { return [{ i18nKey: 'plantuml', categoryI18nKey: 'charts', readMoreUrl: new URL('https://plantuml.com/') }] } + + buildAutocompletion(): CompletionSource[] { + return [basicCompletion(codeFenceRegex, '```plantuml\n\n```')] + } } diff --git a/frontend/src/extensions/extra-integrations/spoiler/spoiler-app-extension.ts b/frontend/src/extensions/extra-integrations/spoiler/spoiler-app-extension.ts index bf921fec5..8e5a359e6 100644 --- a/frontend/src/extensions/extra-integrations/spoiler/spoiler-app-extension.ts +++ b/frontend/src/extensions/extra-integrations/spoiler/spoiler-app-extension.ts @@ -4,9 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' +import { basicCompletion } from '../../../components/editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension' import { AppExtension } from '../../base/app-extension' import { SpoilerMarkdownExtension } from './spoiler-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' +import { t } from 'i18next' + +const spoilerRegex = /(?:^|\s):(?::|::|::\w+)?/ /** * Adds support for html spoiler tags. @@ -21,4 +26,10 @@ export class SpoilerAppExtension extends AppExtension { buildCheatsheetExtensions(): CheatsheetExtension[] { return [{ i18nKey: 'spoiler' }] } + + buildAutocompletion(): CompletionSource[] { + return [ + basicCompletion(spoilerRegex, ':::spoiler Label text\n\n:::', t('editor.autocompletions.spoiler') ?? undefined) + ] + } } diff --git a/frontend/src/extensions/extra-integrations/vega-lite/vega-lite-app-extension.ts b/frontend/src/extensions/extra-integrations/vega-lite/vega-lite-app-extension.ts index e06e4d1d9..fd9ce89b4 100644 --- a/frontend/src/extensions/extra-integrations/vega-lite/vega-lite-app-extension.ts +++ b/frontend/src/extensions/extra-integrations/vega-lite/vega-lite-app-extension.ts @@ -4,9 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' +import { + basicCompletion, + codeFenceRegex +} from '../../../components/editor-page/editor-pane/autocompletions/basic-completion' import type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension' import { AppExtension } from '../../base/app-extension' import { VegaLiteMarkdownExtension } from './vega-lite-markdown-extension' +import type { CompletionSource } from '@codemirror/autocomplete' /** * Adds support for chart rendering using vega lite to the markdown renderer. @@ -21,4 +26,8 @@ export class VegaLiteAppExtension extends AppExtension { { i18nKey: 'vegaLite', categoryI18nKey: 'charts', readMoreUrl: new URL('https://vega.github.io/vega-lite/') } ] } + + buildAutocompletion(): CompletionSource[] { + return [basicCompletion(codeFenceRegex, '```vega-lite\n\n```')] + } }