From 9f31987960886a59a902e93bf86c363c53197346 Mon Sep 17 00:00:00 2001 From: lightbulbman Date: Fri, 11 Sep 2026 00:34:44 +0100 Subject: [PATCH 01/16] chore: remove the unused @codemirror/fold 0.19 package --- package-lock.json | 80 ----------------------------------------------- package.json | 1 - 2 files changed, 81 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9f98a02e3..ff9dfa29e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "4.0.0-beta.1", "license": "GPL-2.0-or-later", "dependencies": { - "@codemirror/fold": "^0.19.4", "@wordpress/components": "^29.3.0", "@wordpress/date": "^5.43.0", "@wordpress/dom-ready": "^4.17.0", @@ -1769,66 +1768,6 @@ "node": ">=6.9.0" } }, - "node_modules/@codemirror/fold": { - "version": "0.19.4", - "license": "MIT", - "dependencies": { - "@codemirror/gutter": "^0.19.0", - "@codemirror/language": "^0.19.0", - "@codemirror/rangeset": "^0.19.0", - "@codemirror/state": "^0.19.0", - "@codemirror/view": "^0.19.22" - } - }, - "node_modules/@codemirror/gutter": { - "version": "0.19.9", - "license": "MIT", - "dependencies": { - "@codemirror/rangeset": "^0.19.0", - "@codemirror/state": "^0.19.0", - "@codemirror/view": "^0.19.23" - } - }, - "node_modules/@codemirror/language": { - "version": "0.19.10", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^0.19.0", - "@codemirror/text": "^0.19.0", - "@codemirror/view": "^0.19.0", - "@lezer/common": "^0.15.5", - "@lezer/lr": "^0.15.0" - } - }, - "node_modules/@codemirror/rangeset": { - "version": "0.19.9", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^0.19.0" - } - }, - "node_modules/@codemirror/state": { - "version": "0.19.9", - "license": "MIT", - "dependencies": { - "@codemirror/text": "^0.19.0" - } - }, - "node_modules/@codemirror/text": { - "version": "0.19.6", - "license": "MIT" - }, - "node_modules/@codemirror/view": { - "version": "0.19.48", - "license": "MIT", - "dependencies": { - "@codemirror/rangeset": "^0.19.5", - "@codemirror/state": "^0.19.3", - "@codemirror/text": "^0.19.0", - "style-mod": "^4.0.0", - "w3c-keyname": "^2.2.4" - } - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "dev": true, @@ -2531,17 +2470,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@lezer/common": { - "version": "0.15.12", - "license": "MIT" - }, - "node_modules/@lezer/lr": { - "version": "0.15.8", - "license": "MIT", - "dependencies": { - "@lezer/common": "^0.15.0" - } - }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -12599,10 +12527,6 @@ "webpack": "^5.27.0" } }, - "node_modules/style-mod": { - "version": "4.1.0", - "license": "MIT" - }, "node_modules/style-search": { "version": "0.1.0", "dev": true, @@ -13740,10 +13664,6 @@ "dev": true, "license": "MIT" }, - "node_modules/w3c-keyname": { - "version": "2.2.8", - "license": "MIT" - }, "node_modules/watchpack": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", diff --git a/package.json b/package.json index 4d395c85b..207953157 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "url": "https://github.com/codesnippetspro/code-snippets/issues" }, "dependencies": { - "@codemirror/fold": "^0.19.4", "@wordpress/components": "^29.3.0", "@wordpress/date": "^5.43.0", "@wordpress/dom-ready": "^4.17.0", From 7a9e284111553dcd98602a3d0241cfd916a6d728 Mon Sep 17 00:00:00 2001 From: lightbulbman Date: Fri, 11 Sep 2026 01:03:54 +0100 Subject: [PATCH 02/16] feat: replace the CodeMirror 5 code editor with CodeMirror 6 --- config/webpack/modules/codemirror-emacs.d.ts | 7 + .../webpack/modules/codemirror-keymaps.d.ts | 5 - config/webpack/postcss-codemirror-theme.ts | 95 ++++++ config/webpack/webpack-css.ts | 10 +- config/webpack/webpack-js.ts | 7 +- package-lock.json | 312 ++++++++++++++++-- package.json | 19 +- src/css/common/_codemirror.scss | 166 ++++------ src/css/common/_modal.scss | 4 +- src/css/edit/_editor.scss | 28 +- src/css/manage.scss | 1 + src/css/settings.scss | 14 +- .../EditorSidebar/controls/RTLControl.tsx | 11 +- .../SnippetForm/WithSnippetFormContext.tsx | 17 +- .../SnippetForm/fields/CodeEditor.tsx | 212 ++++++------ .../fields/CodeEditorShortcuts.tsx | 145 +++----- .../SnippetForm/fields/SnippetTypeInput.tsx | 30 +- .../common/snippets/SnippetPreviewModal.tsx | 116 ++++--- src/js/editor/keymaps.ts | 81 +++++ src/js/editor/languages.ts | 79 +++++ src/js/editor/phrases.ts | 43 +++ src/js/editor/snippetEditor.ts | 264 +++++++++++++++ src/js/editor/theme.ts | 144 ++++++++ src/js/entries/editor.ts | 24 +- src/js/services/settings/editor-preview.ts | 66 ++-- src/js/services/settings/tabs.ts | 9 - src/js/types/EditorSettings.ts | 33 ++ src/js/types/Window.ts | 7 +- src/js/types/vendor/WordPressCodeEditor.ts | 28 -- src/js/utils/Linter.ts | 25 +- src/php/Admin/Menus/Edit_Menu.php | 29 +- .../Admin/Menus/Manage/Manage_Menu_Assets.php | 5 +- src/php/Admin/Menus/Settings_Menu.php | 24 +- src/php/Utils/editor.php | 154 ++++----- tests/e2e/code-snippets-paste-tags.spec.ts | 37 +-- tests/e2e/code-snippets-preview.spec.ts | 33 +- tests/e2e/editor-labels.spec.ts | 14 +- tests/e2e/helpers/SnippetsTestHelper.ts | 46 +-- tests/e2e/helpers/codeEditor.ts | 38 +++ tests/e2e/helpers/constants.ts | 1 - tests/e2e/rtl-layout.spec.ts | 2 +- .../Menus/Manage/Manage_Menu_Assets_Test.php | 39 +-- tests/unit/Utils/Editor_Test.php | 170 ++++++++++ 43 files changed, 1828 insertions(+), 766 deletions(-) create mode 100644 config/webpack/modules/codemirror-emacs.d.ts delete mode 100644 config/webpack/modules/codemirror-keymaps.d.ts create mode 100644 config/webpack/postcss-codemirror-theme.ts create mode 100644 src/js/editor/keymaps.ts create mode 100644 src/js/editor/languages.ts create mode 100644 src/js/editor/phrases.ts create mode 100644 src/js/editor/snippetEditor.ts create mode 100644 src/js/editor/theme.ts create mode 100644 src/js/types/EditorSettings.ts delete mode 100644 src/js/types/vendor/WordPressCodeEditor.ts create mode 100644 tests/e2e/helpers/codeEditor.ts create mode 100644 tests/unit/Utils/Editor_Test.php diff --git a/config/webpack/modules/codemirror-emacs.d.ts b/config/webpack/modules/codemirror-emacs.d.ts new file mode 100644 index 000000000..e4c630d5d --- /dev/null +++ b/config/webpack/modules/codemirror-emacs.d.ts @@ -0,0 +1,7 @@ +// The package's export map has no types condition, so its declarations are +// not found when it is resolved as CommonJS. +declare module '@replit/codemirror-emacs' { + import type { Extension } from '@codemirror/state' + + export const emacs: () => Extension +} diff --git a/config/webpack/modules/codemirror-keymaps.d.ts b/config/webpack/modules/codemirror-keymaps.d.ts deleted file mode 100644 index 9f5e587c1..000000000 --- a/config/webpack/modules/codemirror-keymaps.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module 'codemirror/src/input/keymap' { - import type { KeyMap } from 'codemirror' - - export const getKeyMap: (keyMap: string) => KeyMap -} diff --git a/config/webpack/postcss-codemirror-theme.ts b/config/webpack/postcss-codemirror-theme.ts new file mode 100644 index 000000000..08f9b39e9 --- /dev/null +++ b/config/webpack/postcss-codemirror-theme.ts @@ -0,0 +1,95 @@ +import type { PluginCreator, Rule } from 'postcss' + +/** + * Selectors for CodeMirror 5 editor structure, and their CodeMirror 6 + * equivalents. Token classes such as `.cm-keyword` need no mapping, as the + * editor's highlight style emits them unchanged. + */ +const SELECTOR_MAP: readonly (readonly [RegExp, string])[] = [ + [/\.CodeMirror-activeline-background(?![\w-])/g, '.cm-activeLine'], + [/\.CodeMirror-activeline-gutter(?![\w-])/g, '.cm-activeLineGutter'], + [/\.CodeMirror-gutters(?![\w-])/g, '.cm-gutters'], + [/\.CodeMirror-gutter-elt(?![\w-])/g, '.cm-gutterElement'], + [/\.CodeMirror-gutter(?![\w-])/g, '.cm-gutter'], + [/\.CodeMirror-linenumber(?![\w-])/g, '.cm-lineNumbers .cm-gutterElement'], + [/\.CodeMirror-guttermarker(?:-subtle)?(?![\w-])/g, '.cm-foldGutter .cm-gutterElement'], + [/\.CodeMirror-foldmarker(?![\w-])/g, '.cm-foldPlaceholder'], + [/\.CodeMirror-cursor(?![\w-])/g, '.cm-cursor'], + [/\.CodeMirror-selected(?![\w-])/g, '.cm-selectionBackground'], + [/\.CodeMirror-matchingbracket(?![\w-])/g, '.cm-matchingBracket'], + [/\.CodeMirror-nonmatchingbracket(?![\w-])/g, '.cm-nonmatchingBracket'], + [/\.CodeMirror-focused(?![\w-])/g, '.cm-focused'], + [/\.CodeMirror-line(?![\w-])/g, '.cm-line'], + [/\.CodeMirror-(?:code|lines)(?![\w-])/g, '.cm-content'], + [/\.CodeMirror-scroll(?![\w-])/g, '.cm-scroller'], + [/\.CodeMirror(?![\w-])/g, '.cm-editor'], + [/\.cm-searching(?![\w-])/g, '.cm-searchMatch'], + [/\.cm-matchhighlight(?![\w-])/g, '.cm-selectionMatch'] +] + +/** + * Selectors with no CodeMirror 6 counterpart: native selection is hidden in + * favour of drawn selection, and the rest style addons that are not loaded. + */ +const UNSUPPORTED_ADDON_CLASSES = [ + 'simplescroll', 'overlayscroll', 'hint', 'ruler', 'widget', 'secondarycursor', 'overwrite', 'cursors', + 'matchingtag', 'selectedtext', 'foldgutter', 'gutter-text', 'activeline(?![\\w-])' +] + +const UNSUPPORTED_SELECTOR = new RegExp(`::?-?(?:moz-)?selection|\\.CodeMirror-(?:${UNSUPPORTED_ADDON_CLASSES.join('|')})`) + +const STRUCTURAL_CLASSES = [ + 'editor', 'gutters?', 'gutterElement', 'lineNumbers', 'foldGutter', 'foldPlaceholder', 'cursor', + 'selectionBackground', 'matchingBracket', 'nonmatchingBracket', 'focused', 'line', 'content', 'scroller', + 'activeLine', 'activeLineGutter', 'searchMatch', 'selectionMatch' +] + +const STRUCTURAL_SELECTOR = new RegExp(`\\.cm-(?:${STRUCTURAL_CLASSES.join('|')})(?![\\w-])`) + +const convertSelector = (selector: string): string | null => { + if (UNSUPPORTED_SELECTOR.test(selector)) { + return null + } + + const converted = SELECTOR_MAP.reduce((result, [pattern, replacement]) => + result.replace(pattern, replacement), selector) + + return converted.includes('.CodeMirror') ? null : converted +} + +/** + * Convert a CodeMirror 5 theme stylesheet to style a CodeMirror 6 editor. + * + * Declarations on editor structure are made important: CodeMirror 6 injects + * its base theme after the page's stylesheets, so it would otherwise win any + * rule of equal specificity. + */ +const postcssCodeMirrorTheme: PluginCreator = () => ({ + postcssPlugin: 'postcss-codemirror-theme', + Rule(rule: Rule) { + if ('atrule' === rule.parent?.type && 'keyframes' === (<{ name?: string }> rule.parent).name) { + return + } + + const selectors = rule.selectors + .map(convertSelector) + .filter((selector): selector is string => null !== selector) + + if (0 === selectors.length) { + rule.remove() + return + } + + rule.selectors = selectors + + if (selectors.some(selector => STRUCTURAL_SELECTOR.test(selector))) { + rule.walkDecls(declaration => { + declaration.important = true + }) + } + } +}) + +postcssCodeMirrorTheme.postcss = true + +export default postcssCodeMirrorTheme diff --git a/config/webpack/webpack-css.ts b/config/webpack/webpack-css.ts index 1e4f94e54..7f4ad39ac 100644 --- a/config/webpack/webpack-css.ts +++ b/config/webpack/webpack-css.ts @@ -6,6 +6,7 @@ import rgbaCompat from 'postcss-hexrgba' import MiniCssExtractPlugin from 'mini-css-extract-plugin' import RemoveEmptyScriptsPlugin from 'webpack-remove-empty-scripts' import { glob } from 'glob' +import codeMirrorTheme from './postcss-codemirror-theme' import hslCompat from './postcss-hsl-legacy' import type { Configuration, EntryObject } from 'webpack' import type { Config as PostCssConfig } from 'postcss-load-config' @@ -21,6 +22,8 @@ const postcssOptions: PostCssConfig = { ] } +const EDITOR_THEME_FILES = 'node_modules/codemirror/theme/*.css' + const entriesFromFiles = (patterns: string | string[], entry: (filename: string) => string): EntryObject => Object.fromEntries( glob.sync(patterns) @@ -34,9 +37,10 @@ export const cssWebpackConfig: Configuration = { filename => `${path.parse(filename).name}-css` ), ...entriesFromFiles( - 'node_modules/codemirror/theme/*.css', + EDITOR_THEME_FILES, filename => `codemirror-theme-${path.parse(filename).name}` - ) + ), + 'editor-themes-css': glob.sync(EDITOR_THEME_FILES).sort().map(filename => `./${filename}`) }, module: { rules: [ @@ -85,7 +89,7 @@ export const cssWebpackConfig: Configuration = { loader: 'postcss-loader', options: { postcssOptions: { - plugins: [cssnano()] + plugins: [codeMirrorTheme(), cssnano()] } } } diff --git a/config/webpack/webpack-js.ts b/config/webpack/webpack-js.ts index 62981acea..3c00ed347 100644 --- a/config/webpack/webpack-js.ts +++ b/config/webpack/webpack-js.ts @@ -40,6 +40,7 @@ export const jsWebpackConfig: Configuration = { output: { path: join(resolve(__dirname), '..', '..', DEST_DIR), filename: '[name].js', + chunkFilename: 'chunks/[name].[contenthash:8].js', clean: true }, externalsType: 'window', @@ -50,7 +51,6 @@ export const jsWebpackConfig: Configuration = { 'react/jsx-runtime': 'ReactJSXRuntime', 'jquery': 'jQuery', 'tinymce': 'tinymce', - 'codemirror': ['wp', 'CodeMirror'], ...Object.fromEntries( Object.keys(dependencies) .filter(name => name.startsWith('@wordpress/')) @@ -69,6 +69,11 @@ export const jsWebpackConfig: Configuration = { { test: /\.[jt]sx?$/, exclude: /node_modules/, + // Dynamic imports are resolved as ECMAScript modules by TypeScript, which + // requires them to name a file extension. + resolve: { + extensionAlias: { '.js': ['.ts', '.tsx', '.js'] } + }, use: { loader: 'babel-loader', options: babelConfig diff --git a/package-lock.json b/package-lock.json index ff9dfa29e..a716682ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,15 +9,30 @@ "version": "4.0.0-beta.1", "license": "GPL-2.0-or-later", "dependencies": { + "@codemirror/autocomplete": "^6.20.3", + "@codemirror/commands": "^6.11.0", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.12", + "@codemirror/lang-javascript": "^6.2.5", + "@codemirror/lang-php": "^6.0.2", + "@codemirror/language": "^6.12.4", + "@codemirror/lint": "^6.9.7", + "@codemirror/search": "^6.7.2", + "@codemirror/state": "^6.7.4", + "@codemirror/view": "^6.43.11", + "@lezer/common": "^1.5.2", + "@lezer/highlight": "^1.2.3", + "@replit/codemirror-emacs": "^6.1.0", + "@replit/codemirror-vim": "^6.4.0", "@wordpress/components": "^29.3.0", "@wordpress/date": "^5.43.0", "@wordpress/dom-ready": "^4.17.0", "@wordpress/element": "^6.28.0", + "@wordpress/hooks": "^4.55.0", "@wordpress/i18n": "^5.17.0", "@wordpress/url": "^4.20.0", "axios": "^1.13.5", "classnames": "^2.5.1", - "codemirror": "^5.29", "php-parser": "^3.2.2", "prismjs": "^1.29.0", "react": "^18.3.1", @@ -33,7 +48,6 @@ "@stylistic/stylelint-plugin": "^3.1.2", "@tsconfig/node18": "^18.2.4", "@types/archiver": "^6.0.3", - "@types/codemirror": "^5.60.15", "@types/jquery": "^3.5.32", "@types/node": "^22.13.1", "@types/prismjs": "^1.26.5", @@ -49,6 +63,7 @@ "autoprefixer": "^10.4.20", "babel-loader": "^9.2.1", "babel-plugin-prismjs": "^2.1.0", + "codemirror": "^5.65.21", "css-loader": "^7.1.2", "cssnano": "^7.0.6", "eslint": "^9.20.1", @@ -1768,6 +1783,145 @@ "node": ">=6.9.0" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.20.3", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz", + "integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.11.0.tgz", + "integrity": "sha512-/K4Rl5BN0OtTiPWmJCdqODu38XnDMsDxKY5rgrPnCkutPTJf2wVbkoixLfealF5Kwse/s8P8M5jAiURiwSwnFA==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.7.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-css": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", + "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.1.7" + } + }, + "node_modules/@codemirror/lang-html": { + "version": "6.4.12", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.12.tgz", + "integrity": "sha512-pw2ReWKUqSkbvh76RAT4NYxiogRu+PWkR2ukAwO9uOgrm8uipkzjtKKtNpyeAQwHOqxEeSvAXZ6vr3AfyB9y/w==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.12" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz", + "integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-php": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-php/-/lang-php-6.0.2.tgz", + "integrity": "sha512-ZKy2v1n8Fc8oEXj0Th0PUMXzQJ0AIR6TaZU+PbDHExFwdu+guzOA4jmCHS1Nz4vbFezwD7LyBdDnddSJeScMCA==", + "license": "MIT", + "dependencies": { + "@codemirror/lang-html": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/php": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.4.tgz", + "integrity": "sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.7.tgz", + "integrity": "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.42.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.2.tgz", + "integrity": "sha512-gUYkYhT2+n/+VGZ+8EzE5WFkYZUZYm1VOKDudIsNqh42uRVQJ0a6Yss9sdKT3MeOYfuL1N6AZA57oza0Oyr0LA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.37.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.4.tgz", + "integrity": "sha512-QhQIVRY+xHZDxwOSFrJ1eUMapJBUID3IdeAjf7dHO7zBUzSkyooHiodnalz5MG3iHzwixKMlAAyn7244y537EA==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.43.11", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.11.tgz", + "integrity": "sha512-2+esucbQX6wB2JYi1eDvdCPFTA31BN8oSy6xCmk3G6CloV11yOvEjYk+gH7kLrP0MuHG94E8WDhjs5oMiu3+Wg==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.7.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "dev": true, @@ -2470,6 +2624,80 @@ "dev": true, "license": "MIT" }, + "node_modules/@lezer/common": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", + "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==", + "license": "MIT" + }, + "node_modules/@lezer/css": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.6.tgz", + "integrity": "sha512-YJE78Wcg+zX8f10hiHWQ4Az48Qr/c13eId0VtRQYLBpxHDmDeSrXIlkbl+fJGW42rWC/uoUco9mhBZeVWP/A1g==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/html": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz", + "integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", + "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/php": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.6.tgz", + "integrity": "sha512-QJ2xNXPmsm/Z3IpThq8fnJrEIVpxhsIoj6GZBW0JYEd/l9zxpJZW216X4fzqQY4vgpdGIumypvI4uQjd4tnYtw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.1.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.4.tgz", + "integrity": "sha512-Wy0V7+SGUjnF9/TkiM1hKVDPj7jKXduPNboMVtHTA8dySMURWqfg/JZ9E2Sq8JgSJmkl7k7Qe9FLeMSrSraWmQ==", + "license": "MIT" + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -2854,6 +3082,41 @@ "node": ">=18" } }, + "node_modules/@replit/codemirror-emacs": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@replit/codemirror-emacs/-/codemirror-emacs-6.1.0.tgz", + "integrity": "sha512-74DITnht6Cs6sHg02PQ169IKb1XgtyhI9sLD0JeOFco6Ds18PT+dkD8+DgXBDokne9UIFKsBbKPnpFRAz60/Lw==", + "license": "MIT", + "peerDependencies": { + "@codemirror/autocomplete": "^6.0.2", + "@codemirror/commands": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.1", + "@codemirror/view": "^6.3.0" + } + }, + "node_modules/@replit/codemirror-vim": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@replit/codemirror-vim/-/codemirror-vim-6.4.0.tgz", + "integrity": "sha512-t9UMDNhkmeAkl0uRbiJVotv97bGD6mf4GyJJoEbrjUqa/Pov0s9eL+4AaI0Xto3OGri17i9qwIpT0JbVlVXt8A==", + "license": "MIT", + "dependencies": { + "@replit/codemirror-vim-core": "^0.1.0" + }, + "peerDependencies": { + "@codemirror/commands": "6.x.x", + "@codemirror/language": "6.x.x", + "@codemirror/search": "6.x.x", + "@codemirror/state": "6.x.x", + "@codemirror/view": "6.x.x" + } + }, + "node_modules/@replit/codemirror-vim-core": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@replit/codemirror-vim-core/-/codemirror-vim-core-0.1.0.tgz", + "integrity": "sha512-1i6EBKpcNfDKvTmTh6N6g9lL6udD5t+uFNh4JCqozRnVlvUGOps7h/QzS2ne4zcvPUjvApKpmcP7Grc3fNbZiQ==", + "license": "MIT" + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "dev": true, @@ -3050,14 +3313,6 @@ "@types/responselike": "^1.0.0" } }, - "node_modules/@types/codemirror": { - "version": "5.60.15", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/tern": "*" - } - }, "node_modules/@types/eslint": { "version": "8.56.11", "dev": true, @@ -3215,14 +3470,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/tern": { - "version": "0.23.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, "node_modules/@types/tinymce": { "version": "4.6.9", "dev": true, @@ -4424,9 +4671,9 @@ } }, "node_modules/@wordpress/hooks": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.43.0.tgz", - "integrity": "sha512-BY7GPjEwhOlgkavVak40E3RtA8Z9ehydqTZckRoesMRjXYfxKSzr1C1FT4wAPS5uXM1pNlWivfofMaJjVNQu5w==", + "version": "4.55.0", + "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.55.0.tgz", + "integrity": "sha512-oI5NkudaBOewGR/qxPKx7us4h/D+Iokr1UPJHdh3pSnwhUgXFS1ptY7O98Hl6HpUZMyTwdP60fZJ5hlgSyivVA==", "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", @@ -5990,7 +6237,10 @@ } }, "node_modules/codemirror": { - "version": "5.65.15", + "version": "5.65.21", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.21.tgz", + "integrity": "sha512-6teYk0bA0nR3QP0ihGMoxuKzpl5W80FpnHpBJpgy66NK3cZv5b/d/HY8PnRvfSsCG1MTfr92u2WUl+wT0E40mQ==", + "dev": true, "license": "MIT" }, "node_modules/color-convert": { @@ -6218,6 +6468,12 @@ "dev": true, "license": "MIT" }, + "node_modules/crelt": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz", + "integrity": "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "dev": true, @@ -12527,6 +12783,12 @@ "webpack": "^5.27.0" } }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "license": "MIT" + }, "node_modules/style-search": { "version": "0.1.0", "dev": true, @@ -13664,6 +13926,12 @@ "dev": true, "license": "MIT" }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, "node_modules/watchpack": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", diff --git a/package.json b/package.json index 207953157..cdebd9cc8 100644 --- a/package.json +++ b/package.json @@ -49,15 +49,30 @@ "url": "https://github.com/codesnippetspro/code-snippets/issues" }, "dependencies": { + "@codemirror/autocomplete": "^6.20.3", + "@codemirror/commands": "^6.11.0", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.12", + "@codemirror/lang-javascript": "^6.2.5", + "@codemirror/lang-php": "^6.0.2", + "@codemirror/language": "^6.12.4", + "@codemirror/lint": "^6.9.7", + "@codemirror/search": "^6.7.2", + "@codemirror/state": "^6.7.4", + "@codemirror/view": "^6.43.11", + "@lezer/common": "^1.5.2", + "@lezer/highlight": "^1.2.3", + "@replit/codemirror-emacs": "^6.1.0", + "@replit/codemirror-vim": "^6.4.0", "@wordpress/components": "^29.3.0", "@wordpress/date": "^5.43.0", "@wordpress/dom-ready": "^4.17.0", "@wordpress/element": "^6.28.0", + "@wordpress/hooks": "^4.55.0", "@wordpress/i18n": "^5.17.0", "@wordpress/url": "^4.20.0", "axios": "^1.13.5", "classnames": "^2.5.1", - "codemirror": "^5.29", "php-parser": "^3.2.2", "prismjs": "^1.29.0", "react": "^18.3.1", @@ -73,7 +88,6 @@ "@stylistic/stylelint-plugin": "^3.1.2", "@tsconfig/node18": "^18.2.4", "@types/archiver": "^6.0.3", - "@types/codemirror": "^5.60.15", "@types/jquery": "^3.5.32", "@types/node": "^22.13.1", "@types/prismjs": "^1.26.5", @@ -89,6 +103,7 @@ "autoprefixer": "^10.4.20", "babel-loader": "^9.2.1", "babel-plugin-prismjs": "^2.1.0", + "codemirror": "^5.65.21", "css-loader": "^7.1.2", "cssnano": "^7.0.6", "eslint": "^9.20.1", diff --git a/src/css/common/_codemirror.scss b/src/css/common/_codemirror.scss index 2944798a1..451132a9c 100644 --- a/src/css/common/_codemirror.scss +++ b/src/css/common/_codemirror.scss @@ -1,113 +1,89 @@ -.CodeMirror, .snippet-condition-editor-container { - border: 1px solid #c3c4c7; +// CodeMirror injects its base theme after the page's stylesheets, so rules +// here are qualified with the plugin's own editor class to take precedence. +.cs-code-editor.cm-editor { + border: 1px solid var(--cs-color-border); border-radius: 5px; - block-size: auto !important; background-color: #fff; -} - -.CodeMirror-code { - outline: none; -} + color: #000; -.CodeMirror-focused .cm-matchhighlight { - color: white !important; - outline: 1px solid green; -} - -/* Add a bit of extra space above the editor */ -.CodeMirror-sizer { - &::before, &::after { - display: block; - color: #bbb; + &.cm-focused { + outline: 2px solid var(--cs-color-accent); + outline-offset: -1px; } - &::before { - content: ''; - padding-block-end: 5px; + .cm-scroller { + font-family: monospace; + line-height: 1.5; } -} - -/* Fix cursor color with rubyblue theme (see https://goo.gl/3HDgRm */ -.cm-s-rubyblue .CodeMirror-cursor { - border-inline-start: 1px solid white !important; -} - -[class*='CodeMirror-lint-marker'], [class*='CodeMirror-lint-message'], .CodeMirror-lint-marker-multiple { - background-image: none; -} - -.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning { - cursor: help; -} - -.CodeMirror-lint-marker-multiple { - position: absolute; - inset-block-start: 0; -} - -[class*='CodeMirror-lint-marker']::before { - font: normal 18px/1 dashicons; - position: relative; - inset-block-start: -2px; -} - -[class*='CodeMirror-lint-message']::before { - font: normal 16px/1 dashicons; - position: absolute; - inset-inline-start: 16px; -} -.CodeMirror-lint-message-error, -.CodeMirror-lint-message-warning { - box-shadow: 0 1px 1px 0 rgb(0 0 0 / 10%); - margin-block: 5px 2px; - padding-block: 3px; - margin-inline: 0; - padding-inline: 28px 12px; -} - -.CodeMirror-lint-message-warning { - background-color: #fff8e5; - border-inline-start: 4px solid #ffb900; -} + .cm-gutters { + border-start-start-radius: 5px; + border-end-start-radius: 5px; + } -.CodeMirror-lint-marker-warning::before, .CodeMirror-lint-message-warning::before { - content: '\f534'; - color: #f6a306; -} + .cm-lineNumbers .cm-gutterElement { + color: #666; + } -.CodeMirror-lint-message-error { - background-color: #fbeaea; - border-inline-start: 4px solid #dc3232; -} + .cm-matchingBracket { + background: rgb(255 150 0 / 30%); + color: inherit; + } -.CodeMirror-lint-marker-error::before, .CodeMirror-lint-message-error::before { - content: '\f153'; - color: #dc3232; -} + .cm-diagnostic-error { + border-inline-start-color: var(--cs-color-error); + } -.CodeMirror-lint-tooltip { - background: none; - border: none; - border-radius: 0; - direction: ltr; -} + .cm-diagnostic-warning { + border-inline-start-color: var(--cs-color-warning); + } -.CodeMirror .CodeMirror-matchingbracket { - background: rgb(255 150 0 / 30%); - color: inherit; -} + .cm-panels { + border-color: var(--cs-color-border); + } -.CodeMirror .CodeMirror-linenumber { - color: #666; + .cm-foldPlaceholder { + font-weight: bold; + } } -.CodeMirror .codemirror-colorview { - border: 0; +.snippet-condition-editor-container { + border: 1px solid #c3c4c7; + border-radius: 5px; + block-size: auto !important; + background-color: #fff; } -.CodeMirror-foldmarker { - color: inherit; - margin-inline: 0.25em; - font-weight: bold; +// Token colours for the default theme. Other themes are CodeMirror 5 theme +// stylesheets, converted at build time, which style these same classes. +.cm-s-default { + .cm-header { color: blue; } + .cm-quote { color: #090; } + .cm-keyword { color: #708; } + .cm-atom { color: #219; } + .cm-number { color: #164; } + .cm-def { color: #00f; } + .cm-variable-2 { color: #05a; } + .cm-variable-3, .cm-type { color: #085; } + .cm-comment { color: #a50; } + .cm-string { color: #a11; } + .cm-string-2 { color: #f50; } + .cm-meta { color: #555; } + .cm-qualifier { color: #555; } + .cm-builtin { color: #30a; } + .cm-bracket { color: #997; } + .cm-tag { color: #170; } + .cm-attribute { color: #00c; } + .cm-hr { color: #999; } + .cm-link { color: #00c; } + .cm-error { color: #f00; } +} + +.cm-editor { + .cm-negative { color: #d44; } + .cm-positive { color: #292; } + .cm-header, .cm-strong { font-weight: bold; } + .cm-em { font-style: italic; } + .cm-link { text-decoration: underline; } + .cm-strikethrough { text-decoration: line-through; } } diff --git a/src/css/common/_modal.scss b/src/css/common/_modal.scss index 38cb83ba2..0252d3266 100644 --- a/src/css/common/_modal.scss +++ b/src/css/common/_modal.scss @@ -74,7 +74,7 @@ margin-inline-end: 8px; } - // Header and footer stay pinned; the CodeMirror editor is the only + // Header and footer stay pinned; the code editor is the only // scroll region, scrolling long code both vertically and horizontally. .components-modal__content { display: flex; @@ -99,7 +99,7 @@ flex: 1; min-block-size: 0; - .CodeMirror { + .cm-editor { flex: 1; min-block-size: 0; block-size: 100%; diff --git a/src/css/edit/_editor.scss b/src/css/edit/_editor.scss index 2fd9e9a2b..d92cec09c 100644 --- a/src/css/edit/_editor.scss +++ b/src/css/edit/_editor.scss @@ -1,26 +1,26 @@ -.CodeMirror, .snippet-form textarea { inline-size: 100%; font-family: monospace; } -.CodeMirror-sizer { - min-block-size: 300px !important; - box-sizing: border-box; - padding-block-end: 1.5em !important; +.snippet-editor .cm-content, +.snippet-editor .cm-gutter { + min-block-size: 300px; +} - &::after { - position: absolute; - inset-block-end: 0; +// The markup a snippet is wrapped in when it runs, shown around the code. +.snippet-editor .cm-editor { + &::before, &::after { + display: block; + padding-block: 4px; + padding-inline: 8px; + color: #bbb; + font-family: monospace; } - .snippet-form.php-snippet & { - padding-block-end: 0 !important; - - &::before { - content: ' { - const { codeEditorInstance } = useSnippetForm() + const { editorView } = useSnippetForm() const directionId = useId() return ( @@ -12,9 +13,11 @@ export const RTLControl: React.FC = () => { {__('Code Direction', 'code-snippets')} - { + if (editorView) { + setEditorDirection(editorView, 'rtl' === event.target.value ? 'rtl' : 'ltr') + } + }}> diff --git a/src/js/components/EditMenu/SnippetForm/WithSnippetFormContext.tsx b/src/js/components/EditMenu/SnippetForm/WithSnippetFormContext.tsx index a006df03f..998d605b7 100644 --- a/src/js/components/EditMenu/SnippetForm/WithSnippetFormContext.tsx +++ b/src/js/components/EditMenu/SnippetForm/WithSnippetFormContext.tsx @@ -3,10 +3,11 @@ import React, { useCallback, useMemo, useState } from 'react' import { createContextHook } from '../../../utils/bootstrap' import { isLicensed } from '../../../utils/screen' import { isProSnippet } from '../../../utils/snippets/snippets' +import { replaceEditorContent } from '../../../editor/snippetEditor' +import type { EditorView } from '@codemirror/view' import type { Dispatch, PropsWithChildren, SetStateAction } from 'react' import type { ScreenNotice } from '../../../types/ScreenNotice' import type { Snippet } from '../../../types/Snippet' -import type { CodeEditorInstance } from '../../../types/vendor/WordPressCodeEditor' export interface SnippetFormContext { snippet: Snippet @@ -19,9 +20,9 @@ export interface SnippetFormContext { setIsWorking: Dispatch> currentNotice: ScreenNotice | undefined setCurrentNotice: Dispatch> - codeEditorInstance: CodeEditorInstance | undefined + editorView: EditorView | undefined handleRequestError: (error: unknown, message?: string) => void - setCodeEditorInstance: Dispatch> + setEditorView: Dispatch> } const [Context, useSnippetForm] = createContextHook('useSnippetForm') @@ -56,7 +57,7 @@ export const WithSnippetFormContext: React.FC = ({ const [savedSnippet, setSavedSnippet] = useState(initialValue) const [isWorking, setIsWorking] = useState(false) const [currentNotice, setCurrentNotice] = useState() - const [codeEditorInstance, setCodeEditorInstance] = useState() + const [editorView, setEditorView] = useState() const isReadOnly = useMemo( () => snippet.locked || !isLicensed() && isProSnippet({ scope: snippet.scope }), @@ -80,11 +81,11 @@ export const WithSnippetFormContext: React.FC = ({ const updateSnippet: Dispatch> = useCallback((value: SetStateAction) => { setSnippet(previous => { const updated = 'object' === typeof value ? value : value(previous) - codeEditorInstance?.codemirror.setValue(updated.code) + replaceEditorContent(editorView, updated.code) window.tinymce?.activeEditor.setContent(updated.desc) return updated }) - }, [codeEditorInstance?.codemirror]) + }, [editorView]) const value: SnippetFormContext = { snippet, @@ -97,9 +98,9 @@ export const WithSnippetFormContext: React.FC = ({ updateSnippet, currentNotice, setCurrentNotice, - codeEditorInstance, + editorView, handleRequestError, - setCodeEditorInstance + setEditorView } return {children} diff --git a/src/js/components/EditMenu/SnippetForm/fields/CodeEditor.tsx b/src/js/components/EditMenu/SnippetForm/fields/CodeEditor.tsx index 32a67b904..bd73de355 100644 --- a/src/js/components/EditMenu/SnippetForm/fields/CodeEditor.tsx +++ b/src/js/components/EditMenu/SnippetForm/fields/CodeEditor.tsx @@ -1,8 +1,9 @@ import React, { useEffect, useId, useRef } from 'react' import { __, sprintf } from '@wordpress/i18n' +import { getEditorPhrases } from '../../../../editor/phrases' +import { createSnippetEditor, replaceEditorContent, setEditorSnippetType } from '../../../../editor/snippetEditor' import { useSubmitSnippet } from '../../../../hooks/useSubmitSnippet' import { handleUnknownError } from '../../../../utils/errors' -import { isMacOS } from '../../../../utils/screen' import { getSnippetType } from '../../../../utils/snippets/snippets' import { stripWrapperTags } from '../../../../utils/snippets/tags' import { useSnippetForm } from '../WithSnippetFormContext' @@ -10,129 +11,154 @@ import { Button } from '../../../common/Button' import { ExpandIcon } from '../../../common/icons/ExpandIcon' import { MinimiseIcon } from '../../../common/icons/MinimiseIcon' import { CodeEditorShortcuts } from './CodeEditorShortcuts' -import type { Dispatch, RefObject, SetStateAction } from 'react' -import type { ScreenNotice } from '../../../../types/ScreenNotice' -import type { Snippet } from '../../../../types/Snippet' +import type { ViewUpdate } from '@codemirror/view' +import type { Dispatch, SetStateAction } from 'react' +import type { SnippetCodeType, SnippetType } from '../../../../types/Snippet' -interface EditorTextareaProps { - textareaRef: RefObject +const toCodeType = (type: SnippetType): SnippetCodeType => + 'cond' === type ? 'php' : type + +interface EditorAreaProps { snippetCodeId: string } -const EditorTextarea: React.FC = ({ textareaRef, snippetCodeId }) => { - const descriptionId = useId() +const PlainTextEditor: React.FC = ({ snippetCodeId }) => { const { snippet, setSnippet } = useSnippetForm() return ( -
-

- {__('In the editing area, the Tab key enters a tab character. To exit the code editor, press the Escape key and then the Tab key.', 'code-snippets')} -

-