From fb251760acd2882850c964d4de0d50322da8c6c7 Mon Sep 17 00:00:00 2001 From: drl990114 Date: Mon, 22 Jul 2024 22:11:49 +0800 Subject: [PATCH] feat: sourcecode add onCodemirrorViewLoad callback prop --- package.json | 4 ++-- src/App.tsx | 11 +++++++-- src/editor/codemirror/codemirror.ts | 23 +++++++++++++++---- .../components/SourceEditor/delegate.ts | 15 +++++++++--- .../CodeMirror/codemirror-extension.ts | 7 ++++-- .../CodeMirror/codemirror-node-view.ts | 12 +++++++--- .../extensions/CodeMirror/codemirror-types.ts | 13 +++++++---- .../extensions/HtmlNode/html-block-view.ts | 2 +- src/editor/theme/WysiwygThemeWrapper.tsx | 12 +++++----- src/playground/content.ts | 4 ++++ yarn.lock | 15 ++++++++---- 11 files changed, 87 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index 3f93570..373b963 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rme", - "version": "0.0.65", + "version": "0.0.73", "type": "module", "scripts": { "build": "yarn clear && yarn build-esbuild && yarn types", @@ -177,6 +177,6 @@ "svgmoji": "^3.2.0", "void-elements": "^3.1.0", "yjs": "^13.6.14", - "zens": "^0.0.26" + "zens": "^0.0.32" } } diff --git a/src/App.tsx b/src/App.tsx index 251fc53..cf539cf 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,8 +6,9 @@ import { WysiwygThemeWrapper, Preview, createSourceCodeDelegate, + extractMatches, } from '.' -import React, { FC, useLayoutEffect, useMemo, useRef, useState } from 'react' +import React, { FC, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' import useDevTools from './playground/hooks/use-devtools' import useContent from './playground/hooks/use-content' import { DebugConsole } from './playground/components/DebugConsole' @@ -54,6 +55,7 @@ function App() { { + extractMatches(cmNodeView.cm) + console.log('cmNodeView', cmNodeView) + } + })) editorRef.current?.toggleType('sourceCode') } else { editorRef.current?.toggleType('preview') diff --git a/src/editor/codemirror/codemirror.ts b/src/editor/codemirror/codemirror.ts index 3b73f55..f5a1178 100644 --- a/src/editor/codemirror/codemirror.ts +++ b/src/editor/codemirror/codemirror.ts @@ -14,13 +14,14 @@ import type { EditorSchema, EditorView, ProsemirrorNode } from '@remirror/pm' import { exitCode } from '@remirror/pm/commands' import { Selection, TextSelection } from '@remirror/pm/state' import { lightTheme } from '../../editor/theme' -import type { LanguageDescription, LanguageSupport } from '@codemirror/language' +import { ensureSyntaxTree, type LanguageDescription, type LanguageSupport } from '@codemirror/language' import { languages } from '@codemirror/language-data' import { nanoid } from 'nanoid' import type { LoadLanguage } from '../extensions/CodeMirror/codemirror-node-view' import { createTheme } from './theme' import type { CreateThemeOptions } from './theme' import { redo, undo } from '@remirror/pm/history' +import { Tree, SyntaxNodeRef } from '@lezer/common' const cmInstanceMap = new Map() const themeRef = { current: createTheme(lightTheme.codemirrorTheme as CreateThemeOptions) } @@ -34,6 +35,22 @@ export const changeTheme = (theme: CreateThemeOptions): void => { }) } +export const extractMatches = (view: CodeMirrorEditorView) => { + const tree: Tree | null = ensureSyntaxTree(view.state, view.state.doc.length); + const matches: any[] = []; + tree?.iterate({ + from: 0, + to: view.state.doc.length, + enter: ({ type, from, to }: SyntaxNodeRef) => { + if (type.name.startsWith('ATXHeading')) { + matches.push({ from, to, value: view.state.doc.sliceString(from, to), type: type.name }); + } + } + }); + + return matches +} + export type CreateCodemirrorOptions = { /** * when it is true, undo and redo will use prosemirror view. @@ -43,7 +60,7 @@ export type CreateCodemirrorOptions = { codemirrorEditorViewConfig?: CodeMirrorEditorViewConfig } -class MfCodemirrorView { +export class MfCodemirrorView { private readonly view: EditorView private readonly getPos: () => number @@ -429,5 +446,3 @@ export function computeChange( return { from: start, to: oldEnd, text: newVal.slice(start, newEnd) } } - -export default MfCodemirrorView diff --git a/src/editor/components/SourceEditor/delegate.ts b/src/editor/components/SourceEditor/delegate.ts index 5eae00b..99d6862 100644 --- a/src/editor/components/SourceEditor/delegate.ts +++ b/src/editor/components/SourceEditor/delegate.ts @@ -6,20 +6,29 @@ import { LineCodeMirrorExtension } from '../../extensions/CodeMirror/codemirror- import { markdown } from '@codemirror/lang-markdown' import { basicSetup } from '../../extensions/CodeMirror/setup' import { CountExtension } from '@remirror/extension-count' +import { MfCodemirrorView } from '../../codemirror/codemirror' -export function createSourceCodeManager(): RemirrorManager { +type CreateSourceCodeManagerOptions = { + onCodemirrorViewLoad: (cm: MfCodemirrorView) => void +} +export function createSourceCodeManager( + options?: CreateSourceCodeManagerOptions, +): RemirrorManager { return createReactManager(() => [ new CountExtension({}), new DocExtension({ content: 'codeMirror' }), new LineCodeMirrorExtension({ hideDecoration: true, extensions: [basicSetup, markdown()], + onCodemirrorViewLoad: options?.onCodemirrorViewLoad, }), ]) } -export const createSourceCodeDelegate = (): EditorDelegate => { - const manager = createSourceCodeManager() +export const createSourceCodeDelegate = ( + options?: CreateSourceCodeManagerOptions, +): EditorDelegate => { + const manager = createSourceCodeManager(options) const stringToDoc: StringToDoc = (content: string) => { const schema = manager.schema diff --git a/src/editor/extensions/CodeMirror/codemirror-extension.ts b/src/editor/extensions/CodeMirror/codemirror-extension.ts index 260583a..79b79e6 100644 --- a/src/editor/extensions/CodeMirror/codemirror-extension.ts +++ b/src/editor/extensions/CodeMirror/codemirror-extension.ts @@ -43,7 +43,8 @@ export const fakeIndentedLanguage = 'indent-code' hideDecoration: false, extensions: null, toggleName: 'paragraph', - useProsemirrorHistoryKey: false + useProsemirrorHistoryKey: false, + onCodemirrorViewLoad: () => {} }, staticKeys: [], handlerKeys: [], @@ -51,6 +52,7 @@ export const fakeIndentedLanguage = 'indent-code' }) export class LineCodeMirrorExtension extends NodeExtension { private nodeview: CodeMirror6NodeView | undefined + get name() { return 'codeMirror' as const } @@ -92,7 +94,8 @@ export class LineCodeMirrorExtension extends NodeExtension Promise | LanguageSupport | void @@ -20,14 +20,16 @@ export class CodeMirror6NodeView implements NodeView { view, getPos, extensions, - options + options, + onCodemirrorViewLoad }: { node: ProsemirrorNode view: EditorView getPos: () => number extensions: CodeMirrorExtension[] | null options?: CreateCodemirrorOptions - toggleName: string + toggleName: string, + onCodemirrorViewLoad: (cm: MfCodemirrorView) => void }) { this.node = node this.view = view @@ -42,6 +44,10 @@ export class CodeMirror6NodeView implements NodeView { languageName: this.languageName, options }) + + if (onCodemirrorViewLoad) { + onCodemirrorViewLoad(this.mfCodemirrorView) + } // Create a CodeMirror instance this.cm = this.mfCodemirrorView.cm diff --git a/src/editor/extensions/CodeMirror/codemirror-types.ts b/src/editor/extensions/CodeMirror/codemirror-types.ts index b94a48a..03f5b91 100644 --- a/src/editor/extensions/CodeMirror/codemirror-types.ts +++ b/src/editor/extensions/CodeMirror/codemirror-types.ts @@ -1,3 +1,4 @@ +import { MfCodemirrorView } from '@/editor/codemirror/codemirror' import type { Extension as CodeMirrorExtension } from '@codemirror/state' import type { ProsemirrorAttributes } from '@remirror/core' @@ -7,7 +8,7 @@ export interface CodeMirrorExtensionOptions { * * @defaultValue false */ - hideDecoration?: boolean; + hideDecoration?: boolean /** * The CodeMirror extensions to use. * @@ -20,19 +21,23 @@ export interface CodeMirrorExtensionOptions { * * @defaultValue null */ - extensions?: CodeMirrorExtension[] | null; + extensions?: CodeMirrorExtension[] | null /** * The name of the node that the codeMirror block should toggle back and forth from. * * @defaultValue "paragraph" */ - toggleName?: string; + toggleName?: string /** * @defaultValue false */ useProsemirrorHistoryKey?: boolean + /** + * This will return the codemirror node view instance, which is very useful in source code mode + */ + onCodemirrorViewLoad?: (cm: MfCodemirrorView) => void } export interface CodeMirrorExtensionAttributes extends ProsemirrorAttributes { @@ -42,5 +47,5 @@ export interface CodeMirrorExtensionAttributes extends ProsemirrorAttributes { * * @defaultValue '' */ - language?: string; + language?: string } diff --git a/src/editor/extensions/HtmlNode/html-block-view.ts b/src/editor/extensions/HtmlNode/html-block-view.ts index d3eecdc..5831808 100644 --- a/src/editor/extensions/HtmlNode/html-block-view.ts +++ b/src/editor/extensions/HtmlNode/html-block-view.ts @@ -5,7 +5,7 @@ import type { ProsemirrorNode } from 'remirror' import { type EditorSchema } from 'remirror' import type { EditorView as CodeMirrorEditorView } from '@codemirror/view' import { Compartment } from '@codemirror/state' -import MfCodemirrorView from '@/editor/codemirror/codemirror' +import { MfCodemirrorView } from '@/editor/codemirror/codemirror' import { minimalSetup } from '../CodeMirror/setup' import { html } from '@codemirror/lang-html' diff --git a/src/editor/theme/WysiwygThemeWrapper.tsx b/src/editor/theme/WysiwygThemeWrapper.tsx index 8a6b844..b9398b5 100644 --- a/src/editor/theme/WysiwygThemeWrapper.tsx +++ b/src/editor/theme/WysiwygThemeWrapper.tsx @@ -1000,37 +1000,37 @@ export const WysiwygThemeWrapper = styled.div.attrs((p) => ({ ` return css` & h1 { - &:hover::before { + &::before { content: 'h1'; ${style} } } & h2 { - &:hover::before { + &::before { content: 'h2'; ${style} } } & h3 { - &:hover::before { + &::before { content: 'h3'; ${style} } } & h4 { - &:hover::before { + &::before { content: 'h4'; ${style} } } & h5 { - &:hover::before { + &::before { content: 'h5'; ${style} } } & h6 { - &:hover::before { + &::before { content: 'h6'; ${style} } diff --git a/src/playground/content.ts b/src/playground/content.ts index a1300a5..ca7cd05 100644 --- a/src/playground/content.ts +++ b/src/playground/content.ts @@ -10,6 +10,10 @@ console.log("hello world") const defaultContent = [ ` +# Exploring the Dystopian Depths of "Wool" + +The world is almost ruined. None of the remaining people live on the surface, where deadly toxic air makes life impossible. To survive, humankind has no option but to live in a massive underground silo with numerous levels, bound by strict social rules.  
 
Holston, the silo's sheriff, who has dedicated himself to law enforcement for years, begins the story with a profound sadness at the loss of his wife. Three years ago, she ventured out for "cleaning," a grim sentence for those who wonder what the outside world is like or dare to discuss the past. This task, essential yet fatal, involves cleaning the lenses of the silo's external cameras, offering the inhabitants a glimpse of the destroyed surface. + # hello world! ![img](https://www.gstatic.com/images/branding/googlelogo/svg/googlelogo_clr_74x24px.svg) diff --git a/yarn.lock b/yarn.lock index 72e5b75..6191977 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2742,6 +2742,11 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.0.tgz#d774355e41f372d5350a4d0714abb48194a489c3" integrity sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA== +"@types/lodash@^4.17.6": + version "4.17.6" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.6.tgz#193ced6a40c8006cfc1ca3f4553444fb38f0e543" + integrity sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA== + "@types/markdown-it@^14": version "14.1.1" resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-14.1.1.tgz#06bafb7a4e3f77b62b1f308acf7df76687887e0b" @@ -10295,19 +10300,21 @@ yocto-queue@^1.0.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== -zens@^0.0.26: - version "0.0.26" - resolved "https://registry.yarnpkg.com/zens/-/zens-0.0.26.tgz#b809861a72b52fa8ae50f59216228b400dac4b86" - integrity sha512-FW/TIgBhlddmqDQhU/wXj9/2rjiEWFVpO3prdkoGkTmNnWXf6zCzLgaQIPZdfEyhmc0Y/SNRvdJCm5Z0dBypAQ== +zens@^0.0.32: + version "0.0.32" + resolved "https://registry.yarnpkg.com/zens/-/zens-0.0.32.tgz#8b334e7a3fe78a36eecf3c594e780e41d5b45778" + integrity sha512-DNTuv/AeOnXNLlq4lSgTwPwRaiyVk27/qyVB5XPwZx2ucKAlv+xqJso/CTeYGNQfLfvoR57NqtagB3XKxVTqZg== dependencies: "@ant-design/icons" "^5.3.7" "@ariakit/react" "^0.4.5" "@babel/runtime" "^7" "@emotion/is-prop-valid" "^1.2.2" "@floating-ui/dom" "^1.0.0" + "@types/lodash" "^4.17.6" classnames "^2.3.0" clsx "^2.0.0" color "^4.2.3" + lodash "^4.17.21" rc-image "^7.5.1" react "^18.2.0" react-dom "^18.2.0"