11import React , { useMemo , useRef } from "react" ;
22import { defaultKeymap , indentWithTab } from "@codemirror/commands" ;
33import { foldKeymap } from "@codemirror/language" ;
4- import { EditorState , Extension } from "@codemirror/state" ;
4+ import { EditorState , Extension , Compartment } from "@codemirror/state" ;
55import { DOMEventHandlers , EditorView , KeyBinding , keymap , Rect , ViewUpdate } from "@codemirror/view" ;
66import { minimalSetup } from "codemirror" ;
77
@@ -30,7 +30,7 @@ import {
3030 adaptedHighlightSpecialChars ,
3131 adaptedLineNumbers ,
3232 adaptedLintGutter ,
33- adaptedPlaceholder ,
33+ adaptedPlaceholder , compartment ,
3434} from "./tests/codemirrorTestHelper" ;
3535import { ExtensionCreator } from "./types" ;
3636
@@ -221,7 +221,24 @@ export const CodeEditor = ({
221221} : CodeEditorProps ) => {
222222 const parent = useRef < any > ( undefined ) ;
223223 const [ view , setView ] = React . useState < EditorView | undefined > ( ) ;
224+ const currentView = React . useRef < EditorView > ( )
225+ currentView . current = view
226+ const currentReadOnly = React . useRef ( readOnly )
227+ currentReadOnly . current = readOnly
224228 const [ showPreview , setShowPreview ] = React . useState < boolean > ( false ) ;
229+ // CodeMirror Compartments in order to allow for re-configuration after initialization
230+ const readOnlyCompartment = React . useRef < Compartment > ( compartment ( ) )
231+ const wrapLinesCompartment = React . useRef < Compartment > ( compartment ( ) )
232+ const preventLineNumbersCompartment = React . useRef < Compartment > ( compartment ( ) )
233+ const shouldHaveMinimalSetupCompartment = React . useRef < Compartment > ( compartment ( ) )
234+ const placeholderCompartment = React . useRef < Compartment > ( compartment ( ) )
235+ const modeCompartment = React . useRef < Compartment > ( compartment ( ) )
236+ const keyMapConfigsCompartment = React . useRef < Compartment > ( compartment ( ) )
237+ const tabIntentSizeCompartment = React . useRef < Compartment > ( compartment ( ) )
238+ const disabledCompartment = React . useRef < Compartment > ( compartment ( ) )
239+ const supportCodeFoldingCompartment = React . useRef < Compartment > ( compartment ( ) )
240+ const useLintingCompartment = React . useRef < Compartment > ( compartment ( ) )
241+ const shouldHighlightActiveLineCompartment = React . useRef < Compartment > ( compartment ( ) )
225242
226243 const linters = useMemo ( ( ) => {
227244 if ( ! mode ) {
@@ -240,7 +257,7 @@ export const CodeEditor = ({
240257
241258 const onKeyDownHandler = ( event : KeyboardEvent , view : EditorView ) => {
242259 if ( onKeyDown && ! onKeyDown ( event ) ) {
243- if ( event . key === "Enter" ) {
260+ if ( event . key === "Enter" && ! currentReadOnly . current ) {
244261 const cursor = view . state . selection . main . head ;
245262 view . dispatch ( {
246263 changes : {
@@ -263,14 +280,17 @@ export const CodeEditor = ({
263280 return false ;
264281 } ;
265282
266- React . useEffect ( ( ) => {
283+ const createKeyMapConfigs = ( ) => {
267284 const tabIndent =
268285 ! ! ( tabIntentStyle === "tab" && mode && ! ( tabForceSpaceForModes ?? [ ] ) . includes ( mode ) ) || enableTab ;
269- const keyMapConfigs = [
286+ return [
270287 defaultKeymap as KeyBinding ,
271288 ...addToKeyMapConfigFor ( supportCodeFolding , ...foldKeymap ) ,
272289 ...addToKeyMapConfigFor ( tabIndent , indentWithTab ) ,
273290 ] ;
291+ }
292+
293+ React . useEffect ( ( ) => {
274294 const domEventHandlers = {
275295 ...addHandlersFor ( ! ! onScroll , "scroll" , onScroll ) ,
276296 ...addHandlersFor (
@@ -284,13 +304,13 @@ export const CodeEditor = ({
284304 } as DOMEventHandlers < any > ;
285305 const extensions = [
286306 markField ,
287- adaptedPlaceholder ( placeholder ) ,
307+ placeholderCompartment . current . of ( adaptedPlaceholder ( placeholder ) ) ,
288308 adaptedHighlightSpecialChars ( ) ,
289- useCodeMirrorModeExtension ( mode ) ,
290- keymap ?. of ( keyMapConfigs ) ,
291- EditorState ?. tabSize . of ( tabIntentSize ) ,
292- EditorState ?. readOnly . of ( readOnly ) ,
293- EditorView ?. editable . of ( ! disabled ) ,
309+ modeCompartment . current . of ( useCodeMirrorModeExtension ( mode ) ) ,
310+ keyMapConfigsCompartment . current . of ( keymap ?. of ( createKeyMapConfigs ( ) ) ) ,
311+ tabIntentSizeCompartment . current . of ( EditorState ?. tabSize . of ( tabIntentSize ) ) ,
312+ readOnlyCompartment . current . of ( EditorState ?. readOnly . of ( readOnly ) ) ,
313+ disabledCompartment . current . of ( EditorView ?. editable . of ( ! disabled ) ) ,
294314 AdaptedEditorViewDomEventHandlers ( domEventHandlers ) as Extension ,
295315 EditorView ?. updateListener . of ( ( v : ViewUpdate ) => {
296316 if ( disabled ) return ;
@@ -326,12 +346,12 @@ export const CodeEditor = ({
326346 }
327347 }
328348 } ) ,
329- addExtensionsFor ( shouldHaveMinimalSetup , minimalSetup ) ,
330- addExtensionsFor ( ! preventLineNumbers , adaptedLineNumbers ( ) ) ,
331- addExtensionsFor ( shouldHighlightActiveLine , adaptedHighlightActiveLine ( ) ) ,
332- addExtensionsFor ( wrapLines , EditorView ?. lineWrapping ) ,
333- addExtensionsFor ( supportCodeFolding , adaptedFoldGutter ( ) , adaptedCodeFolding ( ) ) ,
334- addExtensionsFor ( useLinting , ...linters ) ,
349+ shouldHaveMinimalSetupCompartment . current . of ( addExtensionsFor ( shouldHaveMinimalSetup , minimalSetup ) ) ,
350+ preventLineNumbersCompartment . current . of ( addExtensionsFor ( ! preventLineNumbers , adaptedLineNumbers ( ) ) ) ,
351+ shouldHighlightActiveLineCompartment . current . of ( addExtensionsFor ( shouldHighlightActiveLine , adaptedHighlightActiveLine ( ) ) ) ,
352+ wrapLinesCompartment . current . of ( addExtensionsFor ( wrapLines , EditorView ?. lineWrapping ) ) ,
353+ supportCodeFoldingCompartment . current . of ( addExtensionsFor ( supportCodeFolding , adaptedFoldGutter ( ) , adaptedCodeFolding ( ) ) ) ,
354+ useLintingCompartment . current . of ( addExtensionsFor ( useLinting , ...linters ) ) ,
335355 additionalExtensions ,
336356 ] ;
337357
@@ -373,7 +393,64 @@ export const CodeEditor = ({
373393 setView ( undefined ) ;
374394 }
375395 } ;
376- } , [ parent . current , mode , preventLineNumbers , wrapLines ] ) ;
396+ } , [ parent . current ] ) ;
397+
398+ // Updates an extension for a specific parameter that has changed after the initialization
399+ const updateExtension = ( extension : Extension | undefined , parameterCompartment : Compartment ) : void => {
400+ if ( extension ) {
401+ currentView . current ?. dispatch ( {
402+ effects : parameterCompartment . reconfigure ( extension )
403+ } )
404+ }
405+ }
406+
407+ React . useEffect ( ( ) => {
408+ updateExtension ( EditorState ?. readOnly . of ( readOnly ! ) , readOnlyCompartment . current )
409+ } , [ readOnly ] )
410+
411+ React . useEffect ( ( ) => {
412+ updateExtension ( adaptedPlaceholder ( placeholder ) , placeholderCompartment . current )
413+ } , [ placeholder ] )
414+
415+ React . useEffect ( ( ) => {
416+ updateExtension ( useCodeMirrorModeExtension ( mode ) , modeCompartment . current )
417+ } , [ mode ] )
418+
419+ React . useEffect ( ( ) => {
420+ updateExtension ( keymap ?. of ( createKeyMapConfigs ( ) ) , keyMapConfigsCompartment . current )
421+ } , [ supportCodeFolding , mode , tabIntentStyle , ( tabForceSpaceForModes ?? [ ] ) . join ( ", " ) , enableTab ] )
422+
423+ React . useEffect ( ( ) => {
424+ updateExtension ( EditorState ?. tabSize . of ( tabIntentSize ?? 2 ) , tabIntentSizeCompartment . current )
425+ } , [ tabIntentSize ] )
426+
427+ React . useEffect ( ( ) => {
428+ updateExtension ( EditorView ?. editable . of ( ! disabled ) , disabledCompartment . current )
429+ } , [ disabled ] )
430+
431+ React . useEffect ( ( ) => {
432+ updateExtension ( addExtensionsFor ( shouldHaveMinimalSetup ?? true , minimalSetup ) , shouldHaveMinimalSetupCompartment . current )
433+ } , [ shouldHaveMinimalSetup ] )
434+
435+ React . useEffect ( ( ) => {
436+ updateExtension ( addExtensionsFor ( ! preventLineNumbers , adaptedLineNumbers ( ) ) , preventLineNumbersCompartment . current )
437+ } , [ preventLineNumbers ] )
438+
439+ React . useEffect ( ( ) => {
440+ updateExtension ( addExtensionsFor ( shouldHighlightActiveLine ?? false , adaptedHighlightActiveLine ( ) ) , shouldHighlightActiveLineCompartment . current )
441+ } , [ shouldHighlightActiveLine ] )
442+
443+ React . useEffect ( ( ) => {
444+ updateExtension ( addExtensionsFor ( wrapLines ?? false , EditorView ?. lineWrapping ) , wrapLinesCompartment . current )
445+ } , [ wrapLines ] )
446+
447+ React . useEffect ( ( ) => {
448+ updateExtension ( addExtensionsFor ( supportCodeFolding ?? false , adaptedFoldGutter ( ) , adaptedCodeFolding ( ) ) , supportCodeFoldingCompartment . current )
449+ } , [ supportCodeFolding ] )
450+
451+ React . useEffect ( ( ) => {
452+ updateExtension ( addExtensionsFor ( useLinting ?? false , ...linters ) , useLintingCompartment . current )
453+ } , [ mode , useLinting ] )
377454
378455 const hasToolbarSupport = mode && ModeToolbarSupport . indexOf ( mode ) > - 1 && useToolbar ;
379456
0 commit comments