Ver código fonte

Introduced the ShiftEnter plugin.

Kamil Piechaczek 7 anos atrás
pai
commit
4370c3a3e8

+ 6 - 1
packages/ckeditor5-enter/src/enter.js

@@ -12,7 +12,7 @@ import EnterCommand from './entercommand';
 import EnterObserver from './enterobserver';
 
 /**
- * The Enter feature. Handles the <kbd>Enter</kbd> and <kbd>Shift + Enter</kbd> keys in the editor.
+ * The Enter feature. Handles the <kbd>Enter</kbd> key in the editor.
  *
  * @extends module:core/plugin~Plugin
  */
@@ -35,6 +35,11 @@ export default class Enter extends Plugin {
 
 		// TODO We may use the keystroke handler for that.
 		this.listenTo( viewDocument, 'enter', ( evt, data ) => {
+			// The 'Enter + Shift' keys are handled by the ShiftEnter plugin.
+			if ( data.isSoft ) {
+				return;
+			}
+
 			editor.execute( 'enter' );
 			data.preventDefault();
 			view.scrollToTheSelection();

+ 2 - 1
packages/ckeditor5-enter/src/enterobserver.js

@@ -51,7 +51,8 @@ export default class EnterObserver extends Observer {
  * Event fired when the user presses the <kbd>Enter</kbd> key.
  *
  * Note: This event is fired by the {@link module:enter/enterobserver~EnterObserver observer}
- * (usually registered by the {@link module:enter/enter~Enter Enter feature}).
+ * (usually registered by the {@link module:enter/enter~Enter Enter feature} and
+ * {@link module:enter/softenter~SoftEnter SoftEnter feature}).
  *
  * @event module:engine/view/document~Document#event:enter
  * @param {module:engine/view/observer/domeventdata~DomEventData} data

+ 70 - 0
packages/ckeditor5-enter/src/shiftenter.js

@@ -0,0 +1,70 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module enter/shiftenter
+ */
+
+import ShiftEnterCommand from './shiftentercommand';
+import EnterObserver from './enterobserver';
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import { upcastElementToElement } from '@ckeditor/ckeditor5-engine/src/conversion/upcast-converters';
+import { downcastElementToElement } from '@ckeditor/ckeditor5-engine/src/conversion/downcast-converters';
+
+/**
+ * The ShiftEnter feature. Handles the <kbd>Shift + Enter</kbd> keys in the editor.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class ShiftEnter extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'SoftEnter';
+	}
+
+	init() {
+		const editor = this.editor;
+		const schema = editor.model.schema;
+		const conversion = editor.conversion;
+		const view = editor.editing.view;
+		const viewDocument = view.document;
+
+		// Configure schema.
+		schema.register( 'break', {
+			allowWhere: '$text'
+		} );
+
+		// Configure converters.
+		conversion.for( 'upcast' )
+			.add( upcastElementToElement( {
+				model: 'break',
+				view: 'br'
+			} ) );
+
+		conversion.for( 'downcast' )
+			.add( downcastElementToElement( {
+				model: 'break',
+				view: ( modelElement, viewWriter ) => viewWriter.createEmptyElement( 'br' )
+			} ) );
+
+		view.addObserver( EnterObserver );
+
+		editor.commands.add( 'shiftEnter', new ShiftEnterCommand( editor ) );
+
+		// TODO We may use the keystroke handler for that.
+		this.listenTo( viewDocument, 'enter', ( evt, data ) => {
+			// The 'Enter' key is handled by the Enter plugin.
+			if ( !data.isSoft ) {
+				return;
+			}
+
+			editor.execute( 'shiftEnter' );
+			data.preventDefault();
+			view.scrollToTheSelection();
+		}, { priority: 'low' } );
+	}
+}

+ 99 - 0
packages/ckeditor5-enter/src/shiftentercommand.js

@@ -0,0 +1,99 @@
+/**
+ * @module enter/shiftentercommand
+ */
+
+import Command from '@ckeditor/ckeditor5-core/src/command';
+
+/**
+ * Soft break command.
+ *
+ * @extends module:core/command~Command
+ */
+export default class ShiftEnterCommand extends Command {
+	/**
+	 * @inheritDoc
+	 */
+	execute() {
+		const model = this.editor.model;
+		const doc = model.document;
+
+		model.change( writer => {
+			softBreakAction( model, writer, doc.selection );
+			this.fire( 'afterExecute', { writer } );
+		} );
+	}
+
+	refresh() {
+		const model = this.editor.model;
+		const doc = model.document;
+
+		this.isEnabled = isEnabled( model.schema, doc.selection );
+	}
+}
+
+// Checks whether the soft enter command should be enabled in the specified selection.
+//
+// @param {module:engine/model/schema~Schema} schema
+// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
+function isEnabled( schema, selection ) {
+	if ( selection.rangeCount > 1 ) {
+		return false;
+	}
+
+	const anchorPos = selection.anchor;
+
+	// Check whether the break element can be inserted in the current selection anchor.
+	if ( !anchorPos || !schema.checkChild( anchorPos, 'break' ) ) {
+		return false;
+	}
+
+	const range = selection.getFirstRange();
+	const startElement = range.start.parent;
+	const endElement = range.end.parent;
+
+	// If the selection contains at least two elements and one of them is the limit element, the soft enter shouldn't be enabled.
+	if ( ( schema.isLimit( startElement ) || schema.isLimit( endElement ) ) && startElement !== endElement ) {
+		return false;
+	}
+
+	return true;
+}
+
+// Creates a break in the way that the <kbd>Shift+Enter</kbd> key is expected to work.
+//
+// @param {module:engine/model~Model} model
+// @param {module:engine/model/writer~Writer} writer
+// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
+// Selection on which the action should be performed.
+function softBreakAction( model, writer, selection ) {
+	const isSelectionEmpty = selection.isCollapsed;
+	const range = selection.getFirstRange();
+	const startElement = range.start.parent;
+	const endElement = range.end.parent;
+	const isContainedWithinOneElement = ( startElement == endElement );
+
+	if ( isSelectionEmpty ) {
+		insertBreak( writer, range.end );
+	} else {
+		const leaveUnmerged = !( range.start.isAtStart && range.end.isAtEnd );
+		model.deleteContent( selection, { leaveUnmerged } );
+
+		// Partially selected elements.
+		//
+		// <h>x[xx]x</h>		-> <h>x^x</h>			-> <h>x<br>^x</h>
+		if ( isContainedWithinOneElement ) {
+			insertBreak( writer, selection.focus );
+		}
+
+		if ( leaveUnmerged && !isContainedWithinOneElement ) {
+			writer.setSelection( endElement, 0 );
+		}
+	}
+}
+
+function insertBreak( writer, position ) {
+	const breakLineElement = writer.createElement( 'break' );
+
+	writer.insert( breakLineElement, position );
+	writer.setSelection( breakLineElement, 'after' );
+}

+ 0 - 48
packages/ckeditor5-enter/src/softbreak.js

@@ -1,48 +0,0 @@
-/**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module enter/enter
- */
-
-import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-import SoftBreakEditing from './softbreakediting';
-import SoftBreakCommand from './softbreakcommand';
-import SoftBreakObserver from './softbreakobserver';
-
-/**
- * The Enter feature. Handles the <kbd>Enter</kbd> and <kbd>Shift + Enter</kbd> keys in the editor.
- *
- * @extends module:core/plugin~Plugin
- */
-export default class SoftBreak extends Plugin {
-	/**
-	 * @inheritDoc
-	 */
-	static get pluginName() {
-		return 'SoftBreak';
-	}
-
-	static get requires() {
-		return [ SoftBreakEditing ];
-	}
-
-	init() {
-		const editor = this.editor;
-		const view = editor.editing.view;
-		const viewDocument = view.document;
-
-		view.addObserver( SoftBreakObserver );
-
-		editor.commands.add( 'softbreak', new SoftBreakCommand( editor ) );
-
-		// TODO We may use the keystroke handler for that.
-		this.listenTo( viewDocument, 'softbreak', ( evt, data ) => {
-			editor.execute( 'softbreak' );
-			data.preventDefault();
-			view.scrollToTheSelection();
-		}, { priority: 'low' } );
-	}
-}

+ 0 - 93
packages/ckeditor5-enter/src/softbreakcommand.js

@@ -1,93 +0,0 @@
-/**
- * @module enter/softbreakcommand
- */
-
-import Command from '@ckeditor/ckeditor5-core/src/command';
-
-/**
- * Soft break command.
- *
- * @extends module:core/command~Command
- */
-export default class SoftBreakCommand extends Command {
-	/**
-	 * @inheritDoc
-	 */
-	execute() {
-		const model = this.editor.model;
-		const doc = model.document;
-
-		model.change( writer => {
-			softBreakAction( this.editor.model, writer, doc.selection, model.schema );
-			this.fire( 'afterExecute', { writer } );
-		} );
-	}
-}
-
-// Creates a new block in the way that the <kbd>Enter</kbd> key is expected to work.
-//
-// @param {module:engine/model~Model} model
-// @param {module:engine/model/writer~Writer} writer
-// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
-// Selection on which the action should be performed.
-// @param {module:engine/model/schema~Schema} schema
-function softBreakAction( model, writer, selection, schema ) {
-	const isSelectionEmpty = selection.isCollapsed;
-	const range = selection.getFirstRange();
-	const startElement = range.start.parent;
-	const endElement = range.end.parent;
-
-	// Don't touch the roots and other limit elements.
-	if ( schema.isLimit( startElement ) || schema.isLimit( endElement ) ) {
-		// Delete the selected content but only if inside a single limit element.
-		// Abort, when crossing limit elements boundary (e.g. <limit1>x[x</limit1>donttouchme<limit2>y]y</limit2>).
-		// This is an edge case and it's hard to tell what should actually happen because such a selection
-		// is not entirely valid.
-		if ( !isSelectionEmpty && startElement == endElement ) {
-			model.deleteContent( selection );
-		}
-
-		return;
-	}
-
-	if ( isSelectionEmpty ) {
-		insertBreak( writer, selection, range.end );
-	} else {
-		const leaveUnmerged = !( range.start.isAtStart && range.end.isAtEnd );
-		const isContainedWithinOneElement = ( startElement == endElement );
-
-		model.deleteContent( selection, { leaveUnmerged } );
-
-		if ( leaveUnmerged ) {
-			// Partially selected elements.
-			//
-			// <h>x[xx]x</h>		-> <h>x^x</h>			-> <h>x</h><h>^x</h>
-			if ( isContainedWithinOneElement ) {
-				insertBreak( writer, selection, selection.focus );
-			}
-			// Selection over multiple elements.
-			//
-			// <h>x[x</h><p>y]y<p>	-> <h>x^</h><p>y</p>	-> <h>x</h><p>^y</p>
-			else {
-				writer.setSelection( endElement, 0 );
-			}
-		}
-	}
-}
-
-function insertBreak( writer, selection, position ) {
-	const newBreak = writer.createElement( 'br' );
-
-	if ( position.isAtEnd ) {
-		// If the position is at the end of the element then insert the break at the end
-		writer.insert( newBreak, position, 'after' );
-		writer.setSelection( newBreak, 'after' );
-	} else if ( position.isAtStart ) {
-		// If the position is at the start of element, insert break at start
-		writer.insert( newBreak, position, 'before' );
-		writer.setSelection( newBreak, 'after' );
-	} else {
-		writer.insert( newBreak, position, 'end' );
-		writer.setSelection( newBreak, 'after' );
-	}
-}

+ 0 - 40
packages/ckeditor5-enter/src/softbreakediting.js

@@ -1,40 +0,0 @@
-/**
- * @module enter/softbreakediting
- */
-
-import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-
-import { downcastElementToElement } from '@ckeditor/ckeditor5-engine/src/conversion/downcast-converters';
-
-/**
- * The soft-break enter plugin.
- * It registers `<br>` as an element in the document schema.
- * It also registers converters for editing and data pipelines.
- *
- * @extends module:core/plugin~Plugin
- */
-export default class SoftBreakEditing extends Plugin {
-	/**
-	 * @inheritDoc
-	 */
-	init() {
-		const editor = this.editor;
-		const schema = editor.model.schema;
-		const conversion = editor.conversion;
-
-		// Configure schema.
-		schema.register( 'br', {
-			isObject: true,
-			isBlock: false,
-			allowWhere: '$text'
-		} );
-
-		conversion.elementToElement( { model: 'br', view: 'br' } );
-
-		conversion.for( 'downcast' )
-			.add( downcastElementToElement( {
-				model: 'br',
-				view: ( modelElement, viewWriter ) => viewWriter.createEmptyElement( 'br' )
-			} ) );
-	}
-}

+ 0 - 56
packages/ckeditor5-enter/src/softbreakobserver.js

@@ -1,56 +0,0 @@
-/**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module enter/softbreakobserver
- */
-
-import Observer from '@ckeditor/ckeditor5-engine/src/view/observer/observer';
-import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
-import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
-
-/**
- * SoftBreak observer introduces the {@link module:engine/view/document~Document#event:softbreak} event.
- *
- * @extends module:engine/view/observer~Observer
- */
-export default class SoftBreakObserver extends Observer {
-	constructor( view ) {
-		super( view );
-
-		const document = this.document;
-
-		document.on( 'keydown', ( evt, data ) => {
-			if ( this.isEnabled && data.keyCode == keyCodes.enter && data.shiftKey ) {
-				// Save the event object to check later if it was stopped or not.
-				let event;
-				document.once( 'softbreak', evt => ( event = evt ), { priority: 'highest' } );
-
-				document.fire( 'softbreak', new DomEventData( document, data.domEvent ) );
-
-				// Stop `keydown` event if `enter` event was stopped.
-				// https://github.com/ckeditor/ckeditor5/issues/753
-				if ( event && event.stop.called ) {
-					evt.stop();
-				}
-			}
-		} );
-	}
-
-	/**
-	 * @inheritDoc
-	 */
-	observe() {}
-}
-
-/**
- * Event fired when the user presses the <kbd>Shift+Enter</kbd> key.
- *
- * Note: This event is fired by the {@link module:enter/softbreakobserver~SoftBreakObserver observer}
- * (usually registered by the {@link module:enter/softbreak~SoftBreak SoftBreak feature}).
- *
- * @event module:engine/view/document~Document#event:softbreak
- * @param {module:engine/view/observer/domeventdata~DomEventData} data
- */

+ 10 - 1
packages/ckeditor5-enter/tests/enter.js

@@ -30,7 +30,7 @@ describe( 'Enter feature', () => {
 		const spy = editor.execute = sinon.spy();
 		const domEvt = getDomEvent();
 
-		viewDocument.fire( 'enter', new DomEventData( viewDocument, domEvt ) );
+		viewDocument.fire( 'enter', new DomEventData( viewDocument, domEvt, { isSoft: false } ) );
 
 		expect( spy.calledOnce ).to.be.true;
 		expect( spy.calledWithExactly( 'enter' ) ).to.be.true;
@@ -49,6 +49,15 @@ describe( 'Enter feature', () => {
 		sinon.assert.callOrder( executeSpy, scrollSpy );
 	} );
 
+	it( 'does not execute the command if soft enter should be used', () => {
+		const domEvt = getDomEvent();
+		const commandExecuteSpy = sinon.stub( editor.commands.get( 'enter' ), 'execute' );
+
+		viewDocument.fire( 'enter', new DomEventData( viewDocument, domEvt, { isSoft: true } ) );
+
+		sinon.assert.notCalled( commandExecuteSpy );
+	} );
+
 	function getDomEvent() {
 		return {
 			preventDefault: sinon.spy()

+ 44 - 0
packages/ckeditor5-enter/tests/shiftenter-integration.js

@@ -0,0 +1,44 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* global document */
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import ShiftEnter from '../src/shiftenter';
+
+import { getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
+
+describe( 'ShiftEnter integration', () => {
+	let editor, model, div;
+
+	beforeEach( () => {
+		div = document.createElement( 'div' );
+		div.innerHTML = '<p>First line.<br>Second line.</p>';
+
+		document.body.appendChild( div );
+
+		return ClassicEditor.create( div, { plugins: [ Paragraph, ShiftEnter ] } )
+			.then( newEditor => {
+				editor = newEditor;
+
+				model = editor.model;
+			} );
+	} );
+
+	afterEach( () => {
+		div.remove();
+		return editor.destroy();
+	} );
+
+	it( 'loads correct data', () => {
+		const options = { withoutSelection: true };
+
+		expect( getModelData( model, options) ).to.equal( '<paragraph>First line.<break></break>Second line.</paragraph>' );
+		expect( getViewData( editor.editing.view, options ) ).to.equal( '<p>First line.<br></br>Second line.</p>' );
+	} );
+} );

+ 18 - 9
packages/ckeditor5-enter/tests/softbreak.js

@@ -4,17 +4,17 @@
  */
 
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
-import SoftBreak from '../src/softbreak';
-import SoftBreakCommand from '../src/softbreakcommand';
+import ShiftEnter from '../src/shiftenter';
+import ShiftEnterCommand from '../src/shiftentercommand';
 import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
 
-describe( 'Soft Break feature', () => {
+describe( 'ShiftEnter feature', () => {
 	let editor, viewDocument;
 
 	beforeEach( () => {
 		return VirtualTestEditor
 			.create( {
-				plugins: [ SoftBreak ]
+				plugins: [ ShiftEnter ]
 			} )
 			.then( newEditor => {
 				editor = newEditor;
@@ -23,17 +23,17 @@ describe( 'Soft Break feature', () => {
 	} );
 
 	it( 'creates the commands', () => {
-		expect( editor.commands.get( 'softbreak' ) ).to.be.instanceof( SoftBreakCommand );
+		expect( editor.commands.get( 'shiftEnter' ) ).to.be.instanceof( ShiftEnterCommand );
 	} );
 
-	it( 'listens to the editing view softbreak event', () => {
+	it( 'listens to the editing view enter event', () => {
 		const spy = editor.execute = sinon.spy();
 		const domEvt = getDomEvent();
 
-		viewDocument.fire( 'softbreak', new DomEventData( viewDocument, domEvt ) );
+		viewDocument.fire( 'enter', new DomEventData( viewDocument, domEvt, { isSoft: true } ) );
 
 		expect( spy.calledOnce ).to.be.true;
-		expect( spy.calledWithExactly( 'softbreak' ) ).to.be.true;
+		expect( spy.calledWithExactly( 'shiftEnter' ) ).to.be.true;
 
 		expect( domEvt.preventDefault.calledOnce ).to.be.true;
 	} );
@@ -43,12 +43,21 @@ describe( 'Soft Break feature', () => {
 		const executeSpy = editor.execute = sinon.spy();
 		const scrollSpy = sinon.stub( editor.editing.view, 'scrollToTheSelection' );
 
-		viewDocument.fire( 'softbreak', new DomEventData( viewDocument, domEvt ) );
+		viewDocument.fire( 'enter', new DomEventData( viewDocument, domEvt, { isSoft: true } ) );
 
 		sinon.assert.calledOnce( scrollSpy );
 		sinon.assert.callOrder( executeSpy, scrollSpy );
 	} );
 
+	it( 'does not execute the command if hard enter should be used', () => {
+		const domEvt = getDomEvent();
+		const commandExecuteSpy = sinon.stub( editor.commands.get( 'shiftEnter' ), 'execute' );
+
+		viewDocument.fire( 'enter', new DomEventData( viewDocument, domEvt, { isSoft: false } ) );
+
+		sinon.assert.notCalled( commandExecuteSpy );
+	} );
+
 	function getDomEvent() {
 		return {
 			preventDefault: sinon.spy()

+ 49 - 37
packages/ckeditor5-enter/tests/softbreakcommand.js

@@ -4,30 +4,24 @@
  */
 
 import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
-import SoftBreakCommand from '../src/softbreakcommand';
-import SoftBreakEditing from '../src/softbreakediting';
 import InsertDelta from '@ckeditor/ckeditor5-engine/src/model/delta/insertdelta';
 import { getData, setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+import ShiftEnter from '../src/shiftenter';
 
-describe( 'SoftBreakCommand', () => {
+describe( 'ShiftEnterCommand', () => {
 	let editor, model, doc, schema, command;
 
 	beforeEach( () => {
-		return ModelTestEditor.create( {
-			plugins: [ SoftBreakEditing ]
-		} )
+		return ModelTestEditor.create( { plugins: [ ShiftEnter ] } )
 			.then( newEditor => {
 				editor = newEditor;
 				model = editor.model;
 				doc = model.document;
 
-				command = new SoftBreakCommand( editor );
-				editor.commands.add( 'softbreak', command );
+				command = editor.commands.get( 'shiftEnter' );
 
 				schema = model.schema;
 
-				// TODO softbreakediting include
-
 				// Note: We could use real names like 'paragraph', but that would make test patterns too long.
 				// Plus, this is actually a good test that the algorithm can be used for any model.
 				schema.register( 'img', { allowWhere: '$block' } );
@@ -50,31 +44,31 @@ describe( 'SoftBreakCommand', () => {
 			} );
 	} );
 
-	describe( 'SoftBreakCommand', () => {
+	describe( 'ShiftEnterCommand', () => {
 		it( 'soft breaks a block using parent batch', () => {
 			setData( model, '<p>foo[]</p>' );
 
 			model.change( writer => {
 				expect( writer.batch.deltas ).to.length( 0 );
-				editor.execute( 'softbreak' );
+				editor.execute( 'shiftEnter' );
 				expect( writer.batch.deltas ).to.length.above( 0 );
 			} );
 		} );
 
-		it( 'creates InsertDelta if soft break is at the beginning of block', () => {
+		it( 'creates InsertDelta if soft enter is at the beginning of block', () => {
 			setData( model, '<p>[]foo</p>' );
 
-			editor.execute( 'softbreak' );
+			editor.execute( 'shiftEnter' );
 
 			const deltas = Array.from( doc.history.getDeltas() );
 
 			expect( deltas[ deltas.length - 1 ] ).to.be.instanceof( InsertDelta );
 		} );
 
-		it( 'creates InsertDelta if soft break is at the end of block', () => {
+		it( 'creates InsertDelta if soft enter is at the end of block', () => {
 			setData( model, '<p>foo[]</p>' );
 
-			editor.execute( 'softbreak' );
+			editor.execute( 'shiftEnter' );
 
 			const deltas = Array.from( doc.history.getDeltas() );
 
@@ -85,47 +79,41 @@ describe( 'SoftBreakCommand', () => {
 	describe( 'execute()', () => {
 		describe( 'collapsed selection', () => {
 			test(
-				'does nothing in the root',
+				'inserts in the root',
 				'foo[]bar',
-				'foo[]bar'
+				'foo<break></break>[]bar'
 			);
 
 			test(
 				'inserts inside block',
 				'<p>x</p><p>foo[]bar</p><p>y</p>',
-				'<p>x</p><p>foo<br></br>[]bar</p><p>y</p>'
+				'<p>x</p><p>foo<break></break>[]bar</p><p>y</p>'
 			);
 
 			test(
 				'inserts at the end of block',
 				'<p>x</p><p>foo[]</p><p>y</p>',
-				'<p>x</p><p>foo<br></br>[]</p><p>y</p>'
+				'<p>x</p><p>foo<break></break>[]</p><p>y</p>'
 			);
 
 			test(
 				'inserts at the beginning of block',
 				'<p>x</p><p>[]foo</p><p>y</p>',
-				'<p>x</p><p><br></br>[]foo</p><p>y</p>'
+				'<p>x</p><p><break></break>[]foo</p><p>y</p>'
 			);
-
-			// test(
-			// 	'inserts new block after empty one',
-			// 	'<p>x</p><p>[]</p><p>y</p>',
-			// 	'<p>x</p><p></p><p>[]</p><p>y</p>'
-			// );
 		} );
 
 		describe( 'non-collapsed selection', () => {
 			test(
-				'only deletes the content when directly in the root',
+				'deletes the content and inserts the break when directly in the root',
 				'fo[ob]ar',
-				'fo[]ar'
+				'fo<break></break>[]ar'
 			);
 
 			test(
 				'deletes text and adds break',
 				'<p>ab[cd]ef</p><p>ghi</p>',
-				'<p>ab<br></br>[]ef</p><p>ghi</p>'
+				'<p>ab<break></break>[]ef</p><p>ghi</p>'
 			);
 
 			test(
@@ -135,9 +123,15 @@ describe( 'SoftBreakCommand', () => {
 			);
 
 			test(
-				'leaves one empty element after one was fully selected',
+				'does nothing for selection that contains more than one range',
+				'<p>[abc]</p><p>[def]</p>',
+				'<p>[abc]</p><p>[def]</p>'
+			);
+
+			test(
+				'inserts break in empty element after it was fully selected',
 				'<p>x</p><p>[abcdef]</p><p>y</p>',
-				'<p>x</p><p>[]</p><p>y</p>'
+				'<p>x</p><p><break></break>[]</p><p>y</p>'
 			);
 
 			test(
@@ -147,15 +141,27 @@ describe( 'SoftBreakCommand', () => {
 			);
 
 			test(
-				'should not break inline limit elements - collapsed',
+				'should insert the break in inline limit element - collapsed',
 				'<p><inlineLimit>foo[]bar</inlineLimit></p>',
-				'<p><inlineLimit>foo[]bar</inlineLimit></p>'
+				'<p><inlineLimit>foo<break></break>[]bar</inlineLimit></p>'
 			);
 
 			test(
-				'should not break inline limit elements',
+				'should insert the break in inline limit elements',
 				'<p><inlineLimit>foo[bar]baz</inlineLimit></p>',
-				'<p><inlineLimit>foo[]baz</inlineLimit></p>'
+				'<p><inlineLimit>foo<break></break>[]baz</inlineLimit></p>'
+			);
+
+			test(
+				'should insert the break at beginning of the inline limit elements',
+				'<p><inlineLimit>foo[bar]baz</inlineLimit></p>',
+				'<p><inlineLimit>foo<break></break>[]baz</inlineLimit></p>'
+			);
+
+			test(
+				'should insert the break at ending of the inline limit elements',
+				'<p><inlineLimit>foobaz[]</inlineLimit></p>',
+				'<p><inlineLimit>foobaz<break></break>[]</inlineLimit></p>'
 			);
 
 			test(
@@ -167,7 +173,13 @@ describe( 'SoftBreakCommand', () => {
 			test(
 				'should break paragraph in blockLimit',
 				'<blockLimit><p>foo[]bar</p></blockLimit>',
-				'<blockLimit><p>foo<br></br>[]bar</p></blockLimit>'
+				'<blockLimit><p>foo<break></break>[]bar</p></blockLimit>'
+			);
+
+			test(
+				'does nothing when break element cannot be inserted in specified context',
+				'<img>[]</img>',
+				'<img>[]</img>'
 			);
 
 			it( 'leaves one empty element after two were fully selected (backward)', () => {

+ 0 - 102
packages/ckeditor5-enter/tests/softbreakobserver.js

@@ -1,102 +0,0 @@
-/**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* globals document */
-
-import View from '@ckeditor/ckeditor5-engine/src/view/view';
-import SoftBreakObserver from '../src/softbreakobserver';
-import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
-import createViewRoot from '@ckeditor/ckeditor5-engine/tests/view/_utils/createroot';
-import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard';
-
-describe( 'SoftBreakObserver', () => {
-	let view, viewDocument;
-
-	beforeEach( () => {
-		view = new View();
-		viewDocument = view.document;
-		view.addObserver( SoftBreakObserver );
-	} );
-
-	// See #10.
-	it( 'can be initialized', () => {
-		expect( () => {
-			createViewRoot( viewDocument );
-			view.attachDomRoot( document.createElement( 'div' ) );
-		} ).to.not.throw();
-	} );
-
-	describe( 'enter event', () => {
-		it( 'is fired on keydown', () => {
-			const spy = sinon.spy();
-
-			viewDocument.on( 'softbreak', spy );
-
-			viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
-				keyCode: getCode( 'enter' ),
-				shiftKey: true
-			} ) );
-
-			expect( spy.calledOnce ).to.be.true;
-		} );
-
-		it( 'is not fired on keydown when keyCode does not match enter', () => {
-			const spy = sinon.spy();
-
-			viewDocument.on( 'softbreak', spy );
-
-			viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
-				keyCode: 1
-			} ) );
-
-			expect( spy.calledOnce ).to.be.false;
-		} );
-
-		it( 'is not fired on keydown when keyCode does not match shift+enter', () => {
-			const spy = sinon.spy();
-
-			viewDocument.on( 'softbreak', spy );
-
-			viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
-				keyCode: 'enter',
-				shiftKey: false
-			} ) );
-
-			expect( spy.calledOnce ).to.be.false;
-		} );
-
-		it( 'should stop keydown event when shift+enter event is stopped', () => {
-			const keydownSpy = sinon.spy();
-			viewDocument.on( 'keydown', keydownSpy );
-			viewDocument.on( 'softbreak', evt => evt.stop() );
-
-			viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
-				keyCode: getCode( 'enter' ),
-				shiftKey: true
-			} ) );
-
-			sinon.assert.notCalled( keydownSpy );
-		} );
-
-		it( 'should not stop keydown event when shift+enter event is not stopped', () => {
-			const keydownSpy = sinon.spy();
-			viewDocument.on( 'keydown', keydownSpy );
-			viewDocument.on( 'softbreak', evt => evt.stop() );
-
-			viewDocument.fire( 'keydown', new DomEventData( viewDocument, getDomEvent(), {
-				keyCode: getCode( 'x' ),
-				shiftKey: true
-			} ) );
-
-			sinon.assert.calledOnce( keydownSpy );
-		} );
-	} );
-
-	function getDomEvent() {
-		return {
-			preventDefault: sinon.spy()
-		};
-	}
-} );