Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to codemirror 6 #3617

Merged
merged 24 commits into from
Apr 18, 2023
Merged

Upgrade to codemirror 6 #3617

merged 24 commits into from
Apr 18, 2023

Conversation

ssddanbrown
Copy link
Member

@ssddanbrown ssddanbrown commented Aug 2, 2022

PR to track upgrade from Codemirror 5 to 6. Quite a large change.

Closes #3518

References

Progress Report

Got a lot of fundamentals in place but concerned about the growth of complexity here.

Specifically:

  • The growth of bundle size, might need to do finer splitting. Added extra split
  • The complex requirements of a theme. They're really designed to be bundled. Here's an example of an external theme. Don't think we can just define a string and import global JS like before. We could pass all required dependencies through a configure event but the theme implementation will probably be quite custom to BookStack. Or can we provide a simpler abstraction?
    • Now added new API with methods to allow themeing. Is passed required tags. Is a custom API so external themes will need adapting.

Probable going to progress this slowly across a number of months, to understand codemirror rate of change and to gain some perspectives on best approach.

Todo

  • Add system to handle languages using old map keys.
  • Fix missing languages (commented out)
  • Use new lang packages where available
  • Handle language loading to prevent large bundle size.
  • Find solution for smarty/twig where possible.
  • Test activation of each language available.
  • Fix usage in WYSIWYG editor.
  • Fix usage within WYSIWYG editor code popup window.
  • Understand themeing, and assess existing use/docs mention of window.codeTheme.
  • Markdown editor shortcuts.
  • Markdown editor events.
  • Add back support for the copy-icon popup buttony thingy.
  • Test support of PHP blocks without starting php tag and mixed content.
  • Add block tab indent/un-indent support for code editor.
  • Dark mode support
  • Align events to be cm6 instead of cm.
  • Remove extra border in md editor.
  • Only add copy button if in secure context Added execCommand fallback instead.
  • Cross-browser testing
    • Tested on FF/Chromium (Fedora 37), Safari/FF on MacOS

Docs/API Updates

  • The 'editor-markdown::setup' editor event no longer contains codeMirrorInstance in the event data. It instead has a cmEditorView property that contains the CodeMirror EditorView instance for the Markdown editor.
  • The 'editor-markdown-cm::pre-init' editor event has been renamed to editor-markdown-cm6::pre-init and no longer contains config in the event data. It instead has a editorViewConfig property that contains the CodeMirror EditorViewConfig data that's used to create the EditorView. Config object can be manipulated before use.
  • window.codeTheme is no longer used to set themes. Instead, there's a library-cm6::configure-theme event which emits event data in the following format:
{
    // Boolean to indicate if dark mode is active.
    darkModeActive: true,
    // Call this function with a builder callback to register a CodeMirror view theme.
    // If a value is returned it will be passed to CodeMirror View.theme as an Object<StyleSpec>.
    registerViewTheme: (builder) => {},
    // Call this function with a builder callback to register a CodeMirror highlighting style.
    // The builder callback is provided a references to Lezer.tags.
    // If a value is returned it will be passed to CodeMirror HighlightStyle.define as an Array<TagStyle>
    registerHighlightStyle: (builder) => {},
}

@ssddanbrown ssddanbrown removed this from the Next Feature Release milestone Aug 4, 2022
Cannot find existing option for twig/smarty, need to look other methods.
@ssddanbrown ssddanbrown changed the base branch from development to user_permissions February 17, 2023 22:20
@ssddanbrown ssddanbrown changed the base branch from user_permissions to development February 17, 2023 22:20
@ssddanbrown ssddanbrown mentioned this pull request Mar 22, 2023
1 task
- Updated clipboard handling
  - Removed old clipboard package for browser-native API.
- Updated codemirror editor events to use new props for new data types.
Split out legacy modes to their own dynamically imported bundle to
reduce main code bundle size.
Uses our custom event system, uses methods that take callables so that
internal dependancies can be passed.
@ssddanbrown
Copy link
Member Author

Custom Theme Registration Example

Details

<script>

    // Theme data taken from:
    // https://github.com/craftzdog/cm6-themes/blob/main/packages/solarized-dark/src/index.ts
    // (MIT License) - Copyright (C) 2022 by Takuya Matsuyama and others
    const base00 = '#002b36',
        base01 = '#073642',
        base02 = '#586e75',
        base03 = '#657b83',
        base04 = '#839496',
        base05 = '#93a1a1',
        base06 = '#eee8d5',
        base07 = '#fdf6e3',
        base_red = '#dc322f',
        base_orange = '#cb4b16',
        base_yellow = '#b58900',
        base_green = '#859900',
        base_cyan = '#2aa198',
        base_blue = '#268bd2',
        base_violet = '#6c71c4',
        base_magenta = '#d33682'

    const invalid = '#d30102',
        stone = base04,
        darkBackground = '#00252f',
        highlightBackground = '#173541',
        background = base00,
        tooltipBackground = base01,
        selection = '#173541',
        cursor = base04

    function viewThemeBuilder() {
        return {
            '&': {
                color: base05,
                backgroundColor: background
            },

            '.cm-content': {
                caretColor: cursor
            },

            '.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor },
            '&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection':
                { backgroundColor: selection },

            '.cm-panels': { backgroundColor: darkBackground, color: base03 },
            '.cm-panels.cm-panels-top': { borderBottom: '2px solid black' },
            '.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' },

            '.cm-searchMatch': {
                backgroundColor: '#72a1ff59',
                outline: '1px solid #457dff'
            },
            '.cm-searchMatch.cm-searchMatch-selected': {
                backgroundColor: '#6199ff2f'
            },

            '.cm-activeLine': { backgroundColor: highlightBackground },
            '.cm-selectionMatch': { backgroundColor: '#aafe661a' },

            '&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
                outline: `1px solid ${base06}`
            },

            '.cm-gutters': {
                backgroundColor: darkBackground,
                color: stone,
                border: 'none'
            },

            '.cm-activeLineGutter': {
                backgroundColor: highlightBackground
            },

            '.cm-foldPlaceholder': {
                backgroundColor: 'transparent',
                border: 'none',
                color: '#ddd'
            },

            '.cm-tooltip': {
                border: 'none',
                backgroundColor: tooltipBackground
            },
            '.cm-tooltip .cm-tooltip-arrow:before': {
                borderTopColor: 'transparent',
                borderBottomColor: 'transparent'
            },
            '.cm-tooltip .cm-tooltip-arrow:after': {
                borderTopColor: tooltipBackground,
                borderBottomColor: tooltipBackground
            },
            '.cm-tooltip-autocomplete': {
                '& > ul > li[aria-selected]': {
                    backgroundColor: highlightBackground,
                    color: base03
                }
            }
        };
    }

    function highlightStyleBuilder(t) {
        return [
            { tag: t.keyword, color: base_green },
            {
                tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName],
                color: base_cyan
            },
            { tag: [t.variableName], color: base05 },
            { tag: [t.function(t.variableName)], color: base_blue },
            { tag: [t.labelName], color: base_magenta },
            {
                tag: [t.color, t.constant(t.name), t.standard(t.name)],
                color: base_yellow
            },
            { tag: [t.definition(t.name), t.separator], color: base_cyan },
            { tag: [t.brace], color: base_magenta },
            {
                tag: [t.annotation],
                color: invalid
            },
            {
                tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],
                color: base_magenta
            },
            {
                tag: [t.typeName, t.className],
                color: base_orange
            },
            {
                tag: [t.operator, t.operatorKeyword],
                color: base_violet
            },
            {
                tag: [t.tagName],
                color: base_blue
            },
            {
                tag: [t.squareBracket],
                color: base_red
            },
            {
                tag: [t.angleBracket],
                color: base02
            },
            {
                tag: [t.attributeName],
                color: base05
            },
            {
                tag: [t.regexp],
                color: invalid
            },
            {
                tag: [t.quote],
                color: base_green
            },
            { tag: [t.string], color: base_yellow },
            {
                tag: t.link,
                color: base_cyan,
                textDecoration: 'underline',
                textUnderlinePosition: 'under'
            },
            {
                tag: [t.url, t.escape, t.special(t.string)],
                color: base_yellow
            },
            { tag: [t.meta], color: base_red },
            { tag: [t.comment], color: base02, fontStyle: 'italic' },
            { tag: t.strong, fontWeight: 'bold', color: base06 },
            { tag: t.emphasis, fontStyle: 'italic', color: base_green },
            { tag: t.strikethrough, textDecoration: 'line-through' },
            { tag: t.heading, fontWeight: 'bold', color: base_yellow },
            { tag: t.heading1, fontWeight: 'bold', color: base07 },
            {
                tag: [t.heading2, t.heading3, t.heading4],
                fontWeight: 'bold',
                color: base06
            },
            {
                tag: [t.heading5, t.heading6],
                color: base06
            },
            { tag: [t.atom, t.bool, t.special(t.variableName)], color: base_magenta },
            {
                tag: [t.processingInstruction, t.inserted, t.contentSeparator],
                color: base_red
            },
            {
                tag: [t.contentSeparator],
                color: base_yellow
            },
            { tag: t.invalid, color: base02, borderBottom: `1px dotted ${base_red}` }
        ];
    }

    window.addEventListener('library-cm6::configure-theme', event => {
        const detail = event.detail;
        detail.registerViewTheme(viewThemeBuilder);
        detail.registerHighlightStyle(highlightStyleBuilder);
    });
</script>

Required monkey-patch to work around potential codemirror issue with
shadowdom+iframe usage.
Also updated JS packages to latest versions.
New simple interface added for abstraction of CM editor in simple
use-cases, just to provide common actions like get/set content, focus
and set mode.
- Fixed some keybindings not running as expected, due to some editor
  defaults overriding or further actions taking place since the action
  would not indicate it's been dealt with (by returning boolean).
- Fixed spacing/border-radius being used on codeblocks on non-intended
  areas like the MD editor.
- Fixed lack of BG on default light theme, visible on full screen md
  editor.
- Fixed error thrown when the user does not have access to change the
  current editor (Likely non-cm related existing issue)
- Updated event naming to be "cm6" when codemirror-specific.
- Removed cm block border in md editor to prevent double bordering.
- Updated copy handling to fallback to execCommand.
@ssddanbrown ssddanbrown added this to the Next Feature Release milestone Apr 18, 2023
@ssddanbrown ssddanbrown merged commit 69d0304 into development Apr 18, 2023
@ssddanbrown ssddanbrown deleted the codemirror6 branch April 18, 2023 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

Upgrade to CodeMirror 6
1 participant