Isolate build step to CM6 library and restructure files to work with RL

CodeMirror 6 requires the use of NPM, but we can still bundle all CM
packages into one file, and then everything else (i.e. our code) is
managed by ResourceLoader as per usual. This makes contribution
considerably easier as we no longer need a build step for each change.

CM5 files are now under resources/legacy, and the CM6 files are moved to
the root of the resources/ directory. Only one file,
codemirror.bundle.js, is managed by Rollup, while everything else is RL.
The Rollup output for now is put under resources/lib/ alongside the CM5
upstream files.

This patch is *mostly* renames of files, along with changing ECMAScript
Module (ESM) syntax into the CommonJS style that ResourceLoader prefers.
We also remove more modern JS syntax (i.e. private class methods) that
we were able to use before because we had a build step with Babel.

This patch should effectively make no user-facing changes, or to the
ResourceLoader modules we offer in Extension:CodeMirror.

Finally, bump version in extension.json to 6, to match the upstream lib,
and add Bhsd as an author :-)

Bug: T368053
Change-Id: Ie258e49f5df8db23a7344ac3c4c9300aaa991042
This commit is contained in:
MusikAnimal 2024-06-20 23:21:09 -04:00
parent f3be172173
commit 7d3482f89e
47 changed files with 23838 additions and 2427 deletions

View file

@ -1,4 +0,0 @@
{
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-transform-private-methods"]
}

View file

@ -1,27 +1,30 @@
The CodeMirror extension provides syntax highlighting in MediaWiki wikitext editors using
the [CodeMirror library](https://codemirror.net/).
CodeMirror 6 homepage: [https://www.mediawiki.org/wiki/Extension:CodeMirror/6](https://www.mediawiki.org/wiki/Extension:CodeMirror/6)
JS documentation: [https://doc.wikimedia.org/CodeMirror](https://doc.wikimedia.org/CodeMirror)
## Development
As part of the [upgrade to CodeMirror 6](https://phabricator.wikimedia.org/T259059),
CodeMirror now uses an asset bundler, so during development you'll need to run a script
to assemble the frontend assets.
### Preface
Extension:CodeMirror is currently in the process of being upgraded to the new major version, CodeMirror 6.
See the [change log](https://www.mediawiki.org/wiki/Extension:CodeMirror/6#Change_log) for details.
Use of CodeMirror 6 is controlled by the `wgCodeMirrorV6` configuration setting, or by
passing in `cm6enable=1` in the URL query string.
You can find the v6 frontend source files in `src/`, the compiled sources in
`resources/dist/`, and other frontend assets managed by ResourceLoader in
`resources/*`.
CodeMirror 6 requires the use of NPM to bundle the dependencies. These are bundled in
[resources/codemirror.bundle.js](resources/codemirror.bundle.js), built using [Rollup](https://rollupjs.org/),
and packaged as the `ext.CodeMirror.v6.lib` ResourceLoader module. If you make changes to the
versions of the dependencies, you will need to run `npm run build` to update the ResourceLoader module.
### Commands
### NPM commands
_NOTE: Consider using [Fresh](https://gerrit.wikimedia.org/g/fresh/) to run these tasks._
* `npm install` to install dependencies.
* `npm start` to run the bundler in watch mode, reassembling the files on file change.
You'll want to keep this running in a separate terminal during development.
* `npm run build` to compile the production assets. You *must* run this step before
sending the patch or CI will fail (so that sources and built assets are in sync).
* `npm run doc` to generate the API documentation.
* `npm test` to run the linting tools, JavaScript unit tests, and build checks.
* `npm run test:lint` for linting of JS/LESS/CSS.
@ -30,9 +33,8 @@ _NOTE: Consider using [Fresh](https://gerrit.wikimedia.org/g/fresh/) to run thes
* `npm run test:i18n` for linting of i18n messages with banana-checker.
* `npm run test:unit` for the new Jest unit tests.
* `npm run selenium-test` for the Selenium tests.
* `npm run build` to rebundle the CodeMirror library. If changes are made to the `@codemirror`
or `@lezer` dependencies in [package.json](package.json), this command *must* be run before
sending the patch or CI will fail.
* Older QUnit tests are in `resources/mode/mediawiki/tests/qunit/`. These have been
replaced and will be removed after the CodeMirror 6 upgrade.
## CodeMirror 6 change log
* See [Extension:CodeMirror/6](https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:CodeMirror/6#Differences_from_CodeMirror_5)
replaced and will be removed after the CodeMirror 6 upgrade is complete.

View file

@ -1,11 +1,12 @@
{
"name": "CodeMirror",
"version": "5.0.0",
"version": "6.0.0",
"author": [
"[https://www.mediawiki.org/wiki/User:Pastakhov Pavel Astakhov]",
"[https://www.mediawiki.org/wiki/User:Florianschmidtwelzow Florian Schmidt]",
"Marijn Haverbeke",
"MusikAnimal",
"Bhsd",
"[https://raw.githubusercontent.com/codemirror/CodeMirror/master/AUTHORS CodeMirror contributors]"
],
"url": "https://www.mediawiki.org/wiki/Extension:CodeMirror",
@ -58,9 +59,9 @@
"user.options"
],
"packageFiles": [
"ext.CodeMirror.js",
"legacy/ext.CodeMirror.js",
{
"name": "ext.CodeMirror.data.js",
"name": "legacy/ext.CodeMirror.data.js",
"callback": "MediaWiki\\Extension\\CodeMirror\\DataScript::makeScript"
}
]
@ -74,10 +75,10 @@
"oojs-ui.styles.icons-editing-styling"
],
"packageFiles": [
"ext.CodeMirror.WikiEditor.js"
"legacy/ext.CodeMirror.WikiEditor.js"
],
"styles": [
"ext.CodeMirror.less"
"legacy/ext.CodeMirror.less"
],
"messages": [
"codemirror-toggle-label"
@ -89,22 +90,22 @@
],
"styles": [
"lib/codemirror/lib/codemirror.css",
"lib/codemirror-fixes.less"
"legacy/codemirror-fixes.less"
]
},
"ext.CodeMirror.addons": {
"scripts": [
"addon/edit/matchbrackets-wmde.js"
"legacy/addon/matchbrackets-wmde.js"
],
"dependencies": [
"ext.CodeMirror.lib"
]
},
"ext.CodeMirror.mode.mediawiki": {
"packageFiles": "mode/mediawiki/mediawiki.js",
"packageFiles": "legacy/mode/mediawiki/mediawiki.js",
"styles": [
"mode/mediawiki/mediawiki.less",
"mode/mediawiki/colorblind-colors.less"
"codemirror.mediawiki.less",
"codemirror.mediawiki.colorblind.less"
],
"dependencies": [
"ext.CodeMirror.lib"
@ -160,11 +161,11 @@
"ext.CodeMirror"
],
"scripts": [
"modules/ve-cm/ve.ui.CodeMirrorAction.js",
"modules/ve-cm/ve.ui.CodeMirrorTool.js"
"legacy/modules/ve-cm/ve.ui.CodeMirrorAction.js",
"legacy/modules/ve-cm/ve.ui.CodeMirrorTool.js"
],
"styles": [
"modules/ve-cm/ve.ui.CodeMirror.less"
"legacy/modules/ve-cm/ve.ui.CodeMirror.less"
],
"messages": [
"codemirror-toggle-label"
@ -172,21 +173,21 @@
},
"ext.CodeMirror.v6": {
"dependencies": [
"web2017-polyfills",
"mediawiki.api",
"mediawiki.user",
"user.options",
"ext.CodeMirror.v6.lib"
],
"packageFiles": [
"dist/codemirror.js",
"codemirror.js",
"codemirror.textSelection.js",
{
"name": "ext.CodeMirror.data.js",
"callback": "MediaWiki\\Extension\\CodeMirror\\DataScript::makeScript"
}
],
"styles": [
"ext.CodeMirror.v6.less"
"codemirror.less"
],
"messages": [
"codemirror-find",
@ -234,22 +235,24 @@
"ext.CodeMirror.v6.mode.mediawiki"
],
"packageFiles": [
"dist/codemirror.mediawiki.js"
"codemirror.mediawiki.init.js"
]
},
"ext.CodeMirror.v6.lib": {
"packageFiles": [
"dist/vendor.js"
],
"dependencies": [
"web2017-polyfills"
"lib/codemirror6.bundle.dist.js"
]
},
"ext.CodeMirror.v6.mode.mediawiki": {
"packageFiles": "dist/codemirror.mode.mediawiki.js",
"packageFiles": [
"codemirror.mediawiki.js",
"codemirror.mediawiki.config.js",
"codemirror.mediawiki.bidiIsolation.js",
"codemirror.mediawiki.templateFolding.js"
],
"styles": [
"mode/mediawiki/mediawiki.less",
"mode/mediawiki/colorblind-colors.less"
"codemirror.mediawiki.less",
"codemirror.mediawiki.colorblind.less"
],
"dependencies": [
"ext.CodeMirror.v6",
@ -258,11 +261,10 @@
},
"ext.CodeMirror.v6.WikiEditor": {
"dependencies": [
"ext.wikiEditor",
"ext.CodeMirror.v6"
"ext.wikiEditor"
],
"packageFiles": [
"dist/codemirror.wikieditor.js"
"codemirror.wikieditor.js"
],
"messages": [
"codemirror-toggle-label"
@ -274,7 +276,7 @@
"ext.CodeMirror.v6.mode.mediawiki"
],
"packageFiles": [
"dist/codemirror.wikieditor.mediawiki.js"
"codemirror.wikieditor.mediawiki.init.js"
]
}
},
@ -287,8 +289,8 @@
"usecodemirror": 0
},
"QUnitTestModule": {
"localBasePath": "resources/mode/mediawiki/tests",
"remoteExtPath": "CodeMirror/resources/mode/mediawiki/tests",
"localBasePath": "resources/legacy/mode/mediawiki/tests",
"remoteExtPath": "CodeMirror/resources/legacy/mode/mediawiki/tests",
"packageFiles": [
"qunit/CodeMirror.mediawiki.test.js"
],

View file

@ -11,7 +11,8 @@
"node_modules/jsdoc-wmf-theme/plugins/default"
],
"source": {
"include": [ "src" ],
"include": [ "resources" ],
"exclude": [ "resources/legacy", "resources/lib" ],
"includePattern": ".+\\.js$"
},
"tags": {},
@ -25,9 +26,10 @@
"repository": "https://gerrit.wikimedia.org/g/mediawiki/extensions/CodeMirror",
"linkMap": {
"jQuery.fn.textSelection": "https://doc.wikimedia.org/mediawiki-core/master/js/jQueryPlugins.html#.textSelection",
"Compartment": "https://codemirror.net/docs/ref/#state.Compartment",
"Decoration": "https://codemirror.net/docs/ref/#view.Decoration",
"DecorationSet": "https://codemirror.net/docs/ref/#view.DecorationSet",
"Direction": "https://codemirror.net/docs/ref/#view.Direction",
"EditorState": "https://codemirror.net/docs/ref/#state.EditorState",
"EditorView": "https://codemirror.net/docs/ref/#view.EditorView",
"Extension": "https://codemirror.net/docs/ref/#state.Extension",

2665
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,16 +1,15 @@
{
"name": "CodeMirror",
"name": "codemirror",
"private": true,
"scripts": {
"start": "rollup -c --watch",
"build": "rollup -c --environment BUILD:production",
"build": "rollup -c",
"test": "npm run test:lint && npm run test:unit && npm run check-built-assets",
"test:lint": "npm run test:lint:styles && npm run test:lint:js && npm run test:lint:i18n",
"test:lint:js": "eslint --cache .",
"test:lint:styles": "stylelint \"resources/**/*.less\"",
"test:lint:i18n": "banana-checker i18n/",
"test:unit": "jest",
"check-built-assets": "{ git status src/ | grep \"nothing to commit, working tree clean\"; } && { echo 'CHECKING BUILD SOURCES ARE COMMITTED' && npm run build && git status resources/dist/ | grep \"nothing to commit, working tree clean\" || { npm run node-debug; false; }; }",
"check-built-assets": "{ git status src/ | grep \"nothing to commit, working tree clean\"; } && { echo 'CHECKING BUILD SOURCES ARE COMMITTED' && npm run build && git status resources/lib/ | grep \"nothing to commit, working tree clean\" || { npm run node-debug; false; }; }",
"node-debug": "node -v && npm -v && echo 'ERROR: Please ensure that production assets have been built with `npm run build` and commited, and that you are using the correct version of Node/NPM.'",
"selenium-test": "wdio tests/selenium/wdio.conf.js",
"doc": "jsdoc -c jsdoc.json"
@ -19,22 +18,18 @@
"node": "18.20.2"
},
"devDependencies": {
"@babel/plugin-transform-private-methods": "7.23.3",
"@babel/preset-env": "7.24.0",
"@codemirror/commands": "6.2.5",
"@codemirror/language": "6.9.3",
"@codemirror/search": "6.5.4",
"@codemirror/state": "6.2.1",
"@codemirror/view": "6.22.2",
"@lezer/highlight": "1.2.0",
"@rollup/plugin-babel": "6.0.4",
"@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-terser": "0.4.4",
"@wdio/cli": "7.30.1",
"@wdio/junit-reporter": "7.29.1",
"@wdio/local-runner": "7.30.1",
"@wdio/mocha-framework": "7.26.0",
"@wdio/spec-reporter": "7.29.1",
"@wdio/cli": "7.36.0",
"@wdio/junit-reporter": "7.35.0",
"@wdio/local-runner": "7.36.0",
"@wdio/mocha-framework": "7.33.0",
"@wdio/spec-reporter": "7.33.0",
"@wikimedia/mw-node-qunit": "7.2.0",
"dotenv": "8.2.0",
"eslint-config-wikimedia": "0.28.2",
@ -45,8 +40,7 @@
"jsdoc": "4.0.3",
"jsdoc-wmf-theme": "1.1.0",
"rollup": "4.13.0",
"rollup-plugin-copy": "3.5.0",
"stylelint-config-wikimedia": "0.16.1",
"wdio-mediawiki": "2.3.0"
"wdio-mediawiki": "2.5.0"
}
}

View file

@ -6,13 +6,18 @@
"wikimedia/mediawiki"
],
"parserOptions": {
"sourceType": "commonjs"
"sourceType": "module"
},
"env": {
"browser": true,
"commonjs": true
},
"globals": {
"CodeMirror": "readonly"
"Tree": "readonly"
},
"rules": {
"max-len": "off"
"max-len": "off",
"es-x/no-array-prototype-includes": "off"
},
"overrides": [
{

View file

@ -0,0 +1,18 @@
/**
* This file is managed by Rollup and bundles all the CodeMirror dependencies
* into the single file resources/lib/codemirror6.bundle.dist.js.
*/
import '@codemirror/commands';
import '@codemirror/language';
import '@codemirror/search';
import '@codemirror/state';
import '@codemirror/view';
import '@lezer/highlight';
/* eslint-disable es-x/no-export-ns-from */
export * from '@codemirror/commands';
export * from '@codemirror/language';
export * from '@codemirror/search';
export * from '@codemirror/state';
export * from '@codemirror/view';
export * from '@lezer/highlight';

View file

@ -1,20 +1,23 @@
import { EditorState, Extension, Compartment } from '@codemirror/state';
import {
const {
EditorState,
EditorView,
drawSelection,
lineNumbers,
highlightSpecialChars,
keymap,
rectangularSelection,
Extension,
Compartment,
ViewUpdate,
bracketMatching,
crosshairCursor,
ViewUpdate
} from '@codemirror/view';
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
import { searchKeymap } from '@codemirror/search';
import { bracketMatching } from '@codemirror/language';
import CodeMirrorTextSelection from './codemirror.textSelection';
require( '../ext.CodeMirror.data.js' );
defaultKeymap,
drawSelection,
highlightSpecialChars,
history,
historyKeymap,
keymap,
lineNumbers,
rectangularSelection,
searchKeymap
} = require( 'ext.CodeMirror.v6.lib' );
const CodeMirrorTextSelection = require( './codemirror.textSelection.js' );
require( './ext.CodeMirror.data.js' );
/**
* Interface for the CodeMirror editor.
@ -381,7 +384,7 @@ class CodeMirror {
} );
// Add CodeMirror view to the DOM.
this.#addCodeMirrorToDom();
this.addCodeMirrorToDom();
// Hide native textarea and sync CodeMirror contents upon submission.
this.$textarea.hide();
@ -418,7 +421,7 @@ class CodeMirror {
*
* @private
*/
#addCodeMirrorToDom() {
addCodeMirrorToDom() {
this.$textarea.wrap( '<div class="ext-codemirror-wrapper"></div>' );
this.view = new EditorView( {
@ -521,4 +524,4 @@ class CodeMirror {
}
}
export default CodeMirror;
module.exports = CodeMirror;

View file

@ -1,15 +1,17 @@
import {
const {
Decoration,
DecorationSet,
Direction,
EditorView,
PluginSpec,
Prec,
RangeSet,
RangeSetBuilder,
ViewPlugin,
ViewUpdate
} from '@codemirror/view';
import { Prec, RangeSet, RangeSetBuilder } from '@codemirror/state';
import { syntaxTree } from '@codemirror/language';
import { mwModeConfig } from './codemirror.mode.mediawiki.config';
ViewUpdate,
syntaxTree
} = require( 'ext.CodeMirror.v6.lib' );
const mwModeConfig = require( './codemirror.mediawiki.config.js' );
/**
* @type {Decoration}
@ -122,4 +124,4 @@ const bidiIsolationSpec = {
* @module CodeMirrorBidiIsolation
* @see https://codemirror.net/examples/bidi/
*/
export default ViewPlugin.fromClass( CodeMirrorBidiIsolation, bidiIsolationSpec );
module.exports = ViewPlugin.fromClass( CodeMirrorBidiIsolation, bidiIsolationSpec );

View file

@ -1,5 +1,9 @@
import { Tag, tags } from '@lezer/highlight';
import { TagStyle, StreamParser } from '@codemirror/language';
const {
StreamParser,
Tag,
TagStyle,
tags
} = require( 'ext.CodeMirror.v6.lib' );
/**
* Configuration for the MediaWiki highlighting mode for CodeMirror.
@ -10,7 +14,7 @@ import { TagStyle, StreamParser } from '@codemirror/language';
*
* @example
* // within MediaWiki:
* import { mwModeConfig } from 'ext.CodeMirror.v6.mode.mediawiki';
* const { mwModeConfig } = require( 'ext.CodeMirror.v6.mode.mediawiki' );
* // Reference tags by their constants in the tags property.
* if ( tag === mwModeConfig.tags.htmlTagBracket ) {
* // …
@ -118,7 +122,7 @@ class CodeMirrorModeMediaWikiConfig {
* @return {Object<string>}
*/
get tags() {
return {
return Object.assign( {
apostrophes: 'character',
apostrophesBold: 'strong',
apostrophesItalic: 'emphasis',
@ -160,9 +164,8 @@ class CodeMirrorModeMediaWikiConfig {
templateName: 'moduleKeyword',
templateVariable: 'atom',
templateVariableBracket: 'brace',
templateVariableName: 'variableName',
...this.#customTags
};
templateVariableName: 'variableName'
}, this.customTags );
}
/**
@ -175,7 +178,7 @@ class CodeMirrorModeMediaWikiConfig {
* @return {Object<string>}
* @private
*/
get #customTags() {
get customTags() {
return {
em: 'mw-em',
error: 'mw-error',
@ -203,7 +206,7 @@ class CodeMirrorModeMediaWikiConfig {
/**
* These are custom tokens (a.k.a. tags) that aren't mapped to any of the standardized tags.
* Make sure these are also defined in #customTags() above.
* Make sure these are also defined in customTags() above.
*
* TODO: pass parent Tags in Tag.define() where appropriate for better theming.
*
@ -516,4 +519,6 @@ class CodeMirrorModeMediaWikiConfig {
* @member CodeMirrorModeMediaWikiConfig
* @type {CodeMirrorModeMediaWikiConfig}
*/
export const mwModeConfig = new CodeMirrorModeMediaWikiConfig();
const mwModeConfig = new CodeMirrorModeMediaWikiConfig();
module.exports = mwModeConfig;

View file

@ -1,5 +1,5 @@
import CodeMirror from './codemirror';
import mediaWikiLang from './codemirror.mode.mediawiki';
const CodeMirror = require( 'ext.CodeMirror.v6' );
const mediaWikiLang = require( 'ext.CodeMirror.v6.mode.mediawiki' );
const textarea = document.getElementById( 'wpTextbox1' );
const cm = new CodeMirror( textarea );

View file

@ -1,15 +1,15 @@
import {
const {
HighlightStyle,
LanguageSupport,
StreamLanguage,
StreamParser,
StringStream,
Tag,
syntaxHighlighting
} from '@codemirror/language';
import { mwModeConfig as modeConfig } from './codemirror.mode.mediawiki.config';
import { Tag } from '@lezer/highlight';
import templateFoldingExtension from './codemirror.templateFolding';
import bidiIsolationExtension from './codemirror.bidiIsolation';
} = require( 'ext.CodeMirror.v6.lib' );
const mwModeConfig = require( './codemirror.mediawiki.config.js' );
const bidiIsolationExtension = require( './codemirror.mediawiki.bidiIsolation.js' );
const templateFoldingExtension = require( './codemirror.mediawiki.templateFolding.js' );
/**
* MediaWiki language support for CodeMirror 6.
@ -47,11 +47,11 @@ class CodeMirrorModeMediaWiki {
this.oldStyle = null;
this.tokens = [];
this.oldTokens = [];
this.tokenTable = modeConfig.tokenTable;
this.tokenTable = mwModeConfig.tokenTable;
this.registerGroundTokens();
// Dynamically register any tags that aren't already in CodeMirrorModeMediaWikiConfig
Object.keys( this.config.tags ).forEach( ( tag ) => modeConfig.addTag( tag ) );
Object.keys( this.config.tags ).forEach( ( tag ) => mwModeConfig.addTag( tag ) );
}
/**
@ -94,7 +94,7 @@ class CodeMirrorModeMediaWiki {
'mw-template3-ext3-link-ground',
'mw-template3-ground',
'mw-template3-link-ground'
].forEach( ( ground ) => modeConfig.addToken( ground ) );
].forEach( ( ground ) => mwModeConfig.addToken( ground ) );
}
eatHtmlEntity( stream, style ) {
@ -109,7 +109,7 @@ class CodeMirrorModeMediaWiki {
ok = stream.eatWhile( /[\w.\-:]/ ) && stream.eat( ';' );
}
if ( ok ) {
return modeConfig.tags.htmlEntity;
return mwModeConfig.tags.htmlEntity;
}
return style;
}
@ -120,10 +120,10 @@ class CodeMirrorModeMediaWiki {
makeStyle( style, state, endGround ) {
if ( this.isBold || state.nDt > 0 ) {
style += ' ' + modeConfig.tags.strong;
style += ' ' + mwModeConfig.tags.strong;
}
if ( this.isItalic ) {
style += ' ' + modeConfig.tags.em;
style += ' ' + mwModeConfig.tags.em;
}
return this.makeLocalStyle( style, state, endGround );
}
@ -196,7 +196,7 @@ class CodeMirrorModeMediaWiki {
if ( stream.eat( char ) ) {
return this.makeLocalStyle( style, state );
}
return this.makeLocalStyle( modeConfig.tags.error, state );
return this.makeLocalStyle( mwModeConfig.tags.error, state );
};
}
@ -205,89 +205,89 @@ class CodeMirrorModeMediaWiki {
if ( stream.match( /^[^&<[{~]+/ ) ) {
if ( stream.eol() ) {
stream.backUp( count );
state.tokenize = this.eatEnd( modeConfig.tags.sectionHeader );
state.tokenize = this.eatEnd( mwModeConfig.tags.sectionHeader );
} else if ( stream.match( /^<!--(?!.*?-->.*?=)/, false ) ) {
// T171074: handle trailing comments
stream.backUp( count );
state.tokenize = this.eatBlock( modeConfig.tags.sectionHeader, '<!--', false );
state.tokenize = this.eatBlock( mwModeConfig.tags.sectionHeader, '<!--', false );
}
return modeConfig.tags.section; // style is null
return mwModeConfig.tags.section; // style is null
}
return this.eatWikiText( modeConfig.tags.section )( stream, state );
return this.eatWikiText( mwModeConfig.tags.section )( stream, state );
};
}
inVariable( stream, state ) {
if ( stream.match( /^[^{}|]+/ ) ) {
return this.makeLocalStyle( modeConfig.tags.templateVariableName, state );
return this.makeLocalStyle( mwModeConfig.tags.templateVariableName, state );
}
if ( stream.eat( '|' ) ) {
state.tokenize = this.inVariableDefault.bind( this );
return this.makeLocalStyle( modeConfig.tags.templateVariableDelimiter, state );
return this.makeLocalStyle( mwModeConfig.tags.templateVariableDelimiter, state );
}
if ( stream.match( '}}}' ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.templateVariableBracket, state );
return this.makeLocalStyle( mwModeConfig.tags.templateVariableBracket, state );
}
if ( stream.match( '{{{' ) ) {
state.stack.push( state.tokenize );
return this.makeLocalStyle( modeConfig.tags.templateVariableBracket, state );
return this.makeLocalStyle( mwModeConfig.tags.templateVariableBracket, state );
}
stream.next();
return this.makeLocalStyle( modeConfig.tags.templateVariableName, state );
return this.makeLocalStyle( mwModeConfig.tags.templateVariableName, state );
}
inVariableDefault( stream, state ) {
if ( stream.match( /^[^{}[<&~]+/ ) ) {
return this.makeLocalStyle( modeConfig.tags.templateVariable, state );
return this.makeLocalStyle( mwModeConfig.tags.templateVariable, state );
}
if ( stream.match( '}}}' ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.templateVariableBracket, state );
return this.makeLocalStyle( mwModeConfig.tags.templateVariableBracket, state );
}
return this.eatWikiText( modeConfig.tags.templateVariable )( stream, state );
return this.eatWikiText( mwModeConfig.tags.templateVariable )( stream, state );
}
inParserFunctionName( stream, state ) {
// FIXME: {{#name}} and {{uc}} are wrong, must have ':'
if ( stream.match( /^#?[^:}{~]+/ ) ) {
return this.makeLocalStyle( modeConfig.tags.parserFunctionName, state );
return this.makeLocalStyle( mwModeConfig.tags.parserFunctionName, state );
}
if ( stream.eat( ':' ) ) {
state.tokenize = this.inParserFunctionArguments.bind( this );
return this.makeLocalStyle( modeConfig.tags.parserFunctionDelimiter, state );
return this.makeLocalStyle( mwModeConfig.tags.parserFunctionDelimiter, state );
}
if ( stream.match( '}}' ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.parserFunctionBracket, state, 'nExt' );
return this.makeLocalStyle( mwModeConfig.tags.parserFunctionBracket, state, 'nExt' );
}
return this.eatWikiText( modeConfig.tags.parserFunction )( stream, state );
return this.eatWikiText( mwModeConfig.tags.parserFunction )( stream, state );
}
inParserFunctionArguments( stream, state ) {
if ( stream.match( /^[^|}{[<&~]+/ ) ) {
return this.makeLocalStyle( modeConfig.tags.parserFunction, state );
return this.makeLocalStyle( mwModeConfig.tags.parserFunction, state );
} else if ( stream.eat( '|' ) ) {
return this.makeLocalStyle( modeConfig.tags.parserFunctionDelimiter, state );
return this.makeLocalStyle( mwModeConfig.tags.parserFunctionDelimiter, state );
} else if ( stream.match( '}}' ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.parserFunctionBracket, state, 'nExt' );
return this.makeLocalStyle( mwModeConfig.tags.parserFunctionBracket, state, 'nExt' );
}
return this.eatWikiText( modeConfig.tags.parserFunction )( stream, state );
return this.eatWikiText( mwModeConfig.tags.parserFunction )( stream, state );
}
eatTemplatePageName( haveAte ) {
return ( stream, state ) => {
if ( stream.match( /^[\s\u00a0]*\|[\s\u00a0]*/ ) ) {
state.tokenize = this.eatTemplateArgument( true );
return this.makeLocalStyle( modeConfig.tags.templateDelimiter, state );
return this.makeLocalStyle( mwModeConfig.tags.templateDelimiter, state );
}
if ( stream.match( /^[\s\u00a0]*\}\}/ ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.templateBracket, state, 'nTemplate' );
return this.makeLocalStyle( mwModeConfig.tags.templateBracket, state, 'nTemplate' );
}
if ( stream.match( /^[\s\u00a0]*<!--.*?-->/ ) ) {
return this.makeLocalStyle( modeConfig.tags.comment, state );
return this.makeLocalStyle( mwModeConfig.tags.comment, state );
}
if ( haveAte && stream.sol() ) {
// @todo error message
@ -297,14 +297,14 @@ class CodeMirrorModeMediaWiki {
}
if ( stream.match( /^[\s\u00a0]*[^\s\u00a0|}<{&~]+/ ) ) {
state.tokenize = this.eatTemplatePageName( true );
return this.makeLocalStyle( modeConfig.tags.templateName, state );
return this.makeLocalStyle( mwModeConfig.tags.templateName, state );
} else if ( stream.eatSpace() ) {
if ( stream.eol() === true ) {
return this.makeLocalStyle( modeConfig.tags.templateName, state );
return this.makeLocalStyle( mwModeConfig.tags.templateName, state );
}
return this.makeLocalStyle( modeConfig.tags.templateName, state );
return this.makeLocalStyle( mwModeConfig.tags.templateName, state );
}
return this.eatWikiText( modeConfig.tags.templateName )( stream, state );
return this.eatWikiText( mwModeConfig.tags.templateName )( stream, state );
};
}
@ -313,19 +313,19 @@ class CodeMirrorModeMediaWiki {
if ( expectArgName && stream.eatWhile( /[^=|}{[<&~]/ ) ) {
if ( stream.eat( '=' ) ) {
state.tokenize = this.eatTemplateArgument( false );
return this.makeLocalStyle( modeConfig.tags.templateArgumentName, state );
return this.makeLocalStyle( mwModeConfig.tags.templateArgumentName, state );
}
return this.makeLocalStyle( modeConfig.tags.template, state );
return this.makeLocalStyle( mwModeConfig.tags.template, state );
} else if ( stream.eatWhile( /[^|}{[<&~]/ ) ) {
return this.makeLocalStyle( modeConfig.tags.template, state );
return this.makeLocalStyle( mwModeConfig.tags.template, state );
} else if ( stream.eat( '|' ) ) {
state.tokenize = this.eatTemplateArgument( true );
return this.makeLocalStyle( modeConfig.tags.templateDelimiter, state );
return this.makeLocalStyle( mwModeConfig.tags.templateDelimiter, state );
} else if ( stream.match( '}}' ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.templateBracket, state, 'nTemplate' );
return this.makeLocalStyle( mwModeConfig.tags.templateBracket, state, 'nTemplate' );
}
return this.eatWikiText( modeConfig.tags.template )( stream, state );
return this.eatWikiText( mwModeConfig.tags.template )( stream, state );
};
}
@ -342,7 +342,7 @@ class CodeMirrorModeMediaWiki {
} else {
state.tokenize = this.inExternalLink.bind( this );
}
return this.makeLocalStyle( modeConfig.tags.extLinkProtocol, state );
return this.makeLocalStyle( mwModeConfig.tags.extLinkProtocol, state );
};
}
@ -355,7 +355,7 @@ class CodeMirrorModeMediaWiki {
}
if ( stream.match( /^[\s\u00a0]*\]/ ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.extLinkBracket, state, 'nLink' );
return this.makeLocalStyle( mwModeConfig.tags.extLinkBracket, state, 'nLink' );
}
if ( stream.eatSpace() ) {
state.tokenize = this.inExternalLinkText.bind( this );
@ -369,9 +369,9 @@ class CodeMirrorModeMediaWiki {
stream.next();
}
}
return this.makeStyle( modeConfig.tags.extLink, state );
return this.makeStyle( mwModeConfig.tags.extLink, state );
}
return this.eatWikiText( modeConfig.tags.extLink )( stream, state );
return this.eatWikiText( mwModeConfig.tags.extLink )( stream, state );
}
inExternalLinkText( stream, state ) {
@ -383,12 +383,12 @@ class CodeMirrorModeMediaWiki {
}
if ( stream.eat( ']' ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.extLinkBracket, state, 'nLink' );
return this.makeLocalStyle( mwModeConfig.tags.extLinkBracket, state, 'nLink' );
}
if ( stream.match( /^[^'\]{&~<]+/ ) ) {
return this.makeStyle( modeConfig.tags.extLinkText, state );
return this.makeStyle( mwModeConfig.tags.extLinkText, state );
}
return this.eatWikiText( modeConfig.tags.extLinkText )( stream, state );
return this.eatWikiText( mwModeConfig.tags.extLinkText )( stream, state );
}
inLink( stream, state ) {
@ -400,24 +400,24 @@ class CodeMirrorModeMediaWiki {
}
if ( stream.match( /^[\s\u00a0]*#[\s\u00a0]*/ ) ) {
state.tokenize = this.inLinkToSection.bind( this );
return this.makeLocalStyle( modeConfig.tags.link, state );
return this.makeLocalStyle( mwModeConfig.tags.link, state );
}
if ( stream.match( /^[\s\u00a0]*\|[\s\u00a0]*/ ) ) {
state.tokenize = this.eatLinkText();
return this.makeLocalStyle( modeConfig.tags.linkDelimiter, state );
return this.makeLocalStyle( mwModeConfig.tags.linkDelimiter, state );
}
if ( stream.match( /^[\s\u00a0]*\]\]/ ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.linkBracket, state, 'nLink' );
return this.makeLocalStyle( mwModeConfig.tags.linkBracket, state, 'nLink' );
}
if ( stream.match( /^[\s\u00a0]*[^\s\u00a0#|\]&~{]+/ ) || stream.eatSpace() ) {
return this.makeStyle(
`${ modeConfig.tags.linkPageName } ${ modeConfig.tags.pageName }`,
`${ mwModeConfig.tags.linkPageName } ${ mwModeConfig.tags.pageName }`,
state
);
}
return this.eatWikiText(
`${ modeConfig.tags.linkPageName } ${ modeConfig.tags.pageName }`
`${ mwModeConfig.tags.linkPageName } ${ mwModeConfig.tags.pageName }`
)( stream, state );
}
@ -430,17 +430,17 @@ class CodeMirrorModeMediaWiki {
}
// FIXME '{{' breaks links, example: [[z{{page]]
if ( stream.match( /^[^|\]&~{}]+/ ) ) {
return this.makeLocalStyle( modeConfig.tags.linkToSection, state );
return this.makeLocalStyle( mwModeConfig.tags.linkToSection, state );
}
if ( stream.eat( '|' ) ) {
state.tokenize = this.eatLinkText();
return this.makeLocalStyle( modeConfig.tags.linkDelimiter, state );
return this.makeLocalStyle( mwModeConfig.tags.linkDelimiter, state );
}
if ( stream.match( ']]' ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.linkBracket, state, 'nLink' );
return this.makeLocalStyle( mwModeConfig.tags.linkBracket, state, 'nLink' );
}
return this.eatWikiText( modeConfig.tags.linkToSection )( stream, state );
return this.eatWikiText( mwModeConfig.tags.linkToSection )( stream, state );
}
eatLinkText() {
@ -449,28 +449,28 @@ class CodeMirrorModeMediaWiki {
let tmpstyle;
if ( stream.match( ']]' ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.linkBracket, state, 'nLink' );
return this.makeLocalStyle( mwModeConfig.tags.linkBracket, state, 'nLink' );
}
if ( stream.match( '\'\'\'' ) ) {
linkIsBold = !linkIsBold;
return this.makeLocalStyle(
`${ modeConfig.tags.linkText } ${ modeConfig.tags.apostrophes }`,
`${ mwModeConfig.tags.linkText } ${ mwModeConfig.tags.apostrophes }`,
state
);
}
if ( stream.match( '\'\'' ) ) {
linkIsItalic = !linkIsItalic;
return this.makeLocalStyle(
`${ modeConfig.tags.linkText } ${ modeConfig.tags.apostrophes }`,
`${ mwModeConfig.tags.linkText } ${ mwModeConfig.tags.apostrophes }`,
state
);
}
tmpstyle = modeConfig.tags.linkText;
tmpstyle = mwModeConfig.tags.linkText;
if ( linkIsBold ) {
tmpstyle += ' ' + modeConfig.tags.strong;
tmpstyle += ' ' + mwModeConfig.tags.strong;
}
if ( linkIsItalic ) {
tmpstyle += ' ' + modeConfig.tags.em;
tmpstyle += ' ' + mwModeConfig.tags.em;
}
if ( stream.match( /^[^'\]{&~<]+/ ) ) {
return this.makeStyle( tmpstyle, state );
@ -490,23 +490,23 @@ class CodeMirrorModeMediaWiki {
name = name.toLowerCase();
if ( isHtmlTag ) {
if ( isCloseTag && !modeConfig.implicitlyClosedHtmlTags[ name ] ) {
state.tokenize = this.eatChar( '>', modeConfig.tags.htmlTagBracket );
if ( isCloseTag && !mwModeConfig.implicitlyClosedHtmlTags[ name ] ) {
state.tokenize = this.eatChar( '>', mwModeConfig.tags.htmlTagBracket );
} else {
state.tokenize = this.eatHtmlTagAttribute( name );
}
return this.makeLocalStyle( modeConfig.tags.htmlTagName, state );
return this.makeLocalStyle( mwModeConfig.tags.htmlTagName, state );
}
// it is the extension tag
if ( isCloseTag ) {
state.tokenize = this.eatChar(
'>',
`${ modeConfig.tags.extTagBracket } mw-ext-${ name }`
`${ mwModeConfig.tags.extTagBracket } mw-ext-${ name }`
);
} else {
state.tokenize = this.eatExtTagAttribute( name );
}
return this.makeLocalStyle( `${ modeConfig.tags.extTagName } mw-ext-${ name }`, state );
return this.makeLocalStyle( `${ mwModeConfig.tags.extTagName } mw-ext-${ name }`, state );
};
}
@ -514,20 +514,20 @@ class CodeMirrorModeMediaWiki {
return ( stream, state ) => {
if ( stream.match( /^(?:"[^<">]*"|'[^<'>]*'|[^>/<{&~])+/ ) ) {
return this.makeLocalStyle( modeConfig.tags.htmlTagAttribute, state );
return this.makeLocalStyle( mwModeConfig.tags.htmlTagAttribute, state );
}
if ( stream.eat( '>' ) ) {
if ( !( name in modeConfig.implicitlyClosedHtmlTags ) ) {
if ( !( name in mwModeConfig.implicitlyClosedHtmlTags ) ) {
state.inHtmlTag.push( name );
}
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.htmlTagBracket, state );
return this.makeLocalStyle( mwModeConfig.tags.htmlTagBracket, state );
}
if ( stream.match( '/>' ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.htmlTagBracket, state );
return this.makeLocalStyle( mwModeConfig.tags.htmlTagBracket, state );
}
return this.eatWikiText( modeConfig.tags.htmlTagAttribute )( stream, state );
return this.eatWikiText( mwModeConfig.tags.htmlTagAttribute )( stream, state );
};
}
@ -546,7 +546,7 @@ class CodeMirrorModeMediaWiki {
return ( stream, state ) => {
if ( stream.match( /^(?:"[^">]*"|'[^'>]*'|[^>/<{&~])+/ ) ) {
return this.makeLocalStyle( `${ modeConfig.tags.extTagAttribute } mw-ext-${ name }`, state );
return this.makeLocalStyle( `${ mwModeConfig.tags.extTagAttribute } mw-ext-${ name }`, state );
}
if ( stream.eat( '>' ) ) {
state.extName = name;
@ -570,13 +570,13 @@ class CodeMirrorModeMediaWiki {
}
state.tokenize = this.eatExtTagArea( name );
return this.makeLocalStyle( `${ modeConfig.tags.extTagBracket } mw-ext-${ name }`, state );
return this.makeLocalStyle( `${ mwModeConfig.tags.extTagBracket } mw-ext-${ name }`, state );
}
if ( stream.match( '/>' ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( `${ modeConfig.tags.extTagBracket } mw-ext-${ name }`, state );
return this.makeLocalStyle( `${ mwModeConfig.tags.extTagBracket } mw-ext-${ name }`, state );
}
return this.eatWikiText( `${ modeConfig.tags.extTagAttribute } mw-ext-${ name }` )( stream, state );
return this.eatWikiText( `${ mwModeConfig.tags.extTagAttribute } mw-ext-${ name }` )( stream, state );
};
}
@ -615,7 +615,7 @@ class CodeMirrorModeMediaWiki {
stream.next(); // eat <
stream.next(); // eat /
state.tokenize = this.eatTagName( name.length, true, false );
return this.makeLocalStyle( `${ modeConfig.tags.extTagBracket } mw-ext-${ name }`, state );
return this.makeLocalStyle( `${ mwModeConfig.tags.extTagBracket } mw-ext-${ name }`, state );
};
}
@ -623,7 +623,7 @@ class CodeMirrorModeMediaWiki {
return ( stream, state ) => {
let ret;
if ( state.extMode === false ) {
ret = modeConfig.tags.extTag;
ret = mwModeConfig.tags.extTag;
stream.skipToEnd();
} else {
ret = `mw-tag-${ state.extName } ` +
@ -643,7 +643,7 @@ class CodeMirrorModeMediaWiki {
stream.match( '{|' );
stream.eatSpace();
state.tokenize = this.inTableDefinition.bind( this );
return modeConfig.tags.tableBracket;
return mwModeConfig.tags.tableBracket;
}
inTableDefinition( stream, state ) {
@ -651,7 +651,7 @@ class CodeMirrorModeMediaWiki {
state.tokenize = this.inTable.bind( this );
return this.inTable( stream, state );
}
return this.eatWikiText( modeConfig.tags.tableDefinition )( stream, state );
return this.eatWikiText( mwModeConfig.tags.tableDefinition )( stream, state );
}
inTable( stream, state ) {
@ -661,25 +661,25 @@ class CodeMirrorModeMediaWiki {
if ( stream.eat( '-' ) ) {
stream.eatSpace();
state.tokenize = this.inTableDefinition.bind( this );
return this.makeLocalStyle( modeConfig.tags.tableDelimiter, state );
return this.makeLocalStyle( mwModeConfig.tags.tableDelimiter, state );
}
if ( stream.eat( '+' ) ) {
stream.eatSpace();
state.tokenize = this.eatTableRow( true, false, true );
return this.makeLocalStyle( modeConfig.tags.tableDelimiter, state );
return this.makeLocalStyle( mwModeConfig.tags.tableDelimiter, state );
}
if ( stream.eat( '}' ) ) {
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.tableBracket, state );
return this.makeLocalStyle( mwModeConfig.tags.tableBracket, state );
}
stream.eatSpace();
state.tokenize = this.eatTableRow( true, false );
return this.makeLocalStyle( modeConfig.tags.tableDelimiter, state );
return this.makeLocalStyle( mwModeConfig.tags.tableDelimiter, state );
}
if ( stream.eat( '!' ) ) {
stream.eatSpace();
state.tokenize = this.eatTableRow( true, true );
return this.makeLocalStyle( modeConfig.tags.tableDelimiter, state );
return this.makeLocalStyle( mwModeConfig.tags.tableDelimiter, state );
}
}
return this.eatWikiText( '' )( stream, state );
@ -689,9 +689,9 @@ class CodeMirrorModeMediaWiki {
eatTableRow( isStart, isHead, isCaption ) {
let tag = '';
if ( isCaption ) {
tag = modeConfig.tags.tableCaption;
tag = mwModeConfig.tags.tableCaption;
} else if ( isHead ) {
tag = modeConfig.tags.strong;
tag = mwModeConfig.tags.strong;
}
return ( stream, state ) => {
if ( stream.sol() ) {
@ -707,11 +707,11 @@ class CodeMirrorModeMediaWiki {
this.isBold = false;
this.isItalic = false;
state.tokenize = this.eatTableRow( true, isHead, isCaption );
return this.makeLocalStyle( modeConfig.tags.tableDelimiter, state );
return this.makeLocalStyle( mwModeConfig.tags.tableDelimiter, state );
}
if ( isStart && stream.eat( '|' ) ) {
state.tokenize = this.eatTableRow( false, isHead, isCaption );
return this.makeLocalStyle( modeConfig.tags.tableDelimiter, state );
return this.makeLocalStyle( mwModeConfig.tags.tableDelimiter, state );
}
}
return this.eatWikiText( tag )( stream, state );
@ -721,7 +721,7 @@ class CodeMirrorModeMediaWiki {
eatFreeExternalLinkProtocol( stream, state ) {
stream.match( this.urlProtocols );
state.tokenize = this.eatFreeExternalLink.bind( this );
return this.makeLocalStyle( modeConfig.tags.freeExtLinkProtocol, state );
return this.makeLocalStyle( mwModeConfig.tags.freeExtLinkProtocol, state );
}
eatFreeExternalLink( stream, state ) {
@ -731,24 +731,24 @@ class CodeMirrorModeMediaWiki {
if ( stream.peek() === '~' ) {
if ( !stream.match( /^~~~+/, false ) ) {
stream.match( /^~*/ );
return this.makeLocalStyle( modeConfig.tags.freeExtLink, state );
return this.makeLocalStyle( mwModeConfig.tags.freeExtLink, state );
}
} else if ( stream.peek() === '{' ) {
if ( !stream.match( '{{', false ) ) {
stream.next();
return this.makeLocalStyle( modeConfig.tags.freeExtLink, state );
return this.makeLocalStyle( mwModeConfig.tags.freeExtLink, state );
}
} else if ( stream.peek() === '\'' ) {
if ( !stream.match( '\'\'', false ) ) {
stream.next();
return this.makeLocalStyle( modeConfig.tags.freeExtLink, state );
return this.makeLocalStyle( mwModeConfig.tags.freeExtLink, state );
}
} else if ( stream.match( /^[).,]+(?=[^\s\u00a0{[\]<>~).,])/ ) ) {
return this.makeLocalStyle( modeConfig.tags.freeExtLink, state );
return this.makeLocalStyle( mwModeConfig.tags.freeExtLink, state );
}
}
state.tokenize = state.stack.pop();
return this.makeLocalStyle( modeConfig.tags.freeExtLink, state );
return this.makeLocalStyle( mwModeConfig.tags.freeExtLink, state );
}
eatList( stream, state ) {
@ -757,7 +757,7 @@ class CodeMirrorModeMediaWiki {
if ( mt && !this.isNested( state ) && mt[ 0 ].includes( ';' ) ) {
state.nDt += mt[ 0 ].split( ';' ).length - 1;
}
return this.makeLocalStyle( modeConfig.tags.list, state );
return this.makeLocalStyle( mwModeConfig.tags.list, state );
}
/**
@ -781,13 +781,13 @@ class CodeMirrorModeMediaWiki {
if ( !stream.match( '//', false ) && stream.match( this.urlProtocols ) ) {
state.stack.push( state.tokenize );
state.tokenize = this.eatFreeExternalLink.bind( this );
return this.makeLocalStyle( modeConfig.tags.freeExtLinkProtocol, state );
return this.makeLocalStyle( mwModeConfig.tags.freeExtLinkProtocol, state );
}
ch = stream.next();
switch ( ch ) {
case '-':
if ( stream.match( /^---+/ ) ) {
return modeConfig.tags.hr;
return mwModeConfig.tags.hr;
}
break;
case '=':
@ -798,7 +798,7 @@ class CodeMirrorModeMediaWiki {
stream.backUp( tmp[ 2 ].length );
state.stack.push( state.tokenize );
state.tokenize = this.eatSectionHeader( tmp[ 3 ].length );
return modeConfig.tags.sectionHeader + ' ' +
return mwModeConfig.tags.sectionHeader + ' ' +
/**
* Tokens used here include:
* - cm-mw-section-1
@ -808,12 +808,12 @@ class CodeMirrorModeMediaWiki {
* - cm-mw-section-5
* - cm-mw-section-6
*/
modeConfig.tags[ `sectionHeader${ tmp[ 1 ].length + 1 }` ];
mwModeConfig.tags[ `sectionHeader${ tmp[ 1 ].length + 1 }` ];
}
break;
case ';':
stream.backUp( 1 );
// fall through
// fall through
case '*':
case '#':
return this.eatList( stream, state );
@ -831,11 +831,11 @@ class CodeMirrorModeMediaWiki {
if ( stream.match( /^:+/ ) ) { // ::{|
state.stack.push( state.tokenize );
state.tokenize = this.eatStartTable.bind( this );
return modeConfig.tags.indenting;
return mwModeConfig.tags.indenting;
}
stream.eat( '{' );
} else {
return modeConfig.tags.skipFormatting;
return mwModeConfig.tags.skipFormatting;
}
// break is not necessary here
// falls through
@ -844,7 +844,7 @@ class CodeMirrorModeMediaWiki {
stream.eatSpace();
state.stack.push( state.tokenize );
state.tokenize = this.inTableDefinition.bind( this );
return modeConfig.tags.tableBracket;
return mwModeConfig.tags.tableBracket;
}
}
} else {
@ -867,10 +867,10 @@ class CodeMirrorModeMediaWiki {
this.prepareItalicForCorrection( stream );
}
this.isBold = !this.isBold;
return this.makeLocalStyle( modeConfig.tags.apostrophesBold, state );
return this.makeLocalStyle( mwModeConfig.tags.apostrophesBold, state );
} else if ( stream.eat( '\'' ) ) { // italic
this.isItalic = !this.isItalic;
return this.makeLocalStyle( modeConfig.tags.apostrophesItalic, state );
return this.makeLocalStyle( mwModeConfig.tags.apostrophesItalic, state );
}
break;
case '[':
@ -880,7 +880,7 @@ class CodeMirrorModeMediaWiki {
state.nLink++;
state.stack.push( state.tokenize );
state.tokenize = this.inLink.bind( this );
return this.makeLocalStyle( modeConfig.tags.linkBracket, state );
return this.makeLocalStyle( mwModeConfig.tags.linkBracket, state );
}
} else {
mt = stream.match( this.urlProtocols );
@ -889,7 +889,7 @@ class CodeMirrorModeMediaWiki {
stream.backUp( mt[ 0 ].length );
state.stack.push( state.tokenize );
state.tokenize = this.eatExternalLinkProtocol( mt[ 0 ].length );
return this.makeLocalStyle( modeConfig.tags.extLinkBracket, state );
return this.makeLocalStyle( mwModeConfig.tags.extLinkBracket, state );
}
}
break;
@ -901,7 +901,7 @@ class CodeMirrorModeMediaWiki {
state.stack.push( state.tokenize );
state.tokenize = this.inVariable.bind( this );
return this.makeLocalStyle(
modeConfig.tags.templateVariableBracket,
mwModeConfig.tags.templateVariableBracket,
state
);
} else if ( stream.match( /^{(?!{(?!{))[\s\u00a0]*/ ) ) {
@ -911,7 +911,7 @@ class CodeMirrorModeMediaWiki {
state.stack.push( state.tokenize );
state.tokenize = this.inParserFunctionName.bind( this );
return this.makeLocalStyle(
modeConfig.tags.parserFunctionBracket,
mwModeConfig.tags.parserFunctionBracket,
state
);
}
@ -931,7 +931,7 @@ class CodeMirrorModeMediaWiki {
state.stack.push( state.tokenize );
state.tokenize = this.inParserFunctionName.bind( this );
return this.makeLocalStyle(
modeConfig.tags.parserFunctionBracket,
mwModeConfig.tags.parserFunctionBracket,
state
);
}
@ -940,39 +940,39 @@ class CodeMirrorModeMediaWiki {
state.nTemplate++;
state.stack.push( state.tokenize );
state.tokenize = this.eatTemplatePageName( false );
return this.makeLocalStyle( modeConfig.tags.templateBracket, state );
return this.makeLocalStyle( mwModeConfig.tags.templateBracket, state );
}
break;
case '<':
isCloseTag = !!stream.eat( '/' );
tagname = stream.match( /^[^>/\s\u00a0.*,[\]{}$^+?|/\\'`~<=!@#%&()-]+/ );
if ( stream.match( '!--' ) ) { // comment
return chain( this.eatBlock( modeConfig.tags.comment, '-->' ) );
return chain( this.eatBlock( mwModeConfig.tags.comment, '-->' ) );
}
if ( tagname ) {
tagname = tagname[ 0 ].toLowerCase();
if ( tagname in this.config.tags ) {
// Parser function
if ( isCloseTag === true ) {
return modeConfig.tags.error;
return mwModeConfig.tags.error;
}
stream.backUp( tagname.length );
state.stack.push( state.tokenize );
state.tokenize = this.eatTagName( tagname.length, isCloseTag, false );
return this.makeLocalStyle( `${ modeConfig.tags.extTagBracket } mw-ext-${ tagname }`, state );
return this.makeLocalStyle( `${ mwModeConfig.tags.extTagBracket } mw-ext-${ tagname }`, state );
}
if ( tagname in modeConfig.permittedHtmlTags ) {
if ( tagname in mwModeConfig.permittedHtmlTags ) {
// Html tag
if ( isCloseTag === true && tagname !== state.inHtmlTag.pop() ) {
// Increment position so that the closing '>' gets highlighted red.
stream.pos++;
return modeConfig.tags.error;
return mwModeConfig.tags.error;
}
if (
isCloseTag === true &&
tagname in modeConfig.implicitlyClosedHtmlTags
tagname in mwModeConfig.implicitlyClosedHtmlTags
) {
return modeConfig.tags.error;
return mwModeConfig.tags.error;
}
stream.backUp( tagname.length );
state.stack.push( state.tokenize );
@ -980,17 +980,17 @@ class CodeMirrorModeMediaWiki {
tagname.length,
// Opening void tags should also be treated as the closing tag.
isCloseTag ||
( tagname in modeConfig.implicitlyClosedHtmlTags ),
( tagname in mwModeConfig.implicitlyClosedHtmlTags ),
true
);
return this.makeLocalStyle( modeConfig.tags.htmlTagBracket, state );
return this.makeLocalStyle( mwModeConfig.tags.htmlTagBracket, state );
}
stream.backUp( tagname.length );
}
break;
case '~':
if ( stream.match( /^~{2,4}/ ) ) {
return modeConfig.tags.signature;
return mwModeConfig.tags.signature;
}
break;
// Maybe double underscored Magic Word such as __TOC__
@ -1008,7 +1008,7 @@ class CodeMirrorModeMediaWiki {
}
// Optimization: skip regex function for EOL and backup-ed symbols
return this.makeStyle( style, state );
// Check on double underscore Magic Word
// Check on double underscore Magic Word
} else if ( tmp === 2 ) {
// The same as the end of function except '_' inside and '__' at the end.
name = stream.match( /^([^\s\u00a0>}[\]<{'|&:~]+?)__/ );
@ -1017,7 +1017,7 @@ class CodeMirrorModeMediaWiki {
'__' + name[ 0 ].toLowerCase() in this.config.doubleUnderscore[ 0 ] ||
'__' + name[ 0 ] in this.config.doubleUnderscore[ 1 ]
) {
return modeConfig.tags.doubleUnderscore;
return mwModeConfig.tags.doubleUnderscore;
}
if ( !stream.eol() ) {
// Two underscore symbols at the end can be the
@ -1032,7 +1032,7 @@ class CodeMirrorModeMediaWiki {
case ':':
if ( state.nDt > 0 && !this.isNested( state ) ) {
state.nDt--;
return modeConfig.tags.indenting;
return mwModeConfig.tags.indenting;
}
break;
default:
@ -1266,14 +1266,14 @@ class CodeMirrorModeMediaWiki {
* @return {LanguageSupport}
* @stable to call
*/
export default ( config = { bidiIsolation: false }, mwConfig = null ) => {
const mediaWikiLang = ( config = { bidiIsolation: false }, mwConfig = null ) => {
mwConfig = mwConfig || mw.config.get( 'extCodeMirrorConfig' );
const mode = new CodeMirrorModeMediaWiki( mwConfig );
const parser = mode.mediawiki;
const lang = StreamLanguage.define( parser );
const langExtension = [ syntaxHighlighting(
HighlightStyle.define(
modeConfig.getTagStyles( parser )
mwModeConfig.getTagStyles( parser )
)
) ];
@ -1292,3 +1292,5 @@ export default ( config = { bidiIsolation: false }, mwConfig = null ) => {
return new LanguageSupport( lang, langExtension );
};
module.exports = mediaWikiLang;

View file

@ -1,8 +1,22 @@
import { showTooltip, keymap, Tooltip, KeyBinding } from '@codemirror/view';
import { StateField, Extension, EditorState } from '@codemirror/state';
import { foldEffect, syntaxTree, ensureSyntaxTree, foldedRanges, unfoldAll, unfoldEffect, codeFolding } from '@codemirror/language';
import { SyntaxNode, Tree } from '@lezer/common';
import { mwModeConfig as modeConfig } from './codemirror.mode.mediawiki.config';
const {
EditorState,
Extension,
KeyBinding,
StateField,
SyntaxNode,
Tree,
Tooltip,
codeFolding,
ensureSyntaxTree,
foldEffect,
foldedRanges,
keymap,
showTooltip,
syntaxTree,
unfoldAll,
unfoldEffect
} = require( 'ext.CodeMirror.v6.lib' );
const modeConfig = require( './codemirror.mediawiki.config.js' );
/**
* Check if a SyntaxNode is a template bracket (`{{` or `}}`)
@ -280,7 +294,7 @@ const foldKeymap = [
* @module CodeMirrorTemplateFolding
* @type {Extension}
*/
export default [
const templateFoldingExtension = [
codeFolding( {
placeholderDOM( view ) {
const element = document.createElement( 'span' );
@ -314,3 +328,5 @@ export default [
} ),
keymap.of( foldKeymap )
];
module.exports = templateFoldingExtension;

View file

@ -1,5 +1,4 @@
import { EditorView } from '@codemirror/view';
import { EditorSelection } from '@codemirror/state';
const { EditorSelection, EditorView } = require( 'ext.CodeMirror.v6.lib' );
/**
* [jQuery.textSelection]{@link jQuery.fn.textSelection} implementation for CodeMirror.
@ -230,4 +229,4 @@ class CodeMirrorTextSelection {
}
}
export default CodeMirrorTextSelection;
module.exports = CodeMirrorTextSelection;

View file

@ -1,7 +1,10 @@
import CodeMirror from './codemirror';
import { EditorSelection, Extension } from '@codemirror/state';
import { EditorView } from '@codemirror/view';
import { LanguageSupport } from '@codemirror/language';
const {
EditorSelection,
EditorView,
Extension,
LanguageSupport
} = require( 'ext.CodeMirror.v6.lib' );
const CodeMirror = require( 'ext.CodeMirror.v6' );
/**
* CodeMirror integration with
@ -242,4 +245,4 @@ class CodeMirrorWikiEditor extends CodeMirror {
}
}
export default CodeMirrorWikiEditor;
module.exports = CodeMirrorWikiEditor;

View file

@ -1,5 +1,5 @@
import CodeMirrorWikiEditor from './codemirror.wikieditor';
import mediaWikiLang from './codemirror.mode.mediawiki';
const CodeMirrorWikiEditor = require( 'ext.CodeMirror.v6.WikiEditor' );
const mediaWikiLang = require( 'ext.CodeMirror.v6.mode.mediawiki' );
// TODO: remove URL feature flag once bidi isolation is more stable.
const urlParams = new URLSearchParams( window.location.search );

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
"use strict";var e=require("ext.CodeMirror.v6"),i=require("ext.CodeMirror.v6.mode.mediawiki");require("ext.CodeMirror.v6.lib");var r=new e(document.getElementById("wpTextbox1")),o=new URLSearchParams(window.location.search);r.initialize([r.defaultExtensions,i({bidiIsolation:o.get("cm6bidi")})]);

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
"use strict";var e=require("ext.CodeMirror.v6.lib"),t=function(t){function r(t,i){var o;return e._classCallCheck(this,r),(o=e._callSuper(this,r,[t])).langExtension=i,o.useCodeMirror=mw.user.options.get("usecodemirror")>0,o.realtimePreviewHandler=null,o}return e._inherits(r,t),e._createClass(r,[{key:"setCodeMirrorPreference",value:function(t){this.useCodeMirror=t,e._get(e._getPrototypeOf(r.prototype),"setCodeMirrorPreference",this).call(this,t)}},{key:"enableCodeMirror",value:function(){var t=this;if(!this.view){var r=this.$textarea.prop("selectionStart"),i=this.$textarea.prop("selectionEnd"),o=this.$textarea.scrollTop(),a=this.$textarea.is(":focus"),s=[this.defaultExtensions,this.langExtension,e.EditorView.updateListener.of((function(e){e.docChanged&&"function"==typeof t.realtimePreviewHandler&&t.realtimePreviewHandler()}))];if(this.initialize(s),this.addRealtimePreviewHandler(),requestAnimationFrame((function(){t.view.scrollDOM.scrollTop=o})),0!==r||0!==i){var n=e.EditorSelection.range(r,i),d=e.EditorView.scrollIntoView(n);d.value.isSnapshot=!0,this.view.dispatch({selection:e.EditorSelection.create([n]),effects:d})}a&&this.view.focus(),mw.hook("ext.CodeMirror.switch").fire(!0,$(this.view.dom))}}},{key:"addRealtimePreviewHandler",value:function(){var e=this;mw.hook("ext.WikiEditor.realtimepreview.enable").add((function(t){e.realtimePreviewHandler=t.getEventHandler().bind(t)})),mw.hook("ext.WikiEditor.realtimepreview.disable").add((function(){e.realtimePreviewHandler=null}))}},{key:"addCodeMirrorToWikiEditor",value:function(){var e=this,t=this.$textarea.data("wikiEditor-context"),r=t&&t.modules&&t.modules.toolbar;r&&(this.$textarea.wikiEditor("addToToolbar",{section:"main",groups:{codemirror:{tools:{CodeMirror:{label:mw.msg("codemirror-toggle-label"),type:"toggle",oouiIcon:"highlight",action:{type:"callback",execute:function(){return e.switchCodeMirror()}}}}}}}),r.$toolbar.find(".tool[rel=CodeMirror]").attr("id","mw-editbutton-codemirror"),this.readOnly&&this.$textarea.data("wikiEditor-context").$ui.addClass("ext-codemirror-readonly"),this.useCodeMirror&&this.enableCodeMirror(),this.updateToolbarButton(),this.logUsage({editor:"wikitext",enabled:this.useCodeMirror,toggled:!1,edit_start_ts_ms:1e3*parseInt($('input[name="wpStarttime"]').val(),10)||0}))}},{key:"updateToolbarButton",value:function(){var e=$("#mw-editbutton-codemirror");e.toggleClass("mw-editbutton-codemirror-active",this.useCodeMirror),e.data("setActive")&&e.data("setActive")(this.useCodeMirror)}},{key:"switchCodeMirror",value:function(){this.view?(this.setCodeMirrorPreference(!1),this.destroy(),mw.hook("ext.CodeMirror.switch").fire(!1,this.$textarea)):(this.enableCodeMirror(),this.setCodeMirrorPreference(!0)),this.updateToolbarButton(),this.logUsage({editor:"wikitext",enabled:this.useCodeMirror,toggled:!0,edit_start_ts_ms:1e3*parseInt($('input[name="wpStarttime"]').val(),10)||0})}}]),r}(require("ext.CodeMirror.v6"));module.exports=t;

View file

@ -1 +0,0 @@
"use strict";var r=require("ext.CodeMirror.v6.WikiEditor"),i=require("ext.CodeMirror.v6.mode.mediawiki");require("ext.CodeMirror.v6.lib"),require("ext.CodeMirror.v6");var e=new URLSearchParams(window.location.search);mw.loader.getState("ext.wikiEditor")&&mw.hook("wikiEditor.toolbarReady").add((function(o){new r(o,i({bidiIsolation:"rtl"===o.attr("dir")&&e.get("cm6bidi")})).addCodeMirrorToWikiEditor()}));

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,29 @@
{
"root": true,
"extends": [
"wikimedia/client",
"wikimedia/jquery",
"wikimedia/mediawiki"
],
"parserOptions": {
"sourceType": "commonjs"
},
"globals": {
"CodeMirror": "readonly"
},
"rules": {
"max-len": "off",
"es-x/no-object-assign": "warn"
},
"overrides": [
{
"files": [
"addon/*.js"
],
"rules": {
"computed-property-spacing": [ "error", "never" ],
"indent": [ "error", 2 ]
}
}
]
}

View file

@ -146,7 +146,6 @@ function init() {
*/
function enableCodeMirror() {
const config = mw.config.get( 'extCodeMirrorConfig' );
mw.loader.using( codeMirrorCoreModules.concat( config.pluginModules ), () => {
const selectionStart = $textbox1.prop( 'selectionStart' ),
selectionEnd = $textbox1.prop( 'selectionEnd' ),

File diff suppressed because it is too large Load diff

View file

@ -1,99 +1,18 @@
'use strict';
const nodeResolve = require( '@rollup/plugin-node-resolve' );
const copy = require( 'rollup-plugin-copy' );
const babel = require( '@rollup/plugin-babel' );
const terser = require( '@rollup/plugin-terser' );
const isProduction = process.env.BUILD === 'production';
/**
* Mapping of import paths to ResourceLoader module names.
* See usage in 'plugins' below for explanation.
*
* @type {Object}
*/
const importAliases = {
'./vendor.js': 'ext.CodeMirror.v6.lib',
'./codemirror.js': 'ext.CodeMirror.v6',
'./codemirror.wikieditor.js': 'ext.CodeMirror.v6.WikiEditor',
'./codemirror.mode.mediawiki.js': 'ext.CodeMirror.v6.mode.mediawiki'
};
module.exports = [
{
// One entry for each ResourceLoader module that we want to ship.
input: [
'src/codemirror.js',
'src/codemirror.mode.mediawiki.js',
'src/codemirror.mediawiki.js',
'src/codemirror.wikieditor.js',
'src/codemirror.wikieditor.mediawiki.js'
],
input: 'resources/codemirror.bundle.js',
output: {
entryFileNames: '[name].js',
dir: 'resources/dist',
// Magically makes our ECMAScript Modules work with the
// CommonJS-style preferred by ResourceLoader. Ta-da!
format: 'cjs',
// Remove hash from chunked file name. We only want vendor code to be
// chunked, and we need the file name to be stable for use by ResourceLoader.
chunkFileNames: () => '[name].js',
// Bundle all vendor code into a single file called 'vendor.js'.
// This includes the Babel helpers because they are used by all our modules.
manualChunks: ( id ) => {
if ( id.includes( 'node_modules' ) || id.includes( 'rollupPluginBabelHelpers' ) ) {
return 'vendor';
}
}
file: 'resources/lib/codemirror6.bundle.dist.js',
format: 'cjs'
},
plugins: [
nodeResolve(),
// HACK: Rollup doesn't know about ResourceLoader and attempts to `require`
// modules using a relative path, when they need to match the RL module name.
// Here we do string replacements to fix that. This is nasty and brittle, but
// otherwise we couldn't offer standalone CodeMirror functionality via RL,
// which is necessary for usage in on-wiki scripts and gadgets (T214989).
copy( {
targets: [ {
src: 'resources/dist/*',
dest: 'resources/dist/',
transform: ( contents ) => {
Object.keys( importAliases ).forEach( ( alias ) => {
contents = contents.toString().replace(
`require("${ alias }")`,
`require("${ importAliases[ alias ] }")`
);
// Yuck, need to do the same with single apostrophes
// in the event terser is disabled such as during dev builds.
contents = contents.toString().replace(
`require('${ alias }')`,
`require('${ importAliases[ alias ] }')`
);
} );
return contents;
}
} ],
hook: 'writeBundle'
} ),
isProduction ? babel( { babelHelpers: 'bundled' } ) : null,
isProduction ? terser() : null
],
onwarn: ( warning, warn ) => {
// Suppress "not exported" warnings. We import those for IDE support not for the build.
if ( warning.code === 'MISSING_EXPORT' ) {
return;
}
warn( warning );
}
nodeResolve()
]
}
];

View file

@ -1,24 +0,0 @@
{
"root": true,
"extends": [
"wikimedia/client-es6",
"wikimedia/jquery",
"wikimedia/mediawiki"
],
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2022
},
"env": {
"browser": true,
"commonjs": true
},
"globals": {
"Tree": "readonly"
},
"rules": {
"es-x/no-array-prototype-includes": 0,
"es-x/no-class-fields": 0,
"es-x/no-rest-spread-properties": 0
}
}

View file

@ -1,5 +1,5 @@
import CodeMirror from '../../src/codemirror.js';
import mediaWikiLang from '../../src/codemirror.mode.mediawiki.js';
const CodeMirror = require( '../../resources/codemirror.js' );
const mediaWikiLang = require( '../../resources/codemirror.mediawiki.js' );
const testCases = [
{

View file

@ -1,8 +1,8 @@
import { StreamParser } from '@codemirror/language';
import { Tag } from '@lezer/highlight';
import CodeMirror from '../../src/codemirror.js';
import mediaWikiLang from '../../src/codemirror.mode.mediawiki.js';
import { mwModeConfig } from '../../src/codemirror.mode.mediawiki.config.js';
const { StreamParser } = require( '@codemirror/language' );
const { Tag } = require( '@lezer/highlight' );
const CodeMirror = require( '../../resources/codemirror.js' );
const mediaWikiLang = require( '../../resources/codemirror.mediawiki.js' );
const mwModeConfig = require( '../../resources/codemirror.mediawiki.config.js' );
// NOTE: each test case should have a space before the closing </div>
// This is to avoid interactive UI components from showing up in the test output.

View file

@ -1,5 +1,6 @@
import { EditorView } from '@codemirror/view';
import CodeMirror from '../../src/codemirror.js';
/* eslint-disable-next-line n/no-missing-require */
const { EditorView } = require( 'ext.CodeMirror.v6.lib' );
const CodeMirror = require( '../../resources/codemirror.js' );
const $textarea = $( '<textarea>' ),
cm = new CodeMirror( $textarea );

View file

@ -1,6 +1,6 @@
mw.loader = { getState: jest.fn() };
const CodeMirrorWikiEditor = require( '../../src/codemirror.wikieditor.js' ).default,
const CodeMirrorWikiEditor = require( '../../resources/codemirror.wikieditor.js' ),
$textarea = $( '<textarea>' )
.text( 'The Smashing Pumpkins' ),
cmWe = new CodeMirrorWikiEditor( $textarea );

View file

@ -1,4 +1,8 @@
jest.mock( '../../ext.CodeMirror.data.js', () => jest.fn(), { virtual: true } );
const mockBundle = require( '../../resources/lib/codemirror6.bundle.dist.js' );
jest.mock( 'ext.CodeMirror.v6.lib', () => mockBundle, { virtual: true } );
const mockCodeMirror = require( '../../resources/codemirror.js' );
jest.mock( 'ext.CodeMirror.v6', () => mockCodeMirror, { virtual: true } );
jest.mock( '../../resources/ext.CodeMirror.data.js', () => jest.fn(), { virtual: true } );
global.mw = require( '@wikimedia/mw-node-qunit/src/mockMediaWiki.js' )();
mw.user = Object.assign( mw.user, {
options: {