Index: src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js
===================================================================
--- src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js	(revision 33440)
+++ src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js	(working copy)
@@ -14,39 +14,19 @@
 	tinymce.PluginManager.add( 'wptextpattern', function( editor ) {
 		var $$ = editor.$,
 			VK = tinymce.util.VK,
-			patterns = [],
-			canUndo = false;
-
-		/**
-		 * Add a pattern to format with a callback.
-		 *
-		 * @since 4.3.0
-		 *
-		 * @param {RegExp}   regExp   RegEx pattern.
-		 * @param {Function} callback Callback.
-		 */
-		function add( regExp, callback ) {
-			patterns.push( {
-				regExp: regExp,
-				callback: callback
-			} );
-		}
-
-		add( /^[*-]\s/, function() {
-			this.execCommand( 'InsertUnorderedList' );
-		} );
-
-		add( /^1[.)]\s/, function() {
-			this.execCommand( 'InsertOrderedList' );
-		} );
-
-		add( /^>\s/, function() {
-			this.formatter.toggle( 'blockquote' );
-		} );
-
-		add( /^(#{2,6})\s/, function() {
-			this.formatter.toggle( 'h' + arguments[1].length );
-		} );
+			canUndo = false,
+			spacePatterns = [
+				{ regExp: /^[*-]\s/, cmd: 'InsertUnorderedList' },
+				{ regExp: /^1[.)]\s/, cmd: 'InsertOrderedList' }
+			],
+			enterPatterns = [
+				{ start: '##', format: 'h2' },
+				{ start: '###', format: 'h3' },
+				{ start: '####', format: 'h4' },
+				{ start: '#####', format: 'h5' },
+				{ start: '######', format: 'h6' },
+				{ start: '>', format: 'blockquote' }
+			];
 
 		editor.on( 'selectionchange', function() {
 			canUndo = false;
@@ -57,24 +37,21 @@
 				editor.undoManager.undo();
 				event.preventDefault();
 			}
-		} );
 
-		editor.on( 'keyup', function( event ) {
-			var rng, node, text, parent, child;
-
-			if ( event.keyCode !== VK.SPACEBAR ) {
-				return;
+			if ( event.keyCode === VK.ENTER && ! VK.modifierPressed( event ) ) {
+				enter();
 			}
+		}, true );
 
-			rng = editor.selection.getRng();
-			node = rng.startContainer;
-
-			if ( ! node || node.nodeType !== 3 ) {
-				return;
+		editor.on( 'keyup', function( event ) {
+			if ( event.keyCode === VK.SPACEBAR || ! VK.modifierPressed( event ) ) {
+				space();
 			}
+		} );
 
-			text = node.nodeValue;
-			parent = editor.dom.getParent( node, 'p' );
+		function firstNode( node ) {
+			var parent = editor.dom.getParent( node, 'p' ),
+				child;
 
 			if ( ! parent ) {
 				return;
@@ -92,20 +69,26 @@
 				return;
 			}
 
-			if ( ! child.nodeValue ) {
+			if ( ! child.data ) {
 				child = child.nextSibling;
 			}
 
-			if ( child !== node ) {
+			return child;
+		}
+
+		function space() {
+			var rng = editor.selection.getRng(),
+				node = rng.startContainer,
+				text;
+
+			if ( firstNode( node ) !== node ) {
 				return;
 			}
 
-			tinymce.each( patterns, function( pattern ) {
-				var args,
-					replace = text.replace( pattern.regExp, function() {
-						args = arguments;
-						return '';
-					} );
+			text = node.nodeValue;
+
+			tinymce.each( spacePatterns, function( pattern ) {
+				var replace = text.replace( pattern.regExp, '' );
 
 				if ( text === replace ) {
 					return;
@@ -118,12 +101,13 @@
 				editor.undoManager.add();
 
 				editor.undoManager.transact( function() {
-					var $$parent;
+					var parent = node.parentNode,
+						$$parent;
 
 					if ( replace ) {
 						$$( node ).replaceWith( document.createTextNode( replace ) );
 					} else  {
-						$$parent = $$( node.parentNode );
+						$$parent = $$( parent );
 
 						$$( node ).remove();
 
@@ -133,8 +117,7 @@
 					}
 
 					editor.selection.setCursorLocation( parent );
-
-					pattern.callback.apply( editor, args );
+					editor.execCommand( pattern.cmd );
 				} );
 
 				// We need to wait for native events to be triggered.
@@ -144,6 +127,54 @@
 
 				return false;
 			} );
-		} );
+		}
+
+		function enter() {
+			var selection = editor.selection,
+				rng = selection.getRng(),
+				offset = rng.startOffset,
+				start = rng.startContainer,
+				node = firstNode( start ),
+				text = node.data,
+				i = enterPatterns.length,
+				pattern;
+
+			if ( ! node ) {
+				return;
+			}
+
+			while ( i-- ) {
+				 if ( text.indexOf( enterPatterns[ i ].start ) === 0 ) {
+				 	pattern = enterPatterns[ i ];
+				 	break;
+				 }
+			}
+
+			if ( ! pattern ) {
+				return;
+			}
+
+			if ( node === start && tinymce.trim( text ) === pattern.start ) {
+				return;
+			}
+
+			if ( node === start ) {
+				offset = Math.max( 0, offset - pattern.start.length );
+			}
+
+			editor.undoManager.add();
+
+			editor.undoManager.transact( function() {
+				node.deleteData( 0, pattern.start.length );
+
+				editor.formatter.apply( pattern.format, {}, start );
+
+				rng.setStart( start, offset );
+				rng.collapse( true );
+				selection.setRng( rng );
+
+				console.log( editor.getContent() );
+			} );
+		}
 	} );
 } )( window.tinymce, window.setTimeout );
Index: tests/qunit/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js
===================================================================
--- tests/qunit/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js	(revision 33440)
+++ tests/qunit/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js	(working copy)
@@ -144,4 +144,31 @@
 			assert.equal( editor.getContent(), '<ul>\n<li>tes</li>\n</ul>' );
 		}, assert.async() );
 	} );
+
+	QUnit.test( 'Heading 3', function( assert ) {
+		editor.setContent( '<p>### test</p>' );
+		editor.selection.setCursorLocation( editor.$( 'p' )[0].firstChild, 8 );
+
+		type( '\n', function() {
+			assert.equal( editor.getContent(), '<h3>test</h3>\n<p>&nbsp;</p>' );
+		}, assert.async() );
+	} );
+
+	QUnit.test( 'Heading 3 with elements.', function( assert ) {
+		editor.setContent( '<p>###<del>test</del></p>' );
+		editor.selection.setCursorLocation( editor.$( 'del' )[0].firstChild, 4 );
+
+		type( '\n', function() {
+			assert.equal( editor.getContent(), '<h3><del>test</del></h3>\n<p>&nbsp;</p>' );
+		}, assert.async() );
+	} );
+
+	QUnit.test( 'Don\'t convert without content', function( assert ) {
+		editor.setContent( '<p>###&nbsp;</p>' );
+		editor.selection.setCursorLocation( editor.$( 'p' )[0].firstChild, 4 );
+
+		type( '\n', function() {
+			assert.equal( editor.getContent(), '<p>###&nbsp;</p>\n<p>&nbsp;</p>' );
+		}, assert.async() );
+	} );
 } )( window.jQuery, window.QUnit, window.tinymce, window.Utils.type, window.setTimeout );
