mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/CodeMirror
synced 2024-11-23 13:56:44 +00:00
Merge "CodeMirror 6: show wikitext highlighting on protected pages"
This commit is contained in:
commit
8dd82a8b86
|
@ -50,6 +50,8 @@ Some may be removed pending user feedback:
|
|||
|
||||
* Closing HTML tags that highlighted as an error now also highlight the closing '>'
|
||||
* Allow link titles to be both emboldened and italicized.
|
||||
* Wikitext syntax highlighting is shown on protected pages
|
||||
([T301615](https://phabricator.wikimedia.org/T301615))
|
||||
|
||||
### Deprecations and other changes
|
||||
|
||||
|
|
|
@ -164,6 +164,7 @@
|
|||
},
|
||||
"ext.CodeMirror.v6.WikiEditor": {
|
||||
"dependencies": [
|
||||
"ext.wikiEditor",
|
||||
"web2017-polyfills",
|
||||
"mediawiki.api",
|
||||
"mediawiki.user",
|
||||
|
@ -250,6 +251,7 @@
|
|||
},
|
||||
"Hooks": {
|
||||
"EditPage::showEditForm:initial": "main",
|
||||
"EditPage::showReadOnlyForm:initial": "main",
|
||||
"GetPreferences": "main",
|
||||
"ResourceLoaderGetConfigVars": "main"
|
||||
},
|
||||
|
|
|
@ -8,6 +8,7 @@ use MediaWiki\Config\Config;
|
|||
use MediaWiki\EditPage\EditPage;
|
||||
use MediaWiki\Extension\Gadgets\GadgetRepo;
|
||||
use MediaWiki\Hook\EditPage__showEditForm_initialHook;
|
||||
use MediaWiki\Hook\EditPage__showReadOnlyForm_initialHook;
|
||||
use MediaWiki\Output\OutputPage;
|
||||
use MediaWiki\Preferences\Hook\GetPreferencesHook;
|
||||
use MediaWiki\ResourceLoader\Hook\ResourceLoaderGetConfigVarsHook;
|
||||
|
@ -19,6 +20,7 @@ use MediaWiki\User\User;
|
|||
*/
|
||||
class Hooks implements
|
||||
EditPage__showEditForm_initialHook,
|
||||
EditPage__showReadOnlyForm_initialHook,
|
||||
ResourceLoaderGetConfigVarsHook,
|
||||
GetPreferencesHook
|
||||
{
|
||||
|
@ -120,6 +122,18 @@ class Hooks implements
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load CodeMirror 6 on read-only pages.
|
||||
*
|
||||
* @param EditPage $editor
|
||||
* @param OutputPage $out
|
||||
*/
|
||||
public function onEditPage__showReadOnlyForm_initial( $editor, $out ): void {
|
||||
if ( $this->shouldUseV6( $out ) && $this->shouldLoadCodeMirror( $out ) ) {
|
||||
$out->addModules( 'ext.CodeMirror.v6.WikiEditor' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OutputPage $out
|
||||
* @return bool
|
||||
|
|
2
resources/dist/main.js
vendored
2
resources/dist/main.js
vendored
File diff suppressed because one or more lines are too long
2
resources/dist/main.js.map.json
vendored
2
resources/dist/main.js.map.json
vendored
File diff suppressed because one or more lines are too long
|
@ -20,3 +20,15 @@
|
|||
direction: ltr;
|
||||
unicode-bidi: isolate;
|
||||
}
|
||||
|
||||
// Hide all buttons except CodeMirror on read only pages (T301615)
|
||||
// This is the same hack that CodeEditor uses to customize the toolbar.
|
||||
// WikiEditor should be updated to better handle read only pages (T188817).
|
||||
.ext-codemirror-readonly {
|
||||
.wikiEditor-section-secondary,
|
||||
.group:not( .group-codemirror ),
|
||||
.tabs,
|
||||
.sections {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import bidiIsolationExtension from './codemirror.bidiIsolation';
|
|||
* @property {jQuery} $textarea
|
||||
* @property {EditorView} view
|
||||
* @property {EditorState} state
|
||||
* @property {boolean} readOnly
|
||||
*/
|
||||
export default class CodeMirror {
|
||||
/**
|
||||
|
@ -17,6 +18,7 @@ export default class CodeMirror {
|
|||
this.$textarea = $( textarea );
|
||||
this.view = null;
|
||||
this.state = null;
|
||||
this.readOnly = this.$textarea.prop( 'readonly' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,7 +34,8 @@ export default class CodeMirror {
|
|||
this.contentAttributesExtension,
|
||||
this.phrasesExtension,
|
||||
this.specialCharsExtension,
|
||||
this.heightExtension
|
||||
this.heightExtension,
|
||||
EditorState.readOnly.of( this.readOnly )
|
||||
];
|
||||
|
||||
// Add bidi isolation to tags on RTL pages (T358804).
|
||||
|
|
|
@ -1214,6 +1214,7 @@ class CodeMirrorModeMediaWiki {
|
|||
* Gets a LanguageSupport instance for the MediaWiki mode.
|
||||
*
|
||||
* @example
|
||||
* import CodeMirror from './codemirror';
|
||||
* import { mediaWikiLang } from './codemirror.mode.mediawiki';
|
||||
* const cm = new CodeMirror( textarea );
|
||||
* cm.initialize( [ ...cm.defaultExtensions, mediaWikiLang() ] );
|
||||
|
|
|
@ -153,8 +153,12 @@ export default class CodeMirrorWikiEditor extends CodeMirror {
|
|||
);
|
||||
|
||||
const $codeMirrorButton = toolbar.$toolbar.find( '.tool[rel=CodeMirror]' );
|
||||
$codeMirrorButton
|
||||
.attr( 'id', 'mw-editbutton-codemirror' );
|
||||
$codeMirrorButton.attr( 'id', 'mw-editbutton-codemirror' );
|
||||
|
||||
// Hide non-applicable buttons until WikiEditor better supports a read-only mode (T188817).
|
||||
if ( this.readOnly ) {
|
||||
this.$textarea.data( 'wikiEditor-context' ).$ui.addClass( 'ext-codemirror-readonly' );
|
||||
}
|
||||
|
||||
if ( this.useCodeMirror ) {
|
||||
this.enableCodeMirror();
|
||||
|
|
|
@ -38,6 +38,16 @@ describe( 'addCodeMirrorToWikiEditor', () => {
|
|||
} )
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should be readonly when the textarea is also readonly', () => {
|
||||
const textarea = document.createElement( 'textarea' );
|
||||
textarea.readOnly = true;
|
||||
const cmWe2 = new CodeMirrorWikiEditor( textarea );
|
||||
cmWe2.initialize();
|
||||
cmWe2.addCodeMirrorToWikiEditor();
|
||||
expect( cmWe2.readOnly ).toEqual( true );
|
||||
expect( cmWe2.state.readOnly ).toEqual( true );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'enableCodeMirror', () => {
|
||||
|
|
|
@ -28,13 +28,18 @@ class HookTest extends MediaWikiIntegrationTestCase {
|
|||
/**
|
||||
* @covers ::shouldLoadCodeMirror
|
||||
* @covers ::onEditPage__showEditForm_initial
|
||||
* @covers ::onEditPage__showReadOnlyForm_initial
|
||||
* @param bool $useCodeMirrorV6
|
||||
* @param int $expectedAddModuleCalls
|
||||
* @param string|null $expectedFirstModule
|
||||
* @param bool $readOnly
|
||||
* @dataProvider provideOnEditPageShowEditFormInitial
|
||||
*/
|
||||
public function testOnEditPageShowEditFormInitial(
|
||||
bool $useCodeMirrorV6, int $expectedAddModuleCalls, ?string $expectedFirstModule
|
||||
bool $useCodeMirrorV6,
|
||||
int $expectedAddModuleCalls,
|
||||
?string $expectedFirstModule,
|
||||
bool $readOnly = false
|
||||
) {
|
||||
$this->overrideConfigValues( [
|
||||
'CodeMirrorV6' => $useCodeMirrorV6,
|
||||
|
@ -55,16 +60,19 @@ class HookTest extends MediaWikiIntegrationTestCase {
|
|||
} );
|
||||
|
||||
$hooks = new Hooks( $userOptionsLookup, $this->getServiceContainer()->getMainConfig() );
|
||||
$hooks->onEditPage__showEditForm_initial( $this->createMock( EditPage::class ), $out );
|
||||
$method = $readOnly ? 'onEditPage__showReadOnlyForm_initial' : 'onEditPage__showEditForm_initial';
|
||||
$hooks->{$method}( $this->createMock( EditPage::class ), $out );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Generator
|
||||
*/
|
||||
public static function provideOnEditPageShowEditFormInitial(): Generator {
|
||||
// useCodeMirrorV6, expectedAddModuleCalls, expectedFirstModule
|
||||
// useCodeMirrorV6, expectedAddModuleCalls, expectedFirstModule, readOnly
|
||||
yield 'CM5' => [ false, 2, 'ext.CodeMirror.WikiEditor' ];
|
||||
yield 'CM6' => [ true, 1, 'ext.CodeMirror.v6.WikiEditor' ];
|
||||
yield 'CM5 read-only' => [ false, 0, null, true ];
|
||||
yield 'CM6 read-only' => [ true, 1, 'ext.CodeMirror.v6.WikiEditor', true ];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,39 +90,35 @@ class HookTest extends MediaWikiIntegrationTestCase {
|
|||
/**
|
||||
* @covers ::shouldLoadCodeMirror
|
||||
* @dataProvider provideShouldLoadCodeMirror
|
||||
* @param string|null $module
|
||||
* @param string|null $gadget
|
||||
* @param array $conds
|
||||
* @param bool $expectation
|
||||
* @param string $contentModel
|
||||
* @param bool $useCodeMirrorV6
|
||||
* @param bool $isRTL
|
||||
*/
|
||||
public function testShouldLoadCodeMirror(
|
||||
?string $module,
|
||||
?string $gadget,
|
||||
bool $expectation,
|
||||
string $contentModel = CONTENT_MODEL_WIKITEXT,
|
||||
bool $useCodeMirrorV6 = false,
|
||||
bool $isRTL = false
|
||||
): void {
|
||||
public function testShouldLoadCodeMirror( array $conds, bool $expectation ): void {
|
||||
$conds = array_merge( [
|
||||
'module' => null,
|
||||
'gadget' => null,
|
||||
'contentModel' => CONTENT_MODEL_WIKITEXT,
|
||||
'useV6' => false,
|
||||
'isRTL' => false
|
||||
], $conds );
|
||||
$this->overrideConfigValues( [
|
||||
'CodeMirrorV6' => $useCodeMirrorV6,
|
||||
'CodeMirrorV6' => $conds['useV6'],
|
||||
] );
|
||||
$out = $this->getMockOutputPage( $contentModel, $isRTL );
|
||||
$out->method( 'getModules' )->willReturn( $module ? [ $module ] : [] );
|
||||
$out = $this->getMockOutputPage( $conds['contentModel'], $conds['isRTL'] );
|
||||
$out->method( 'getModules' )->willReturn( $conds['module'] ? [ $conds['module'] ] : [] );
|
||||
$userOptionsLookup = $this->createMock( UserOptionsLookup::class );
|
||||
$userOptionsLookup->method( 'getOption' )->willReturn( true );
|
||||
|
||||
if ( $gadget && !ExtensionRegistry::getInstance()->isLoaded( 'Gadgets' ) ) {
|
||||
if ( $conds['gadget'] && !ExtensionRegistry::getInstance()->isLoaded( 'Gadgets' ) ) {
|
||||
$this->markTestSkipped( 'Skipped as Gadgets extension is not available' );
|
||||
}
|
||||
|
||||
$extensionRegistry = $this->getMockExtensionRegistry( (bool)$gadget );
|
||||
$extensionRegistry = $this->getMockExtensionRegistry( (bool)$conds['gadget'] );
|
||||
$extensionRegistry->method( 'getAttribute' )
|
||||
->with( 'CodeMirrorContentModels' )
|
||||
->willReturn( [ CONTENT_MODEL_WIKITEXT ] );
|
||||
|
||||
if ( $gadget ) {
|
||||
if ( $conds['gadget'] ) {
|
||||
$gadgetMock = $this->createMock( Gadget::class );
|
||||
$gadgetMock->expects( $this->once() )
|
||||
->method( 'isEnabled' )
|
||||
|
@ -125,14 +129,11 @@ class HookTest extends MediaWikiIntegrationTestCase {
|
|||
->willReturn( $gadgetMock );
|
||||
$gadgetRepoMock->expects( $this->once() )
|
||||
->method( 'getGadgetIds' )
|
||||
->willReturn( [ $gadget ] );
|
||||
->willReturn( [ $conds['gadget'] ] );
|
||||
GadgetRepo::setSingleton( $gadgetRepoMock );
|
||||
}
|
||||
|
||||
$hooks = new Hooks(
|
||||
$userOptionsLookup,
|
||||
$this->getServiceContainer()->getMainConfig()
|
||||
);
|
||||
$hooks = new Hooks( $userOptionsLookup, $this->getServiceContainer()->getMainConfig() );
|
||||
self::assertSame( $expectation, $hooks->shouldLoadCodeMirror( $out, $extensionRegistry ) );
|
||||
}
|
||||
|
||||
|
@ -140,13 +141,13 @@ class HookTest extends MediaWikiIntegrationTestCase {
|
|||
* @return Generator
|
||||
*/
|
||||
public function provideShouldLoadCodeMirror(): Generator {
|
||||
// module, gadget, expectation, contentModel, shouldUseV6, isRTL
|
||||
yield 'no modules, no gadgets, wikitext' => [ null, null, true ];
|
||||
yield 'codeEditor, no gadgets, wikitext' => [ 'ext.codeEditor', null, false ];
|
||||
yield 'no modules, wikEd, wikitext' => [ null, 'wikEd', false ];
|
||||
yield 'no modules, no gadgets, CSS' => [ null, null, false, CONTENT_MODEL_CSS ];
|
||||
yield 'CM5 wikitext RTL' => [ null, null, false, CONTENT_MODEL_WIKITEXT, false, true ];
|
||||
yield 'CM6 wikitext RTL' => [ null, null, true, CONTENT_MODEL_WIKITEXT, true, true ];
|
||||
// [ conditions, expectation ]
|
||||
yield [ [], true ];
|
||||
yield [ [ 'module' => 'ext.codeEditor' ], false ];
|
||||
yield [ [ 'gadget' => 'wikEd' ], false ];
|
||||
yield [ [ 'contentModel' => CONTENT_FORMAT_CSS ], false ];
|
||||
yield [ [ 'isRTL' => true ], false ];
|
||||
yield [ [ 'isRTL' => true, 'useV6' => true ], true ];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue