diff --git a/src/renderer/plugins.ts b/src/renderer/plugins.ts index 40bb705b..ca1c56b5 100644 --- a/src/renderer/plugins.ts +++ b/src/renderer/plugins.ts @@ -17,6 +17,7 @@ import statusBarGet from '@fe/plugins/status-bar-get' import editorPaste from '@fe/plugins/editor-paste' import editorAttachment from '@fe/plugins/editor-attachment' import editorMarkdown from '@fe/plugins/editor-markdown' +import editorEmoji from '@fe/plugins/editor-emoji' import copyText from '@fe/plugins/copy-text' import switchTodo from '@fe/plugins/switch-todo' import imageViewer from '@fe/plugins/image-viewer' @@ -69,6 +70,7 @@ export default [ editorPaste, editorAttachment, editorMarkdown, + editorEmoji, copyText, switchTodo, imageViewer, diff --git a/src/renderer/plugins/editor-emoji.ts b/src/renderer/plugins/editor-emoji.ts new file mode 100644 index 00000000..a7de2833 --- /dev/null +++ b/src/renderer/plugins/editor-emoji.ts @@ -0,0 +1,60 @@ +import type * as Monaco from 'monaco-editor' +import type { Ctx, Plugin } from '@fe/context' +import emoji from '@fe/others/emoji.json' + +const triggerCharacter = ':' + +class EmojiCompletionProvider implements Monaco.languages.CompletionItemProvider { + public readonly triggerCharacters = [triggerCharacter] + + private readonly monaco: typeof Monaco + private readonly ctx: Ctx + + constructor (monaco: typeof Monaco, ctx: Ctx) { + this.monaco = monaco + this.ctx = ctx + } + + public provideCompletionItems (model: Monaco.editor.IModel, position: Monaco.Position): Monaco.languages.CompletionList { + const line = model.getLineContent(position.lineNumber) + const cursor = position.column - 1 + const linePrefixText = line.slice(0, cursor) + + const match = linePrefixText.match(/:[a-zA-Z0-9]*$/) + if (!match || linePrefixText.charAt(linePrefixText.length - match[0].length - 1) === ':') { + return { suggestions: [] } + } + + const startPos = position.delta(0, -match[0].length) + + const result: Monaco.languages.CompletionItem[] = [] + Object.keys(emoji).forEach((key, i) => { + result.push({ + label: { label: `:${key} ${(emoji as any)[key]}` }, + kind: this.monaco.languages.CompletionItemKind.EnumMember, + insertText: (emoji as any)[key], + range: new this.monaco.Range( + position.lineNumber, + startPos.column, + position.lineNumber, + position.column + ), + sortText: i.toString().padStart(7), + }) + }) + + return { suggestions: result } + } +} + +export default { + name: 'editor-markdown', + register: (ctx) => { + ctx.editor.whenEditorReady().then(({ monaco }) => { + monaco.languages.registerCompletionItemProvider( + 'markdown', + new EmojiCompletionProvider(monaco, ctx) + ) + }) + } +} as Plugin diff --git a/src/renderer/plugins/editor-markdown.ts b/src/renderer/plugins/editor-markdown.ts index 2d9437f2..8d8baf53 100644 --- a/src/renderer/plugins/editor-markdown.ts +++ b/src/renderer/plugins/editor-markdown.ts @@ -6,7 +6,6 @@ import type { Plugin } from '@fe/context' import { t } from '@fe/services/i18n' import { getSetting } from '@fe/services/setting' import { isKeydown } from '@fe/core/command' -import emoji from '@fe/others/emoji.json' function getWords (content: string) { const words = new Set() @@ -39,26 +38,10 @@ function createDependencyProposals (range: Monaco.IRange, model: Monaco.editor.I const monaco = getMonaco() const currentWord = model.getWordUntilPosition(position).word - const currentStr = model.getValueInRange(range) const replaceRange = { ...range, endColumn: range.startColumn + 1 } const result: Monaco.languages.CompletionItem[] = [] - // emoji completion - if (/^:[^:]*$/.test(currentStr)) { - Object.keys(emoji).forEach((key, i) => { - result.push({ - label: { label: `:${key}: ${(emoji as any)[key]}` }, - kind: monaco.languages.CompletionItemKind.EnumMember, - insertText: (emoji as any)[key], - range: replaceRange, - sortText: (80000 + i).toString() - }) - }) - - return result - } - getWords(getValue()).forEach((word, i) => { if (currentWord !== word) { result.push({