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

@ -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 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<EditorPaneProps> = ({ 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<EditorPaneProps> = ({ scrollState, onScroll, o
editorScrollExtension,
tablePasteExtensions,
fileInsertExtension,
autocompletion(),
autoCompletionExtension,
cursorActivityExtension,
updateViewContextExtension,
yjsExtension,
@ -99,6 +101,7 @@ export const EditorPane: React.FC<EditorPaneProps> = ({ scrollState, onScroll, o
[
linterExtension,
remoteCursorsExtension,
autoCompletionExtension,
editorScrollExtension,
tablePasteExtensions,
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())
})
}, [])
}