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 const cursorLine = view . state . doc . lineAt ( cursor ) . number ;
246263 const offsetFromFirstLine = view . state . doc . line ( cursorLine ) . to ;
@@ -265,14 +282,17 @@ export const CodeEditor = ({
265282 return false ;
266283 } ;
267284
268- React . useEffect ( ( ) => {
285+ const createKeyMapConfigs = ( ) => {
269286 const tabIndent =
270287 ! ! ( tabIntentStyle === "tab" && mode && ! ( tabForceSpaceForModes ?? [ ] ) . includes ( mode ) ) || enableTab ;
271- const keyMapConfigs = [
288+ return [
272289 defaultKeymap as KeyBinding ,
273290 ...addToKeyMapConfigFor ( supportCodeFolding , foldKeymap ) ,
274291 ...addToKeyMapConfigFor ( tabIndent , indentWithTab ) ,
275292 ] ;
293+ }
294+
295+ React . useEffect ( ( ) => {
276296 const domEventHandlers = {
277297 ...addHandlersFor ( ! ! onScroll , "scroll" , onScroll ) ,
278298 ...addHandlersFor (
@@ -286,13 +306,13 @@ export const CodeEditor = ({
286306 } as DOMEventHandlers < any > ;
287307 const extensions = [
288308 markField ,
289- adaptedPlaceholder ( placeholder ) ,
309+ placeholderCompartment . current . of ( adaptedPlaceholder ( placeholder ) ) ,
290310 adaptedHighlightSpecialChars ( ) ,
291- useCodeMirrorModeExtension ( mode ) ,
292- keymap ?. of ( keyMapConfigs ) ,
293- EditorState ?. tabSize . of ( tabIntentSize ) ,
294- EditorState ?. readOnly . of ( readOnly ) ,
295- EditorView ?. editable . of ( ! disabled ) ,
311+ modeCompartment . current . of ( useCodeMirrorModeExtension ( mode ) ) ,
312+ keyMapConfigsCompartment . current . of ( keymap ?. of ( createKeyMapConfigs ( ) ) ) ,
313+ tabIntentSizeCompartment . current . of ( EditorState ?. tabSize . of ( tabIntentSize ) ) ,
314+ readOnlyCompartment . current . of ( EditorState ?. readOnly . of ( readOnly ) ) ,
315+ disabledCompartment . current . of ( EditorView ?. editable . of ( ! disabled ) ) ,
296316 AdaptedEditorViewDomEventHandlers ( domEventHandlers ) as Extension ,
297317 EditorView ?. updateListener . of ( ( v : ViewUpdate ) => {
298318 if ( disabled ) return ;
@@ -328,12 +348,12 @@ export const CodeEditor = ({
328348 }
329349 }
330350 } ) ,
331- addExtensionsFor ( shouldHaveMinimalSetup , minimalSetup ) ,
332- addExtensionsFor ( ! preventLineNumbers , adaptedLineNumbers ( ) ) ,
333- addExtensionsFor ( shouldHighlightActiveLine , adaptedHighlightActiveLine ( ) ) ,
334- addExtensionsFor ( wrapLines , EditorView ?. lineWrapping ) ,
335- addExtensionsFor ( supportCodeFolding , adaptedFoldGutter ( ) , adaptedCodeFolding ( ) ) ,
336- addExtensionsFor ( useLinting , ...linters ) ,
351+ shouldHaveMinimalSetupCompartment . current . of ( addExtensionsFor ( shouldHaveMinimalSetup , minimalSetup ) ) ,
352+ preventLineNumbersCompartment . current . of ( addExtensionsFor ( ! preventLineNumbers , adaptedLineNumbers ( ) ) ) ,
353+ shouldHighlightActiveLineCompartment . current . of ( addExtensionsFor ( shouldHighlightActiveLine , adaptedHighlightActiveLine ( ) ) ) ,
354+ wrapLinesCompartment . current . of ( addExtensionsFor ( wrapLines , EditorView ?. lineWrapping ) ) ,
355+ supportCodeFoldingCompartment . current . of ( addExtensionsFor ( supportCodeFolding , adaptedFoldGutter ( ) , adaptedCodeFolding ( ) ) ) ,
356+ useLintingCompartment . current . of ( addExtensionsFor ( useLinting , ...linters ) ) ,
337357 additionalExtensions ,
338358 ] ;
339359
@@ -375,7 +395,64 @@ export const CodeEditor = ({
375395 setView ( undefined ) ;
376396 }
377397 } ;
378- } , [ parent . current , mode , preventLineNumbers , wrapLines ] ) ;
398+ } , [ parent . current ] ) ;
399+
400+ // Updates an extension for a specific parameter that has changed after the initialization
401+ const updateExtension = ( extension : Extension | undefined , parameterCompartment : Compartment ) : void => {
402+ if ( extension ) {
403+ currentView . current ?. dispatch ( {
404+ effects : parameterCompartment . reconfigure ( extension )
405+ } )
406+ }
407+ }
408+
409+ React . useEffect ( ( ) => {
410+ updateExtension ( EditorState ?. readOnly . of ( readOnly ! ) , readOnlyCompartment . current )
411+ } , [ readOnly ] )
412+
413+ React . useEffect ( ( ) => {
414+ updateExtension ( adaptedPlaceholder ( placeholder ) , placeholderCompartment . current )
415+ } , [ placeholder ] )
416+
417+ React . useEffect ( ( ) => {
418+ updateExtension ( useCodeMirrorModeExtension ( mode ) , modeCompartment . current )
419+ } , [ mode ] )
420+
421+ React . useEffect ( ( ) => {
422+ updateExtension ( keymap ?. of ( createKeyMapConfigs ( ) ) , keyMapConfigsCompartment . current )
423+ } , [ supportCodeFolding , mode , tabIntentStyle , ( tabForceSpaceForModes ?? [ ] ) . join ( ", " ) , enableTab ] )
424+
425+ React . useEffect ( ( ) => {
426+ updateExtension ( EditorState ?. tabSize . of ( tabIntentSize ?? 2 ) , tabIntentSizeCompartment . current )
427+ } , [ tabIntentSize ] )
428+
429+ React . useEffect ( ( ) => {
430+ updateExtension ( EditorView ?. editable . of ( ! disabled ) , disabledCompartment . current )
431+ } , [ disabled ] )
432+
433+ React . useEffect ( ( ) => {
434+ updateExtension ( addExtensionsFor ( shouldHaveMinimalSetup ?? true , minimalSetup ) , shouldHaveMinimalSetupCompartment . current )
435+ } , [ shouldHaveMinimalSetup ] )
436+
437+ React . useEffect ( ( ) => {
438+ updateExtension ( addExtensionsFor ( ! preventLineNumbers , adaptedLineNumbers ( ) ) , preventLineNumbersCompartment . current )
439+ } , [ preventLineNumbers ] )
440+
441+ React . useEffect ( ( ) => {
442+ updateExtension ( addExtensionsFor ( shouldHighlightActiveLine ?? false , adaptedHighlightActiveLine ( ) ) , shouldHighlightActiveLineCompartment . current )
443+ } , [ shouldHighlightActiveLine ] )
444+
445+ React . useEffect ( ( ) => {
446+ updateExtension ( addExtensionsFor ( wrapLines ?? false , EditorView ?. lineWrapping ) , wrapLinesCompartment . current )
447+ } , [ wrapLines ] )
448+
449+ React . useEffect ( ( ) => {
450+ updateExtension ( addExtensionsFor ( supportCodeFolding ?? false , adaptedFoldGutter ( ) , adaptedCodeFolding ( ) ) , supportCodeFoldingCompartment . current )
451+ } , [ supportCodeFolding ] )
452+
453+ React . useEffect ( ( ) => {
454+ updateExtension ( addExtensionsFor ( useLinting ?? false , ...linters ) , useLintingCompartment . current )
455+ } , [ mode , useLinting ] )
379456
380457 const hasToolbarSupport = mode && ModeToolbarSupport . indexOf ( mode ) > - 1 && useToolbar ;
381458
0 commit comments