feat(editor): add basic codemirror autocompletion suggestions

Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
Erik Michelson 2023-03-12 18:35:44 +01:00
parent 4956a99ced
commit 61032cb745
23 changed files with 284 additions and 2 deletions

View file

@ -373,6 +373,22 @@
"clipboard": "Clipboard", "clipboard": "Clipboard",
"file": "Markdown file" "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": { "modal": {
"snippetImport": { "snippetImport": {
"title": "Import from Snippet", "title": "Import from Snippet",

View file

@ -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 }])
}

View file

@ -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
}
}

View file

@ -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 { 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 type { ScrollProps } from '../synced-scroll/scroll-props'
import styles from './extended-codemirror/codemirror.module.scss' 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 { useCodeMirrorFileInsertExtension } from './hooks/codemirror-extensions/use-code-mirror-file-insert-extension'
import { useCodeMirrorRemoteCursorsExtension } from './hooks/codemirror-extensions/use-code-mirror-remote-cursor-extensions' import { useCodeMirrorRemoteCursorsExtension } from './hooks/codemirror-extensions/use-code-mirror-remote-cursor-extensions'
import { useCodeMirrorScrollWatchExtension } from './hooks/codemirror-extensions/use-code-mirror-scroll-watch-extension' 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 { MaxLengthWarning } from './max-length-warning/max-length-warning'
import { StatusBar } from './status-bar/status-bar' import { StatusBar } from './status-bar/status-bar'
import { ToolBar } from './tool-bar/tool-bar' import { ToolBar } from './tool-bar/tool-bar'
import { autocompletion } from '@codemirror/autocomplete'
import { markdown, markdownLanguage } from '@codemirror/lang-markdown' import { markdown, markdownLanguage } from '@codemirror/lang-markdown'
import { languages } from '@codemirror/language-data' import { languages } from '@codemirror/language-data'
import { lintGutter } from '@codemirror/lint' import { lintGutter } from '@codemirror/lint'
@ -62,6 +62,8 @@ export const EditorPane: React.FC<EditorPaneProps> = ({ scrollState, onScroll, o
const fileInsertExtension = useCodeMirrorFileInsertExtension() const fileInsertExtension = useCodeMirrorFileInsertExtension()
const spellCheckExtension = useCodeMirrorSpellCheckExtension() const spellCheckExtension = useCodeMirrorSpellCheckExtension()
const cursorActivityExtension = useCursorActivityCallback() const cursorActivityExtension = useCursorActivityCallback()
const autoCompletionExtension = useCodeMirrorAutocompletionsExtension()
const updateViewContextExtension = useUpdateCodeMirrorReference() const updateViewContextExtension = useUpdateCodeMirrorReference()
const remoteCursorsExtension = useCodeMirrorRemoteCursorsExtension(messageTransporter) const remoteCursorsExtension = useCodeMirrorRemoteCursorsExtension(messageTransporter)
@ -90,7 +92,7 @@ export const EditorPane: React.FC<EditorPaneProps> = ({ scrollState, onScroll, o
editorScrollExtension, editorScrollExtension,
tablePasteExtensions, tablePasteExtensions,
fileInsertExtension, fileInsertExtension,
autocompletion(), autoCompletionExtension,
cursorActivityExtension, cursorActivityExtension,
updateViewContextExtension, updateViewContextExtension,
yjsExtension, yjsExtension,
@ -99,6 +101,7 @@ export const EditorPane: React.FC<EditorPaneProps> = ({ scrollState, onScroll, o
[ [
linterExtension, linterExtension,
remoteCursorsExtension, remoteCursorsExtension,
autoCompletionExtension,
editorScrollExtension, editorScrollExtension,
tablePasteExtensions, tablePasteExtensions,
fileInsertExtension, fileInsertExtension,

View file

@ -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())
})
}, [])
}

View file

@ -5,8 +5,11 @@
*/ */
import { AppExtension } from '../../../../extensions/base/app-extension' import { AppExtension } from '../../../../extensions/base/app-extension'
import type { CheatsheetExtension } from '../../../editor-page/cheatsheet/cheatsheet-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 type { MarkdownRendererExtension } from '../base/markdown-renderer-extension'
import { BasicMarkdownSyntaxMarkdownExtension } from './basic-markdown-syntax-markdown-extension' import { BasicMarkdownSyntaxMarkdownExtension } from './basic-markdown-syntax-markdown-extension'
import type { CompletionSource } from '@codemirror/autocomplete'
import { t } from 'i18next'
export class BasicMarkdownSyntaxAppExtension extends AppExtension { export class BasicMarkdownSyntaxAppExtension extends AppExtension {
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] { buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
@ -60,4 +63,8 @@ export class BasicMarkdownSyntaxAppExtension extends AppExtension {
} }
] ]
} }
buildAutocompletion(): CompletionSource[] {
return [basicCompletion(/(^|\s)\[/, '[](https://)', t('editor.autocompletions.link') ?? undefined)]
}
} }

View file

@ -4,9 +4,15 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { AppExtension } from '../../../../extensions/base/app-extension' import { AppExtension } from '../../../../extensions/base/app-extension'
import { BootstrapLazyIcons } from '../../../common/icons/bootstrap-icons'
import type { CheatsheetExtension } from '../../../editor-page/cheatsheet/cheatsheet-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 type { MarkdownRendererExtension } from '../base/markdown-renderer-extension'
import { BootstrapIconMarkdownExtension } from './bootstrap-icon-markdown-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 { export class BootstrapIconAppExtension extends AppExtension {
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] { buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
@ -16,4 +22,16 @@ export class BootstrapIconAppExtension extends AppExtension {
buildCheatsheetExtensions(): CheatsheetExtension[] { buildCheatsheetExtensions(): CheatsheetExtension[] {
return [{ i18nKey: 'bootstrapIcon', readMoreUrl: new URL('https://icons.getbootstrap.com/') }] 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}:`
}))
)
]
}
} }

View file

@ -5,8 +5,12 @@
*/ */
import { AppExtension } from '../../../../extensions/base/app-extension' import { AppExtension } from '../../../../extensions/base/app-extension'
import type { CheatsheetExtension } from '../../../editor-page/cheatsheet/cheatsheet-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 type { MarkdownRendererExtension } from '../base/markdown-renderer-extension'
import { EmojiMarkdownExtension } from './emoji-markdown-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 { export class EmojiAppExtension extends AppExtension {
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] { 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}:`
}))
)
]
}
} }

View file

@ -31,3 +31,5 @@ export const combinedEmojiData = {
...shortCodeMap, ...shortCodeMap,
...emojiSkinToneModifierMap ...emojiSkinToneModifierMap
} }
export const emojiShortcodes = Object.keys(combinedEmojiData)

View file

@ -5,8 +5,11 @@
*/ */
import { AppExtension } from '../../../../extensions/base/app-extension' import { AppExtension } from '../../../../extensions/base/app-extension'
import type { CheatsheetExtension } from '../../../editor-page/cheatsheet/cheatsheet-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 type { MarkdownRendererExtension } from '../base/markdown-renderer-extension'
import { ImagePlaceholderMarkdownExtension } from './image-placeholder-markdown-extension' import { ImagePlaceholderMarkdownExtension } from './image-placeholder-markdown-extension'
import type { CompletionSource } from '@codemirror/autocomplete'
import { t } from 'i18next'
export class ImagePlaceholderAppExtension extends AppExtension { export class ImagePlaceholderAppExtension extends AppExtension {
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] { 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
)
]
}
} }

View file

@ -5,9 +5,12 @@
*/ */
import { AppExtension } from '../../../../extensions/base/app-extension' import { AppExtension } from '../../../../extensions/base/app-extension'
import type { CheatsheetExtension } from '../../../editor-page/cheatsheet/cheatsheet-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 type { MarkdownRendererExtension } from '../base/markdown-renderer-extension'
import { TableOfContentsMarkdownExtension } from './table-of-contents-markdown-extension' import { TableOfContentsMarkdownExtension } from './table-of-contents-markdown-extension'
import type { CompletionSource } from '@codemirror/autocomplete'
import type EventEmitter2 from 'eventemitter2' import type EventEmitter2 from 'eventemitter2'
import { t } from 'i18next'
export class TableOfContentsAppExtension extends AppExtension { export class TableOfContentsAppExtension extends AppExtension {
buildMarkdownRendererExtensions(eventEmitter?: EventEmitter2): MarkdownRendererExtension[] { 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)]
}
} }

View file

@ -6,6 +6,7 @@
import type { CheatsheetExtension } from '../../components/editor-page/cheatsheet/cheatsheet-extension' import type { CheatsheetExtension } from '../../components/editor-page/cheatsheet/cheatsheet-extension'
import type { Linter } from '../../components/editor-page/editor-pane/linter/linter' 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 { MarkdownRendererExtension } from '../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
import type { CompletionSource } from '@codemirror/autocomplete'
import type { EventEmitter2 } from 'eventemitter2' import type { EventEmitter2 } from 'eventemitter2'
import type React from 'react' import type React from 'react'
import { Fragment } from 'react' import { Fragment } from 'react'
@ -27,4 +28,8 @@ export abstract class AppExtension {
public buildCheatsheetExtensions(): CheatsheetExtension[] { public buildCheatsheetExtensions(): CheatsheetExtension[] {
return [] return []
} }
public buildAutocompletion(): CompletionSource[] {
return []
}
} }

View file

@ -4,9 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' 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 type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
import { AppExtension } from '../../base/app-extension' import { AppExtension } from '../../base/app-extension'
import { AbcjsMarkdownExtension } from './abcjs-markdown-extension' import { AbcjsMarkdownExtension } from './abcjs-markdown-extension'
import type { CompletionSource } from '@codemirror/autocomplete'
export class AbcjsAppExtension extends AppExtension { export class AbcjsAppExtension extends AppExtension {
buildMarkdownRendererExtensions(): MarkdownRendererExtension[] { buildMarkdownRendererExtensions(): MarkdownRendererExtension[] {
@ -16,4 +21,8 @@ export class AbcjsAppExtension extends AppExtension {
buildCheatsheetExtensions(): CheatsheetExtension[] { buildCheatsheetExtensions(): CheatsheetExtension[] {
return [{ i18nKey: 'abcjs', categoryI18nKey: 'charts', readMoreUrl: new URL('https://www.abcjs.net/') }] return [{ i18nKey: 'abcjs', categoryI18nKey: 'charts', readMoreUrl: new URL('https://www.abcjs.net/') }]
} }
buildAutocompletion(): CompletionSource[] {
return [basicCompletion(codeFenceRegex, '```abc\n\n```')]
}
} }

View file

@ -4,9 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' 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 type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
import { AppExtension } from '../../base/app-extension' import { AppExtension } from '../../base/app-extension'
import { AlertMarkdownExtension } from './alert-markdown-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. * Adds alert boxes to the markdown rendering.
@ -19,4 +24,13 @@ export class AlertAppExtension extends AppExtension {
buildCheatsheetExtensions(): CheatsheetExtension[] { buildCheatsheetExtensions(): CheatsheetExtension[] {
return [{ i18nKey: 'alert' }] 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)
]
}
} }

View file

@ -4,9 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' 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 type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
import { AppExtension } from '../../base/app-extension' import { AppExtension } from '../../base/app-extension'
import { BlockquoteExtraTagMarkdownExtension } from './blockquote-extra-tag-markdown-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. * Adds support for generic blockquote extra tags and blockquote color extra tags.
@ -19,4 +24,12 @@ export class BlockquoteAppExtension extends AppExtension {
buildCheatsheetExtensions(): CheatsheetExtension[] { buildCheatsheetExtensions(): CheatsheetExtension[] {
return [{ i18nKey: 'blockquoteTags', entries: [{ i18nKey: 'name' }, { i18nKey: 'color' }, { i18nKey: 'time' }] }] 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)
]
}
} }

View file

@ -4,9 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' 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 type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
import { AppExtension } from '../../base/app-extension' import { AppExtension } from '../../base/app-extension'
import { CsvTableMarkdownExtension } from './csv-table-markdown-extension' import { CsvTableMarkdownExtension } from './csv-table-markdown-extension'
import type { CompletionSource } from '@codemirror/autocomplete'
/** /**
* Adds support for csv tables to the markdown rendering. * Adds support for csv tables to the markdown rendering.
@ -19,4 +24,8 @@ export class CsvTableAppExtension extends AppExtension {
buildCheatsheetExtensions(): CheatsheetExtension[] { buildCheatsheetExtensions(): CheatsheetExtension[] {
return [{ i18nKey: 'csv', entries: [{ i18nKey: 'table' }, { i18nKey: 'header' }] }] return [{ i18nKey: 'csv', entries: [{ i18nKey: 'table' }, { i18nKey: 'header' }] }]
} }
buildAutocompletion(): CompletionSource[] {
return [basicCompletion(codeFenceRegex, '```csv delimiter=;\n\n```')]
}
} }

View file

@ -4,9 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' 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 type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
import { AppExtension } from '../../base/app-extension' import { AppExtension } from '../../base/app-extension'
import { FlowchartMarkdownExtension } from './flowchart-markdown-extension' import { FlowchartMarkdownExtension } from './flowchart-markdown-extension'
import type { CompletionSource } from '@codemirror/autocomplete'
/** /**
* Adds support for flow charts to the markdown rendering. * Adds support for flow charts to the markdown rendering.
@ -19,4 +24,8 @@ export class FlowchartAppExtension extends AppExtension {
buildCheatsheetExtensions(): CheatsheetExtension[] { buildCheatsheetExtensions(): CheatsheetExtension[] {
return [{ i18nKey: 'flowchart', categoryI18nKey: 'charts', readMoreUrl: new URL('https://flowchart.js.org/') }] return [{ i18nKey: 'flowchart', categoryI18nKey: 'charts', readMoreUrl: new URL('https://flowchart.js.org/') }]
} }
buildAutocompletion(): CompletionSource[] {
return [basicCompletion(codeFenceRegex, '```flow\n\n```')]
}
} }

View file

@ -4,9 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' 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 type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
import { AppExtension } from '../../base/app-extension' import { AppExtension } from '../../base/app-extension'
import { GraphvizMarkdownExtension } from './graphviz-markdown-extension' import { GraphvizMarkdownExtension } from './graphviz-markdown-extension'
import type { CompletionSource } from '@codemirror/autocomplete'
/** /**
* Adds support for graphviz to the markdown rendering. * Adds support for graphviz to the markdown rendering.
@ -19,4 +24,8 @@ export class GraphvizAppExtension extends AppExtension {
buildCheatsheetExtensions(): CheatsheetExtension[] { buildCheatsheetExtensions(): CheatsheetExtension[] {
return [{ i18nKey: 'graphviz', categoryI18nKey: 'charts', readMoreUrl: new URL('https://graphviz.org/') }] return [{ i18nKey: 'graphviz', categoryI18nKey: 'charts', readMoreUrl: new URL('https://graphviz.org/') }]
} }
buildAutocompletion(): CompletionSource[] {
return [basicCompletion(codeFenceRegex, '```graphviz\n\n```')]
}
} }

View file

@ -4,9 +4,13 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' 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 type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
import { AppExtension } from '../../base/app-extension' import { AppExtension } from '../../base/app-extension'
import { HighlightedCodeMarkdownExtension } from './highlighted-code-markdown-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. * 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```'
}))
}
}
]
}
} }

View file

@ -4,9 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' 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 type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
import { AppExtension } from '../../base/app-extension' import { AppExtension } from '../../base/app-extension'
import { MermaidMarkdownExtension } from './mermaid-markdown-extension' import { MermaidMarkdownExtension } from './mermaid-markdown-extension'
import type { CompletionSource } from '@codemirror/autocomplete'
/** /**
* Adds support for chart rendering using mermaid to the markdown renderer. * Adds support for chart rendering using mermaid to the markdown renderer.
@ -19,4 +24,8 @@ export class MermaidAppExtension extends AppExtension {
buildCheatsheetExtensions(): CheatsheetExtension[] { buildCheatsheetExtensions(): CheatsheetExtension[] {
return [{ i18nKey: 'mermaid', categoryI18nKey: 'charts', readMoreUrl: new URL('https://mermaid.js.org/') }] return [{ i18nKey: 'mermaid', categoryI18nKey: 'charts', readMoreUrl: new URL('https://mermaid.js.org/') }]
} }
buildAutocompletion(): CompletionSource[] {
return [basicCompletion(codeFenceRegex, '```mermaid\n\n```')]
}
} }

View file

@ -4,9 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' 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 type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
import { AppExtension } from '../../base/app-extension' import { AppExtension } from '../../base/app-extension'
import { PlantumlMarkdownExtension } from './plantuml-markdown-extension' import { PlantumlMarkdownExtension } from './plantuml-markdown-extension'
import type { CompletionSource } from '@codemirror/autocomplete'
/** /**
* Adds support for chart rendering using plantuml to the markdown renderer. * Adds support for chart rendering using plantuml to the markdown renderer.
@ -21,4 +26,8 @@ export class PlantumlAppExtension extends AppExtension {
buildCheatsheetExtensions(): CheatsheetExtension[] { buildCheatsheetExtensions(): CheatsheetExtension[] {
return [{ i18nKey: 'plantuml', categoryI18nKey: 'charts', readMoreUrl: new URL('https://plantuml.com/') }] return [{ i18nKey: 'plantuml', categoryI18nKey: 'charts', readMoreUrl: new URL('https://plantuml.com/') }]
} }
buildAutocompletion(): CompletionSource[] {
return [basicCompletion(codeFenceRegex, '```plantuml\n\n```')]
}
} }

View file

@ -4,9 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' 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 type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
import { AppExtension } from '../../base/app-extension' import { AppExtension } from '../../base/app-extension'
import { SpoilerMarkdownExtension } from './spoiler-markdown-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. * Adds support for html spoiler tags.
@ -21,4 +26,10 @@ export class SpoilerAppExtension extends AppExtension {
buildCheatsheetExtensions(): CheatsheetExtension[] { buildCheatsheetExtensions(): CheatsheetExtension[] {
return [{ i18nKey: 'spoiler' }] return [{ i18nKey: 'spoiler' }]
} }
buildAutocompletion(): CompletionSource[] {
return [
basicCompletion(spoilerRegex, ':::spoiler Label text\n\n:::', t('editor.autocompletions.spoiler') ?? undefined)
]
}
} }

View file

@ -4,9 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { CheatsheetExtension } from '../../../components/editor-page/cheatsheet/cheatsheet-extension' 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 type { MarkdownRendererExtension } from '../../../components/markdown-renderer/extensions/base/markdown-renderer-extension'
import { AppExtension } from '../../base/app-extension' import { AppExtension } from '../../base/app-extension'
import { VegaLiteMarkdownExtension } from './vega-lite-markdown-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. * 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/') } { i18nKey: 'vegaLite', categoryI18nKey: 'charts', readMoreUrl: new URL('https://vega.github.io/vega-lite/') }
] ]
} }
buildAutocompletion(): CompletionSource[] {
return [basicCompletion(codeFenceRegex, '```vega-lite\n\n```')]
}
} }