UNPKG

@bhsd/codemirror-mediawiki

Version:

Modified CodeMirror mode based on wikimedia/mediawiki-extensions-CodeMirror

85 lines (84 loc) 3.37 kB
/** * @author MusikAnimal * @license GPL-2.0-or-later * @see https://gerrit.wikimedia.org/g/mediawiki/extensions/CodeMirror */ import { EditorView, Direction, ViewPlugin, Decoration } from '@codemirror/view'; import { Prec, RangeSetBuilder } from '@codemirror/state'; import { syntaxTree } from '@codemirror/language'; import { getTag } from './matchTag'; import { tokens } from './config'; const isolateLTR = Decoration.mark({ class: 'cm-bidi-isolate cm-bidi-ltr', bidiIsolate: Direction.LTR, }), isolate = Decoration.mark({ class: 'cm-bidi-isolate' }); export const computeIsolates = ({ visibleRanges, state, textDirection }) => { const set = new RangeSetBuilder(); if (textDirection === Direction.RTL) { for (const { from, to } of visibleRanges) { let node = syntaxTree(state).resolve(from, 1), td = 0, table = 0, parameter = 0; while (node && node.to <= to) { const { name, from: f, to: t, nextSibling } = node; if (/-(?:ext|html)tag-bracket/u.test(name) && state.sliceDoc(f, t).includes('<')) { const tag = getTag(state, nextSibling); if (tag) { set.add(tag.from, tag.to, isolateLTR); } } else if (!td && !table && name.includes(tokens.tableDefinition)) { if (/-html-(?:table|tr)/u.test(name)) { table = state.doc.lineAt(f).to; set.add(f, table, isolateLTR); } else { td = f; } } else if (table && f > table) { table = 0; } else if (td && name.includes(tokens.tableDelimiter2)) { set.add(td, f, isolateLTR); td = 0; } else if (/-(?:template|parserfunction)-delimiter/u.test(name)) { if (parameter) { set.add(parameter, f, isolate); } parameter = t; } else if (parameter && /-(?:template|parserfunction)-bracket/u.test(name)) { if (state.sliceDoc(f, f + 1) === '}') { set.add(parameter, f, isolate); } parameter = 0; } node = node.nextSibling; } } } return set.finish(); }; export default ViewPlugin.fromClass(class { constructor(view) { this.isolates = computeIsolates(view); this.tree = syntaxTree(view.state); this.dir = view.textDirection; } update({ docChanged, viewportChanged, state, view }) { const tree = syntaxTree(state), { textDirection } = view; if (docChanged || viewportChanged || tree !== this.tree || textDirection !== this.dir) { this.isolates = computeIsolates(view); this.tree = tree; this.dir = textDirection; } } }, { provide(plugin) { const access = (view) => view.plugin(plugin)?.isolates ?? Decoration.none; return Prec.lowest([ EditorView.decorations.of(access), EditorView.bidiIsolatedRanges.of(access), ]); }, });