瀏覽代碼

Ensured the code block is properlu copied to the clipboard (getSelectedContent() integration).

Aleksander Nowodzinski 6 年之前
父節點
當前提交
8f41068df3

+ 48 - 0
packages/ckeditor5-code-block/src/codeblockediting.js

@@ -10,6 +10,7 @@
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import ShiftEnter from '@ckeditor/ckeditor5-enter/src/shiftenter';
 import CodeBlockCommand from './codeblockcommand';
+import DocumentFragment from '@ckeditor/ckeditor5-engine/src/model/documentfragment';
 import { getLocalizedLanguageDefinitions } from './utils';
 
 const DEFAULT_ELEMENT = 'paragraph';
@@ -119,6 +120,53 @@ export default class CodeBlockEditing extends Plugin {
 				evt.stop();
 			} );
 		} );
+
+		// Make sure multi–line selection is always wrapped in a code block. Otherwise, only the raw text
+		// will be copied to the clipboard by the user and, upon the next paste, this bare text will not be
+		// inserted as a code block, which is not the best UX.
+		// Similarly, when the selection in a single line, the selected content should be an inline
+		// code so it can be pasted later on and retain it's preformatted nature.
+		this.listenTo( model, 'getSelectedContent', ( evt, [ selection ] ) => {
+			const anchor = selection.anchor;
+
+			if ( !anchor.parent.is( 'codeBlock' ) || !anchor.hasSameParentAs( selection.focus ) ) {
+				return;
+			}
+
+			const docFragment = evt.return;
+
+			// From:
+			//
+			//		fo[o
+			//		<softBreak></softBreak>
+			//		b]ar
+			//
+			// into:
+			//
+			//		<codeBlock language="...">
+			//			[o
+			//			<softBreak></softBreak>
+			//			b]
+			//		<codeBlock>
+			//
+			if ( docFragment.childCount > 1 || selection.containsEntireContent( anchor.parent ) ) {
+				const codeBlock = anchor.parent._clone();
+				codeBlock._insertChild( 0, docFragment );
+				evt.return = new DocumentFragment( [ codeBlock ] );
+			}
+
+			// From:
+			//
+			//		f[oo]
+			//
+			// into:
+			//
+			//		<$text code="true">oo</text>
+			//
+			else {
+				docFragment.getChild( 0 )._setAttribute( 'code', true );
+			}
+		} );
 	}
 
 	/**

+ 75 - 1
packages/ckeditor5-code-block/tests/codeblockediting.js

@@ -17,7 +17,7 @@ import Undo from '@ckeditor/ckeditor5-undo/src/undo';
 import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
 
 import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
-import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+import { getData as getModelData, setData as setModelData, stringify } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
 
 import { _clear as clearTranslations, add as addTranslations } from '@ckeditor/ckeditor5-utils/src/translation-service';
@@ -733,5 +733,79 @@ describe( 'CodeBlockEditing', () => {
 
 			sinon.assert.calledOnce( dataTransferMock.getData );
 		} );
+
+		describe( 'getSelectedContent', () => {
+			it( 'should wrap a partial multi-line selection into a code block (#1)', () => {
+				setModelData( model, '<codeBlock language="css">fo[o<softBreak></softBreak>b]ar</codeBlock>' );
+
+				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
+					'<codeBlock language="css">o<softBreak></softBreak>b</codeBlock>'
+				);
+			} );
+
+			it( 'should wrap a partial multi-line selection into a code block (#2)', () => {
+				setModelData( model, '<codeBlock language="css">fo[o<softBreak></softBreak>]bar</codeBlock>' );
+
+				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
+					'<codeBlock language="css">o<softBreak></softBreak></codeBlock>'
+				);
+			} );
+
+			it( 'should wrap a partial multi-line selection into a code block (#3)', () => {
+				setModelData( model, '<codeBlock language="css">[foo<softBreak></softBreak>bar]</codeBlock>' );
+
+				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
+					'<codeBlock language="css">foo<softBreak></softBreak>bar</codeBlock>'
+				);
+			} );
+
+			it( 'should wrap a complete single-line selection into a code block', () => {
+				setModelData( model, '<codeBlock language="css">[foo]</codeBlock>' );
+
+				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
+					'<codeBlock language="css">foo</codeBlock>'
+				);
+			} );
+
+			it( 'should wrap a partial single-line selection into an inline code (#1)', () => {
+				setModelData( model, '<codeBlock language="css">[fo]o<softBreak></softBreak>bar</codeBlock>' );
+
+				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
+					'<$text code="true">fo</$text>'
+				);
+			} );
+
+			it( 'should wrap a partial single-line selection into an inline code (#2)', () => {
+				setModelData( model, '<codeBlock language="css">foo<softBreak></softBreak>b[a]r</codeBlock>' );
+
+				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
+					'<$text code="true">a</$text>'
+				);
+			} );
+
+			it( 'should preserve a code block in a cross-selection (#1)', () => {
+				setModelData( model, '<paragraph>[x</paragraph><codeBlock language="css">fo]o<softBreak></softBreak>bar</codeBlock>' );
+
+				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
+					'<paragraph>x</paragraph><codeBlock language="css">fo</codeBlock>'
+				);
+			} );
+
+			it( 'should preserve a code block in a cross-selection (#2)', () => {
+				setModelData( model, '<paragraph>[x</paragraph><codeBlock language="css">foo<softBreak></softBreak>b]ar</codeBlock>' );
+
+				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
+					'<paragraph>x</paragraph><codeBlock language="css">foo<softBreak></softBreak>b</codeBlock>'
+				);
+			} );
+
+			it( 'should preserve a code block in a cross-selection (#3)', () => {
+				setModelData( model, '<codeBlock language="css">foo<softBreak></softBreak>b[ar</codeBlock><paragraph>x]</paragraph>' );
+
+				expect( stringify( model.getSelectedContent( model.document.selection ) ) ).to.equal(
+					'<codeBlock language="css">ar</codeBlock><paragraph>x</paragraph>'
+				);
+			} );
+		} );
 	} );
 } );