Index: src/wp-includes/class-wp-editor.php
===================================================================
--- src/wp-includes/class-wp-editor.php	(revision 32634)
+++ src/wp-includes/class-wp-editor.php	(working copy)
@@ -368,6 +368,7 @@
 						'wplink',
 						'wpdialogs',
 						'wpview',
+						'wptextpattern'
 					);
 
 					if ( ! self::$has_medialib ) {
Index: src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js
===================================================================
--- src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js	(revision 0)
+++ src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js	(working copy)
@@ -0,0 +1,62 @@
+( function( tinymce ) {
+	tinymce.PluginManager.add( 'wptextpattern', function( editor ) {
+		var $$ = editor.$;
+		var patterns = [];
+
+		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.execCommand( 'mceBlockQuote' );
+		} );
+
+		editor.on( 'keyup', function( event ) {
+			var node;
+
+			if ( event.keyCode !== tinymce.util.VK.SPACEBAR ) {
+				return;
+			}
+
+			node = editor.selection.getRng().startContainer;
+
+			if ( node.nodeType !== 3 ) {
+				return;
+			}
+
+			tinymce.each( patterns, function( pattern ) {
+				var text = node.nodeValue,
+					replace;
+
+				if ( node.parentNode.firstChild !== node ) {
+					return;
+				}
+
+				replace = text.replace( pattern.regExp, '' );
+
+				if ( text === replace ) {
+					return;
+				}
+
+				editor.undoManager.transact( function() {
+					$$( node.parentNode ).replaceWith( replace ? document.createTextNode( replace ) : document.createElement( 'BR' ) );
+
+					pattern.callback.apply( editor );
+				} );
+
+				return false;
+			} );
+		} );
+	} );
+} )( window.tinymce );
Index: tests/qunit/editor/js/utils.js
===================================================================
--- tests/qunit/editor/js/utils.js	(revision 32634)
+++ tests/qunit/editor/js/utils.js	(working copy)
@@ -131,7 +131,18 @@
 
 	// TODO: Replace this with the new event logic in 3.5
 	function type(chr) {
-		var editor = tinymce.activeEditor, keyCode, charCode, evt, startElm, rng;
+		var editor = tinymce.activeEditor, keyCode, charCode, evt, startElm, rng, textNode;
+
+		function charCodeToKeyCode(charCode) {
+			var lookup = {
+				'0': 48, '1': 49, '2': 50, '3': 51, '4': 52, '5': 53, '6': 54, '7': 55, '8': 56, '9': 57,'a': 65, 'b': 66, 'c': 67,
+				'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73, 'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81,
+				'r': 82, 's': 83, 't': 84, 'u': 85,	'v': 86, 'w': 87, 'x': 88, 'y': 89, ' ': 32, ',': 188, '-': 189, '.': 190, '/': 191, '\\': 220,
+				'[': 219, ']': 221, '\'': 222, ';': 186, '=': 187, ')': 41
+			};
+
+			return lookup[String.fromCharCode(charCode)];
+		}
 
 		function fakeEvent(target, type, evt) {
 			editor.dom.fire(target, type, evt);
@@ -139,7 +150,8 @@
 
 		// Numeric keyCode
 		if (typeof(chr) == "number") {
-			charCode = keyCode = chr;
+			charCode = chr;
+			keyCode = charCodeToKeyCode(charCode);
 		} else if (typeof(chr) == "string") {
 			// String value
 			if (chr == '\b') {
@@ -150,10 +162,18 @@
 				charCode = chr.charCodeAt(0);
 			} else {
 				charCode = chr.charCodeAt(0);
-				keyCode = charCode;
+				keyCode = charCodeToKeyCode(charCode);
 			}
 		} else {
 			evt = chr;
+
+			if (evt.charCode) {
+				chr = String.fromCharCode(evt.charCode);
+			}
+
+			if (evt.keyCode) {
+				keyCode = evt.keyCode;
+			}
 		}
 
 		evt = evt || {keyCode: keyCode, charCode: charCode};
@@ -196,11 +216,14 @@
 				if (rng.startContainer.nodeType == 3 && rng.collapsed) {
 					rng.startContainer.insertData(rng.startOffset, chr);
 					rng.setStart(rng.startContainer, rng.startOffset + 1);
-					rng.collapse(true);
-					editor.selection.setRng(rng);
 				} else {
-					rng.insertNode(editor.getDoc().createTextNode(chr));
+					textNode = editor.getDoc().createTextNode(chr);
+					rng.insertNode(textNode);
+					rng.setStart(textNode, 1);
 				}
+
+				rng.collapse(true);
+				editor.selection.setRng(rng);
 			}
 		}
 
Index: tests/qunit/index.html
===================================================================
--- tests/qunit/index.html	(revision 32634)
+++ tests/qunit/index.html	(working copy)
@@ -39,5 +39,14 @@
 		<script src="wp-includes/js/shortcode.js"></script>
 		<script src="wp-admin/js/customize-controls.js"></script>
 		<script src="wp-admin/js/customize-controls-utils.js"></script>
+
+		<!-- TinyMCE -->
+
+		<script src="../../src/wp-includes/js/tinymce/tinymce.js"></script>
+
+		<script src="editor/js/utils.js"></script>
+
+		<script src="wp-includes/js/tinymce/plugins/wptextpattern/plugin.js"></script>
+
 	</body>
 </html>
Index: tests/qunit/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js
===================================================================
--- tests/qunit/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js	(revision 0)
+++ tests/qunit/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js	(working copy)
@@ -0,0 +1,47 @@
+( function( $, _type ) {
+
+	var editor;
+
+	function type( string ) {
+		tinymce.each( string.split( '' ), _type );
+	}
+
+	module( 'tinymce.plugins.wptextpattern', {
+		beforeEach: function() {
+			stop();
+
+			$( '#qunit-fixture' ).append( '<textarea id="editor">' );
+
+			tinymce.init( {
+				selector: '#editor',
+				plugins: 'wptextpattern',
+				init_instance_callback: function() {
+					editor = arguments[0];
+					editor.focus();
+					start();
+				}
+			} );
+		},
+		afterEach: function( assert ) {
+			editor.remove();
+		}
+	} );
+
+	test( 'unordered list', function() {
+		type( '* ' );
+		equal( editor.getContent(), '<ul>\n<li></li>\n</ul>' );
+	} );
+
+	test( 'ordered list', function() {
+		type( '1. ' );
+		equal( editor.getContent(), '<ol>\n<li></li>\n</ol>' );
+	} );
+
+	test( 'ordered list with text', function() {
+		type( 'test' );
+		editor.selection.setCursorLocation();
+		type( '1. ' );
+		equal( editor.getContent(), '<ol>\n<li>test</li>\n</ol>' );
+	} );
+
+} )( window.jQuery, window.Utils.type );
