jenkins-bot has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/343878 )
Change subject: VisualEditor source mode support
......................................................................
VisualEditor source mode support
Long-term todo:
* Performance will be poor on large pages due
to using a auto-height textarea which CodeMirror
doesn't optimise.
Change-Id: I16598fcdbeee51e6fae88376ec81f1c8552b383d
---
M .eslintrc.json
M CodeMirror.hooks.php
M Gruntfile.js
M extension.json
M i18n/en.json
M i18n/qqq.json
M resources/ext.CodeMirror.less
A resources/modules/ve-cm/ve.ui.CodeMirror.init.js
A resources/modules/ve-cm/ve.ui.CodeMirror.init.less
A resources/modules/ve-cm/ve.ui.CodeMirrorAction.js
A resources/modules/ve-cm/ve.ui.CodeMirrorTool.js
11 files changed, 312 insertions(+), 6 deletions(-)
Approvals:
jenkins-bot: Verified
Jforrester: Looks good to me, approved
diff --git a/.eslintrc.json b/.eslintrc.json
index 40f6bcf..6068805 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -7,7 +7,10 @@
},
"globals": {
"mediaWiki": false,
- "CodeMirror": false
+ "CodeMirror": false,
+ "ve": false,
+ "mw": false,
+ "OO": false
},
"rules": {
"dot-notation": [ "error", { "allowKeywords": true } ]
diff --git a/CodeMirror.hooks.php b/CodeMirror.hooks.php
index 147148d..6bbaa15 100644
--- a/CodeMirror.hooks.php
+++ b/CodeMirror.hooks.php
@@ -107,8 +107,8 @@
*/
public static function onMakeGlobalVariablesScript( array &$vars,
OutputPage $out ) {
$context = $out->getContext();
- // add CodeMirror vars only for edit pages
- if ( self::isCodeMirrorEnabled( $context ) ) {
+ // add CodeMirror vars on edit pages, or if VE is installed
+ if ( self::isCodeMirrorEnabled( $context ) || class_exists(
'VisualEditorHooks' ) ) {
$vars['extCodeMirrorConfig'] = self::getConfiguraton(
$context );
}
}
diff --git a/Gruntfile.js b/Gruntfile.js
index 4a474d8..90f0e22 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -15,7 +15,7 @@
},
stylelint: {
all: [
- '**/*.css',
+ '**/*.{css,less}',
'!resources/lib/**',
'!node_modules/**'
]
diff --git a/extension.json b/extension.json
index 309e110..c592dab 100644
--- a/extension.json
+++ b/extension.json
@@ -96,6 +96,37 @@
"ext.CodeMirror.lib.mode.clike",
"ext.CodeMirror.lib"
]
+ },
+ "ext.CodeMirror.visualEditor.init": {
+ "scripts": [
+ "modules/ve-cm/ve.ui.CodeMirror.init.js"
+ ],
+ "styles": [
+ "modules/ve-cm/ve.ui.CodeMirror.init.less"
+ ],
+ "messages": [
+ "codemirror-toggle-label"
+ ],
+ "targets": [
+ "desktop"
+ ]
+ },
+ "ext.CodeMirror.visualEditor": {
+ "dependencies": [
+ "ext.visualEditor.mwcore",
+ "ext.CodeMirror.lib",
+ "ext.CodeMirror.mode.mediawiki",
+ "mediawiki.api",
+ "mediawiki.api.options",
+ "user.options"
+ ],
+ "scripts": [
+ "modules/ve-cm/ve.ui.CodeMirrorAction.js",
+ "modules/ve-cm/ve.ui.CodeMirrorTool.js"
+ ],
+ "targets": [
+ "desktop"
+ ]
}
},
"ResourceFileModulePaths": {
@@ -113,6 +144,10 @@
"CodeMirrorHooks::onGetPreferences"
]
},
+ "VisualEditorPluginModules": [
+ "ext.CodeMirror.visualEditor.init",
+ "ext.CodeMirror.visualEditor"
+ ],
"config": {
"CodeMirrorEnableFrontend": true
},
diff --git a/i18n/en.json b/i18n/en.json
index a77c902..425efaf 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -6,5 +6,6 @@
},
"codemirror-desc": "Provides syntax highlighting in wikitext editor",
"codemirror-enable-label": "Enable CodeMirror (Syntax highlight)",
- "codemirror-disable-label": "Disable CodeMirror (Syntax highlight)"
+ "codemirror-disable-label": "Disable CodeMirror (Syntax highlight)",
+ "codemirror-toggle-label": "Syntax highlighting"
}
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 5fce41e..81cc890 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -9,5 +9,6 @@
},
"codemirror-desc": "{{desc|name=Code
Mirror|url=https://www.mediawiki.org/wiki/Extension:CodeMirror}}\n\nAdditional
info: Discription of \"Syntax highlighting\" in
wiki\n[[mw:Extension:SyntaxHighlight GeSHi]]",
"codemirror-enable-label": "Title tooltip for button to enable
CodeMirror in the editing toolbar.",
- "codemirror-disable-label": "Title tooltip for button to disable
CodeMirror in the editing toolbar."
+ "codemirror-disable-label": "Title tooltip for button to disable
CodeMirror in the editing toolbar.",
+ "codemirror-toggle-label": "Title tooltip for button to toggle
CodeMirror in the editing toolbar."
}
diff --git a/resources/ext.CodeMirror.less b/resources/ext.CodeMirror.less
index 30a2f48..600820f 100644
--- a/resources/ext.CodeMirror.less
+++ b/resources/ext.CodeMirror.less
@@ -29,3 +29,8 @@
.wikiEditor-ui-toolbar .tool-codemirror-off {
.background-image-svg( 'images/cm-off.svg', 'images/cm-off.png' );
}
+
+.oo-ui-popupWidget.ve-init-mw-switchPopupWidget {
+ // Increase z-index to above scrollbar
+ z-index: 7;
+}
diff --git a/resources/modules/ve-cm/ve.ui.CodeMirror.init.js
b/resources/modules/ve-cm/ve.ui.CodeMirror.init.js
new file mode 100644
index 0000000..4708879
--- /dev/null
+++ b/resources/modules/ve-cm/ve.ui.CodeMirror.init.js
@@ -0,0 +1,12 @@
+( function ( ve, mw ) {
+ mw.libs.ve.targetLoader.addPlugin( function () {
+ var i, target, index;
+ for ( i in ve.init.mw ) {
+ target = ve.init.mw[ i ];
+ if ( target === ve.init.mw.DesktopArticleTarget ) {
+ index = target.static.actionGroups[ 1
].include.indexOf( 'changeDirectionality' );
+ target.static.actionGroups[ 1 ].include.splice(
index, 0, 'codeMirror' );
+ }
+ }
+ } );
+}( ve, mediaWiki ) );
diff --git a/resources/modules/ve-cm/ve.ui.CodeMirror.init.less
b/resources/modules/ve-cm/ve.ui.CodeMirror.init.less
new file mode 100644
index 0000000..2ddbf17
--- /dev/null
+++ b/resources/modules/ve-cm/ve.ui.CodeMirror.init.less
@@ -0,0 +1,49 @@
+.ve-init-mw-desktopArticleTarget {
+ .CodeMirror {
+ height: auto;
+ z-index: -1;
+ position: absolute;
+ top: 0;
+ left: 0;
+ font-size: 1.17216em;
+ line-height: 1.5em;
+ width: 100%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+
+ padding: 0 1.14286em; /* 1/0.875 */
+ @media screen and ( min-width: 982px ) {
+ padding: 0 1.71429em; /* surface-margin-left (1.5em) /
(mw-body-content font-size) 0.875em */
+ }
+ }
+
+ .CodeMirror pre,
+ .CodeMirror-lines {
+ padding: 0;
+ }
+
+ .CodeMirror-scroll {
+ margin-right: 0;
+ overflow: auto !important; /* stylelint-disable-line
declaration-no-important */
+ }
+
+ .CodeMirror-sizer {
+ border-right: 0;
+ }
+
+ .CodeMirror pre.cm-mw-section-1,
+ .CodeMirror pre.cm-mw-section-2 {
+ font-size: inherit;
+ line-height: inherit;
+ font-weight: bold;
+ }
+}
+
+.ve-ce-documentNode-codeEditor-hide {
+ opacity: 0.3;
+}
+
+.ve-ce-documentNode-codeEditor-webkit-hide {
+ -webkit-text-fill-color: transparent;
+}
diff --git a/resources/modules/ve-cm/ve.ui.CodeMirrorAction.js
b/resources/modules/ve-cm/ve.ui.CodeMirrorAction.js
new file mode 100644
index 0000000..effe1de
--- /dev/null
+++ b/resources/modules/ve-cm/ve.ui.CodeMirrorAction.js
@@ -0,0 +1,122 @@
+/*!
+ * VisualEditor UserInterface CodeMirrorAction class.
+ *
+ * @copyright 2011-2017 VisualEditor Team and others; see
http://ve.mit-license.org
+ */
+
+/**
+ * CodeMirror action
+ *
+ * @class
+ * @extends ve.ui.Action
+ * @constructor
+ * @param {ve.ui.Surface} surface Surface to act on
+ */
+ve.ui.CodeMirrorAction = function VeUiCodeMirrorAction() {
+ // Parent constructor
+ ve.ui.CodeMirrorAction.super.apply( this, arguments );
+};
+
+/* Inheritance */
+
+OO.inheritClass( ve.ui.CodeMirrorAction, ve.ui.Action );
+
+/* Static Properties */
+
+ve.ui.CodeMirrorAction.static.name = 'codeMirror';
+
+/**
+ * @inheritdoc
+ */
+ve.ui.CodeMirrorAction.static.methods = [ 'toggle' ];
+
+/* Methods */
+
+/**
+ * @method
+ * @param {boolean} [enable] State to force toggle to, inverts current state
if undefined
+ * @return {boolean} Action was executed
+ */
+ve.ui.CodeMirrorAction.prototype.toggle = function ( enable ) {
+ var surface = this.surface,
+ surfaceView = surface.getView(),
+ doc = surface.getModel().getDocument();
+
+ if ( !surface.mirror && enable !== false ) {
+ surface.mirror = CodeMirror( surfaceView.$element[ 0 ], {
+ value: surface.getDom(),
+ mwConfig: mw.config.get( 'extCodeMirrorConfig' ),
+ lineWrapping: true,
+ tabSize: 1,
+ scrollbarStyle: 'null',
+ viewportMargin: 5,
+ // select mediawiki as text input mode
+ mode: 'text/mediawiki',
+ extraKeys: {
+ Tab: false
+ }
+ } );
+
+ surfaceView.$documentNode.addClass(
+ 'WebkitTextFillColor' in document.body.style ?
+ 've-ce-documentNode-codeEditor-webkit-hide' :
+ 've-ce-documentNode-codeEditor-webkit'
+ );
+
+ // As the action is regenerated each time, we need to store the
bound listener
+ // in the mirror for later disconnection.
+ surface.mirror.veTransactionListener =
this.onDocumentTransact.bind( this, surface );
+
+ doc.on( 'transact', surface.mirror.veTransactionListener );
+ } else if ( surface.mirror && enable !== true ) {
+ doc.off( 'transact', surface.mirror.veTransactionListener );
+
+ surfaceView.$documentNode.removeClass(
+ 've-ce-documentNode-codeEditor-webkit-hide
ve-ce-documentNode-codeEditor-webkit'
+ );
+
+ surface.mirror.getWrapperElement().remove();
+
+ surface.mirror = null;
+ }
+
+ return true;
+};
+
+ve.ui.CodeMirrorAction.prototype.onDocumentTransact = function ( surface, tx )
{
+ var node, textRange, line,
+ doc = surface.getModel().getDocument(),
+ mirror = surface.mirror,
+ modifiedRange = tx.getModifiedRange( doc ),
+ nodes = doc.selectNodes( modifiedRange, 'leaves' );
+
+ // TODO: Iterate over operations and perform a replaceRange for each
replace operation
+ if ( nodes.length === 1 && nodes[ 0 ].node instanceof ve.dm.TextNode ) {
+ node = nodes[ 0 ].node.parent;
+ textRange = nodes[ 0 ].nodeRange;
+ line = node.parent.children.indexOf( node );
+ if ( tx.operations.every( function ( op ) {
+ return op.type === 'retain' || ( op.type === 'replace'
&& op.remove.length === 0 );
+ } ) ) {
+ // Single line insert
+ mirror.replaceRange(
+ doc.data.getText( true, modifiedRange ),
+ { line: line, ch: modifiedRange.start -
textRange.start }
+ );
+ } else {
+ // Single line replace
+ mirror.replaceRange(
+ doc.data.getText( true, textRange ),
+ { line: line, ch: 0 },
+ { line: line, ch: mirror.getLine( line ).length
}
+ );
+ }
+ } else {
+ // Fallback - flush whole doc
+ mirror.setValue( surface.getDom() );
+ }
+};
+
+/* Registration */
+
+ve.ui.actionFactory.register( ve.ui.CodeMirrorAction );
diff --git a/resources/modules/ve-cm/ve.ui.CodeMirrorTool.js
b/resources/modules/ve-cm/ve.ui.CodeMirrorTool.js
new file mode 100644
index 0000000..e7cd52e
--- /dev/null
+++ b/resources/modules/ve-cm/ve.ui.CodeMirrorTool.js
@@ -0,0 +1,78 @@
+/**
+ * MediaWiki UserInterface CodeMirror tool.
+ *
+ * @class
+ * @abstract
+ * @extends ve.ui.Tool
+ * @constructor
+ * @param {OO.ui.ToolGroup} toolGroup
+ * @param {Object} [config] Configuration options
+ */
+ve.ui.CodeMirrorTool = function VeUiCodeMirrorTool() {
+ // Parent constructor
+ ve.ui.CodeMirrorTool.super.apply( this, arguments );
+
+ // Events
+ this.toolbar.connect( this, { surfaceChange: 'onSurfaceChange' } );
+};
+
+/* Inheritance */
+
+OO.inheritClass( ve.ui.CodeMirrorTool, ve.ui.Tool );
+
+/* Static properties */
+
+ve.ui.CodeMirrorTool.static.name = 'codeMirror';
+ve.ui.CodeMirrorTool.static.autoAddToCatchall = false;
+ve.ui.CodeMirrorTool.static.title = OO.ui.deferMsg( 'codemirror-toggle-label'
);
+ve.ui.CodeMirrorTool.static.icon = 'code';
+ve.ui.CodeMirrorTool.static.group = 'codeMirror';
+ve.ui.CodeMirrorTool.static.commandName = 'codeMirror';
+ve.ui.CodeMirrorTool.static.deactivateOnSelect = false;
+
+/**
+ * @inheritdoc
+ */
+ve.ui.CodeMirrorTool.prototype.onSelect = function () {
+ var useCodeMirror;
+
+ // Parent method
+ ve.ui.CodeMirrorTool.super.prototype.onSelect.apply( this, arguments );
+
+ useCodeMirror = !!this.toolbar.surface.mirror;
+ this.setActive( useCodeMirror );
+
+ new mw.Api().saveOption( 'usecodemirror', useCodeMirror ? 1 : 0 );
+ mw.user.options.set( 'usecodemirror', useCodeMirror ? 1 : 0 );
+};
+
+/**
+ * @inheritdoc
+ */
+ve.ui.CodeMirrorTool.prototype.onSurfaceChange = function ( oldSurface,
newSurface ) {
+ var useCodeMirror,
+ isDisabled = newSurface.getMode() !== 'source',
+ command = this.getCommand(),
+ surface = this.toolbar.getSurface();
+
+ this.setDisabled( isDisabled );
+ if ( !isDisabled ) {
+ useCodeMirror = mw.user.options.get( 'usecodemirror' ) > 0;
+ command.execute( surface, [ useCodeMirror ] );
+ this.setActive( useCodeMirror );
+ }
+};
+
+ve.ui.CodeMirrorTool.prototype.onUpdateState = function () {};
+
+/* Registration */
+
+ve.ui.toolFactory.register( ve.ui.CodeMirrorTool );
+
+/* Command */
+
+ve.ui.commandRegistry.register(
+ new ve.ui.Command(
+ 'codeMirror', 'codeMirror', 'toggle'
+ )
+);
--
To view, visit https://gerrit.wikimedia.org/r/343878
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I16598fcdbeee51e6fae88376ec81f1c8552b383d
Gerrit-PatchSet: 24
Gerrit-Project: mediawiki/extensions/CodeMirror
Gerrit-Branch: master
Gerrit-Owner: Esanders <[email protected]>
Gerrit-Reviewer: Bartosz DziewoĆski <[email protected]>
Gerrit-Reviewer: Esanders <[email protected]>
Gerrit-Reviewer: Jforrester <[email protected]>
Gerrit-Reviewer: Kaldari <[email protected]>
Gerrit-Reviewer: MusikAnimal <[email protected]>
Gerrit-Reviewer: Niharika29 <[email protected]>
Gerrit-Reviewer: Pastakhov <[email protected]>
Gerrit-Reviewer: Samwilson <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits