mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/CodeMirror
synced 2024-11-24 06:13:31 +00:00
Merge "VisualEditor source mode support"
This commit is contained in:
commit
2736d27c40
|
@ -7,7 +7,10 @@
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"mediaWiki": false,
|
"mediaWiki": false,
|
||||||
"CodeMirror": false
|
"CodeMirror": false,
|
||||||
|
"ve": false,
|
||||||
|
"mw": false,
|
||||||
|
"OO": false
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"dot-notation": [ "error", { "allowKeywords": true } ]
|
"dot-notation": [ "error", { "allowKeywords": true } ]
|
||||||
|
|
|
@ -107,8 +107,8 @@ class CodeMirrorHooks {
|
||||||
*/
|
*/
|
||||||
public static function onMakeGlobalVariablesScript( array &$vars, OutputPage $out ) {
|
public static function onMakeGlobalVariablesScript( array &$vars, OutputPage $out ) {
|
||||||
$context = $out->getContext();
|
$context = $out->getContext();
|
||||||
// add CodeMirror vars only for edit pages
|
// add CodeMirror vars on edit pages, or if VE is installed
|
||||||
if ( self::isCodeMirrorEnabled( $context ) ) {
|
if ( self::isCodeMirrorEnabled( $context ) || class_exists( 'VisualEditorHooks' ) ) {
|
||||||
$vars['extCodeMirrorConfig'] = self::getConfiguraton( $context );
|
$vars['extCodeMirrorConfig'] = self::getConfiguraton( $context );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ module.exports = function ( grunt ) {
|
||||||
},
|
},
|
||||||
stylelint: {
|
stylelint: {
|
||||||
all: [
|
all: [
|
||||||
'**/*.css',
|
'**/*.{css,less}',
|
||||||
'!resources/lib/**',
|
'!resources/lib/**',
|
||||||
'!node_modules/**'
|
'!node_modules/**'
|
||||||
]
|
]
|
||||||
|
|
|
@ -96,6 +96,37 @@
|
||||||
"ext.CodeMirror.lib.mode.clike",
|
"ext.CodeMirror.lib.mode.clike",
|
||||||
"ext.CodeMirror.lib"
|
"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": {
|
"ResourceFileModulePaths": {
|
||||||
|
@ -113,6 +144,10 @@
|
||||||
"CodeMirrorHooks::onGetPreferences"
|
"CodeMirrorHooks::onGetPreferences"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"VisualEditorPluginModules": [
|
||||||
|
"ext.CodeMirror.visualEditor.init",
|
||||||
|
"ext.CodeMirror.visualEditor"
|
||||||
|
],
|
||||||
"config": {
|
"config": {
|
||||||
"CodeMirrorEnableFrontend": true
|
"CodeMirrorEnableFrontend": true
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,5 +6,6 @@
|
||||||
},
|
},
|
||||||
"codemirror-desc": "Provides syntax highlighting in wikitext editor",
|
"codemirror-desc": "Provides syntax highlighting in wikitext editor",
|
||||||
"codemirror-enable-label": "Enable CodeMirror (Syntax highlight)",
|
"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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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-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-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."
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,3 +29,8 @@
|
||||||
.wikiEditor-ui-toolbar .tool-codemirror-off {
|
.wikiEditor-ui-toolbar .tool-codemirror-off {
|
||||||
.background-image-svg( 'images/cm-off.svg', 'images/cm-off.png' );
|
.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;
|
||||||
|
}
|
||||||
|
|
12
resources/modules/ve-cm/ve.ui.CodeMirror.init.js
Normal file
12
resources/modules/ve-cm/ve.ui.CodeMirror.init.js
Normal file
|
@ -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 ) );
|
49
resources/modules/ve-cm/ve.ui.CodeMirror.init.less
Normal file
49
resources/modules/ve-cm/ve.ui.CodeMirror.init.less
Normal file
|
@ -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;
|
||||||
|
}
|
122
resources/modules/ve-cm/ve.ui.CodeMirrorAction.js
Normal file
122
resources/modules/ve-cm/ve.ui.CodeMirrorAction.js
Normal file
|
@ -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 );
|
78
resources/modules/ve-cm/ve.ui.CodeMirrorTool.js
Normal file
78
resources/modules/ve-cm/ve.ui.CodeMirrorTool.js
Normal file
|
@ -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'
|
||||||
|
)
|
||||||
|
);
|
Loading…
Reference in a new issue