CodeMirror: partially implement $.textSelection 'encapsulateSelection'

Popular extensions like Charinsert use this method to wrap text around a
selection. This patch adds support for multiple selections in CM6.

Some options to encapsulateSelection do not yet have explicit support
here, such as 'peri', but it's unclear if they are truly needed.

Bug: T211205
Change-Id: Idc0abb64eb036fa4a60382aca401d1dba1722405
This commit is contained in:
MusikAnimal 2024-02-15 19:44:46 -05:00
parent 113c179138
commit 26d5023bb7
4 changed files with 48 additions and 3 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
import { EditorState, Extension } from '@codemirror/state';
import { EditorSelection, EditorState, Extension } from '@codemirror/state';
import { EditorView, lineNumbers, highlightSpecialChars } from '@codemirror/view';
/**
@ -256,6 +256,30 @@ export default class CodeMirror {
this.view.focus();
return $cmDom;
},
encapsulateSelection: ( options ) => {
// First set the selection, if applicable.
if ( options.selectionStart || options.selectionEnd ) {
this.view.dispatch( {
selection: {
anchor: options.selectionStart,
head: ( options.selectionEnd || options.selectionStart )
}
} );
}
// Do the actual replacements.
this.view.dispatch( this.view.state.changeByRange( ( range ) => ( {
changes: [
{ from: range.from, insert: options.pre },
{ from: range.to, insert: options.post }
],
range: EditorSelection.range(
range.from,
range.to + options.pre.length + options.post.length
)
} ) ) );
this.view.focus();
return $cmDom;
},
replaceSelection: ( value ) => {
this.view.dispatch(
this.view.state.replaceSelection( value )

View file

@ -20,6 +20,7 @@ describe( 'CodeMirror textSelection for the wikitext 2010 editor', () => {
await EditPage.clickText();
} );
// Content is "[]{{template}}"
it( 'sets and gets the correct text when using setContents and getContents', async () => {
await browser.execute( () => $( '.cm-editor' ).textSelection( 'setContents', 'foobar' ) );
assert.strictEqual(
@ -28,6 +29,7 @@ describe( 'CodeMirror textSelection for the wikitext 2010 editor', () => {
);
} );
// Content is now "foobar"
it( 'sets and gets the correct selection when using setSelection and getSelection', async () => {
await browser.execute( () => {
$( '.cm-editor' ).textSelection( 'setSelection', { start: 3, end: 6 } );
@ -48,6 +50,7 @@ describe( 'CodeMirror textSelection for the wikitext 2010 editor', () => {
);
} );
// Content is now "foobaz"
it( 'returns the correct values for getCaretPosition', async () => {
await browser.execute( () => {
$( '.cm-editor' ).textSelection( 'setSelection', { start: 3, end: 6 } );
@ -64,6 +67,23 @@ describe( 'CodeMirror textSelection for the wikitext 2010 editor', () => {
);
} );
it( 'correctly wraps the selected text when using encapsulateSelection', async function () {
await browser.execute( () => {
$( '.cm-editor' ).textSelection( 'setContents', 'foobaz' )
.textSelection( 'encapsulateSelection', {
selectionStart: 0,
selectionEnd: 6,
pre: '<div>',
post: '</div>'
} );
} );
assert.strictEqual(
await browser.execute( () => $( '.cm-editor' ).textSelection( 'getContents' ) ),
'<div>foobaz</div>'
);
} );
// Content is now "<div>foobaz</div>"
it( 'scrolls to the correct place when using scrollToCaretPosition', async () => {
await browser.execute( () => {
const $cmEditor = $( '.cm-editor' );
@ -81,6 +101,7 @@ describe( 'CodeMirror textSelection for the wikitext 2010 editor', () => {
);
} );
// Content is now "foobar\n" repeated 50 times.
it( 'retains the contents after turning CodeMirror off', async () => {
await EditPage.legacyCodeMirrorButton.click();
await EditPage.legacyTextInput.waitForDisplayed();