Skip to content

Commit

Permalink
Desktop: Improve beta editor support for the Rich Markdown plugin (#9935
Browse files Browse the repository at this point in the history
)
  • Loading branch information
personalizedrefrigerator authored Mar 9, 2024
1 parent 17a8ce5 commit c35085d
Show file tree
Hide file tree
Showing 12 changed files with 152 additions and 17 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@ packages/default-plugins/utils/getCurrentCommitHash.js
packages/default-plugins/utils/getPathToPatchFileFor.js
packages/default-plugins/utils/readRepositoryJson.js
packages/default-plugins/utils/waitForCliInput.js
packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5BuiltInOptions.js
packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.test.js
packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.js
packages/editor/CodeMirror/CodeMirror5Emulation/Decorator.js
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ packages/default-plugins/utils/getCurrentCommitHash.js
packages/default-plugins/utils/getPathToPatchFileFor.js
packages/default-plugins/utils/readRepositoryJson.js
packages/default-plugins/utils/waitForCliInput.js
packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5BuiltInOptions.js
packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.test.js
packages/editor/CodeMirror/CodeMirror5Emulation/CodeMirror5Emulation.js
packages/editor/CodeMirror/CodeMirror5Emulation/Decorator.js
Expand Down
4 changes: 3 additions & 1 deletion packages/app-desktop/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,10 @@ class Application extends BaseApplication {

// The '*' and '!important' parts are necessary to make sure Russian text is displayed properly
// https://github.com/laurent22/joplin/issues/155
//
// Note: Be careful about the specificity here. Incorrect specificity can break monospaced fonts in tables.

const css = `.CodeMirror *, .cm-editor .cm-content { font-family: ${fontFamilies.join(', ')} !important; }`;
const css = `.CodeMirror5 *, .cm-editor .cm-content { font-family: ${fontFamilies.join(', ')} !important; }`;
const styleTag = document.createElement('style');
styleTag.type = 'text/css';
styleTag.appendChild(document.createTextNode(css));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ function Editor(props: EditorProps, ref: any) {
}
}, [pluginOptions, editor]);

return <div className='codeMirrorEditor' style={props.style} ref={editorParent} />;
return <div className='codeMirrorEditor CodeMirror5' style={props.style} ref={editorParent} />;
}

export default forwardRef(Editor);
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ const Editor = (props: Props, ref: ForwardedRef<CodeMirrorControl>) => {
const editor = createEditor(editorContainerRef.current, editorProps);
editor.addStyles({
'.cm-scroller': { overflow: 'auto' },
'&.CodeMirror': {
height: 'unset',
background: 'unset',
overflow: 'unset',
direction: 'unset',
},
});
setEditor(editor);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Compartment, Extension, RangeSetBuilder, StateEffect } from '@codemirror/state';
import { Decoration, DecorationSet, EditorView, ViewPlugin, ViewUpdate } from '@codemirror/view';

const activeLineDecoration = Decoration.line({ class: 'CodeMirror-activeline CodeMirror-activeline-background' });

const optionToExtension: Record<string, Extension> = {
'styleActiveLine': [
ViewPlugin.fromClass(class {
public decorations: DecorationSet;

public constructor(view: EditorView) {
this.updateDecorations(view);
}

public update(update: ViewUpdate) {
this.updateDecorations(update.view);
}

private updateDecorations(view: EditorView) {
const builder = new RangeSetBuilder<Decoration>();
let lastLine = -1;

for (const selection of view.state.selection.ranges) {
const startLine = selection.from;
const line = view.state.doc.lineAt(startLine);

if (line.number !== lastLine) {
builder.add(line.from, line.from, activeLineDecoration);
}

lastLine = line.number;
}

this.decorations = builder.finish();
}
}, {
decorations: plugin => plugin.decorations,
}),
EditorView.baseTheme({
'&dark .CodeMirror-activeline-background': {
background: '#3304',
color: 'white',
},
'&light .CodeMirror-activeline-background': {
background: '#7ff4',
color: 'black',
},
}),
],
};

// Maps several CM5 options to CM6 extensions
export default class CodeMirror5BuiltInOptions {
private activeOptions: string[] = [];
private extensionCompartment: Compartment = new Compartment();

public constructor(private editor: EditorView) {
editor.dispatch({
effects: StateEffect.appendConfig.of(this.extensionCompartment.of([])),
});
}

private updateExtensions() {
const extensions = this.activeOptions.map(option => optionToExtension[option]);
this.editor.dispatch({
effects: this.extensionCompartment.reconfigure(extensions),
});
}

public supportsOption(option: string) {
return optionToExtension.hasOwnProperty(option);
}

public setOption(optionName: string, value: boolean) {
this.activeOptions = this.activeOptions.filter(other => other !== optionName);

if (value) {
this.activeOptions.push(optionName);
}

this.updateExtensions();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { StateEffect } from '@codemirror/state';
import { StreamParser } from '@codemirror/language';
import Decorator, { LineWidgetOptions, MarkTextOptions } from './Decorator';
import insertLineAfter from '../editorCommands/insertLineAfter';
import CodeMirror5BuiltInOptions from './CodeMirror5BuiltInOptions';
const { pregQuote } = require('@joplin/lib/string-utils-common');


Expand Down Expand Up @@ -52,6 +53,7 @@ export default class CodeMirror5Emulation extends BaseCodeMirror5Emulation {
private _options: Record<string, CodeMirror5OptionRecord> = Object.create(null);
private _decorator: Decorator;
private _decoratorExtension: Extension;
private _builtInOptions: CodeMirror5BuiltInOptions;

// Used by some plugins to store state.
public state: Record<string, any> = Object.create(null);
Expand All @@ -70,6 +72,7 @@ export default class CodeMirror5Emulation extends BaseCodeMirror5Emulation {
const { decorator, extension: decoratorExtension } = Decorator.create(editor);
this._decorator = decorator;
this._decoratorExtension = decoratorExtension;
this._builtInOptions = new CodeMirror5BuiltInOptions(editor);

editor.dispatch({
effects: StateEffect.appendConfig.of(this.makeCM6Extensions()),
Expand Down Expand Up @@ -129,10 +132,8 @@ export default class CodeMirror5Emulation extends BaseCodeMirror5Emulation {
return { dom };
}),

// Note: We can allow legacy CM5 CSS to apply to the editor
// with a line similar to the following:
// EditorView.editorAttributes.of({ class: 'CodeMirror' }),
// Many of these styles, however, don't work well with CodeMirror 6.
// Allows legacy CM5 CSS to apply to the editor:
EditorView.editorAttributes.of({ class: 'CodeMirror' }),
];
}

Expand Down Expand Up @@ -316,6 +317,8 @@ export default class CodeMirror5Emulation extends BaseCodeMirror5Emulation {
const oldValue = this._options[name].value;
this._options[name].value = value;
this._options[name].onUpdate(this, value, oldValue);
} else if (this._builtInOptions.supportsOption(name)) {
this._builtInOptions.setOption(name, value);
} else {
super.setOption(name, value);
}
Expand All @@ -329,6 +332,20 @@ export default class CodeMirror5Emulation extends BaseCodeMirror5Emulation {
}
}

public override coordsChar(coords: { left: number; top: number }, mode?: 'div' | 'local'): DocumentPosition {
// codemirror-vim's API only supports "div" mode. Thus, we convert
// local to div:
if (mode !== 'div') {
const bbox = this.editor.contentDOM.getBoundingClientRect();
coords = {
left: coords.left - bbox.left,
top: coords.top - bbox.top,
};
}

return super.coordsChar(coords, 'div');
}

// codemirror-vim's API doesn't match the API docs here -- it expects addOverlay
// to return a SearchQuery. As such, this override returns "any".
public override addOverlay<State>(modeObject: OverlayType<State>): any {
Expand All @@ -353,7 +370,26 @@ export default class CodeMirror5Emulation extends BaseCodeMirror5Emulation {
}

public addLineWidget(lineNumber: number, node: HTMLElement, options: LineWidgetOptions) {
this._decorator.addLineWidget(lineNumber, node, options);
return this._decorator.addLineWidget(lineNumber, node, options);
}

public addWidget(pos: DocumentPosition, node: HTMLElement) {
if (node.parentElement) {
node.remove();
}

const loc = posFromDocumentPosition(this.editor.state.doc, pos);
const screenCoords = this.editor.coordsAtPos(loc);
const bbox = this.editor.contentDOM.getBoundingClientRect();

node.style.position = 'absolute';

const left = screenCoords.left - bbox.left;
node.style.left = `${left}px`;
node.style.maxWidth = `${bbox.width - left}px`;
node.style.top = `${screenCoords.top + this.editor.scrollDOM.scrollTop}px`;

this.editor.scrollDOM.appendChild(node);
}

public markText(from: DocumentPosition, to: DocumentPosition, options?: MarkTextOptions) {
Expand Down
3 changes: 3 additions & 0 deletions packages/editor/CodeMirror/CodeMirror5Emulation/Decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ class WidgetDecorationWrapper extends WidgetType {
container.classList.add(this.options.className);
}

// Applies margins and related CSS:
container.classList.add('cm-line');

return container;
}
}
Expand Down
12 changes: 6 additions & 6 deletions packages/editor/CodeMirror/markdown/decoratorExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,27 +49,27 @@ const blockQuoteDecoration = Decoration.line({
});

const header1LineDecoration = Decoration.line({
attributes: { class: 'cm-h1 cm-headerLine' },
attributes: { class: 'cm-h1 cm-headerLine cm-header' },
});

const header2LineDecoration = Decoration.line({
attributes: { class: 'cm-h2 cm-headerLine' },
attributes: { class: 'cm-h2 cm-headerLine cm-header' },
});

const header3LineDecoration = Decoration.line({
attributes: { class: 'cm-h3 cm-headerLine' },
attributes: { class: 'cm-h3 cm-headerLine cm-header' },
});

const header4LineDecoration = Decoration.line({
attributes: { class: 'cm-h4 cm-headerLine' },
attributes: { class: 'cm-h4 cm-headerLine cm-header' },
});

const header5LineDecoration = Decoration.line({
attributes: { class: 'cm-h5 cm-headerLine' },
attributes: { class: 'cm-h5 cm-headerLine cm-header' },
});

const header6LineDecoration = Decoration.line({
attributes: { class: 'cm-h6 cm-headerLine' },
attributes: { class: 'cm-h6 cm-headerLine cm-header' },
});

const tableHeaderDecoration = Decoration.line({
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/CodeMirror/pluginApi/PluginLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export default class PluginLoader {
pluginId: plugin.pluginId,
contentScriptId: plugin.contentScriptId,
};
const loadedPlugin = exports.default(context);
const loadedPlugin = exports.default(context) ?? {};

loadedPlugin.plugin?.(this.editor);

Expand Down
6 changes: 4 additions & 2 deletions packages/editor/CodeMirror/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,12 @@ const createTheme = (theme: EditorTheme): Extension[] => {
};

const codeMirrorTheme = EditorView.theme({
'&': baseGlobalStyle,
// Include &.CodeMirror to handle the case where additional CodeMirror 5 styles
// need to be overridden.
'&, &.CodeMirror': baseGlobalStyle,

// These must be !important or more specific than CodeMirror's built-ins
'.cm-content': {
'& .cm-content': {
fontFamily: theme.fontFamily,
...baseContentStyle,
paddingBottom: theme.isDesktop ? '400px' : undefined,
Expand Down
3 changes: 2 additions & 1 deletion packages/tools/cspell/dictionary4.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,5 @@ activatable
titlewrapper
notyf
Notyf
Prec
activeline
Prec

0 comments on commit c35085d

Please sign in to comment.