Index: src/wp-admin/css/common.css
===================================================================
--- src/wp-admin/css/common.css	(revision 41811)
+++ src/wp-admin/css/common.css	(working copy)
@@ -1466,17 +1466,17 @@
 .wrap #templateside .notice {
 	display: block;
 	margin: 0;
-	padding: 5px 12px;
+	padding: 5px 8px;
 	font-weight: 600;
 	text-decoration: none;
 }
 
 .wrap #templateside span.notice {
-  margin-left: -12px;
+	margin-left: -12px;
 }
 
 #templateside li.notice a {
-  padding: 0;
+	padding: 0;
 }
 
 /* Update icon. */
@@ -3036,11 +3036,122 @@
 	width: 97%;
 	height: calc( 100vh - 280px );
 }
+
 #templateside {
 	margin-top: 31px;
-	overflow: scroll;
+	overflow: auto;
+	padding: 2px;
+	height: calc(100vh - 280px);
 }
+#templateside ul ul {
+	padding-left: 12px;
+}
 
+/*
+ * Styles for Theme and Plugin editors.
+ */
+
+/* Hide collapsed items. */
+[role="treeitem"][aria-expanded="false"] > ul {
+	display: none;
+}
+
+/* Use arrow dashicons for folder states, but hide from screen readers. */
+[role="treeitem"] span[aria-hidden] {
+	display: inline;
+	font-family: dashicons;
+	font-size: 20px;
+	position: absolute;
+	pointer-events: none;
+}
+[role="treeitem"][aria-expanded="false"] > .folder-label .icon:after {
+	content: "\f139";
+}
+[role="treeitem"][aria-expanded="true"] > .folder-label .icon:after {
+	content: "\f140";
+}
+[role="treeitem"] .folder-label {
+	display: block;
+	padding: 3px 3px 3px 12px;
+}
+
+/* Remove outline, and create our own focus and hover styles */
+[role="treeitem"] {
+	outline: 0;
+}
+[role="treeitem"] .folder-label.focus {
+	color: #124964;
+	box-shadow: 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, .8);
+}
+[role="treeitem"].hover,
+[role="treeitem"] .folder-label.hover {
+	background-color: #DDDDDD;
+}
+
+.tree-folder {
+	margin: 0;
+	position: relative;
+}
+[role="treeitem"] li {
+	position: relative;
+}
+
+/* Styles for folder indicators/depth */
+.tree-folder .tree-folder::after {
+	content: ' ';
+	display: block;
+	position: absolute;
+	left: 2px;
+	border-left: 1px solid #ccc;
+	top: -13px;
+	bottom: 10px;
+}
+.tree-folder > li::before {
+	content: ' ';
+	position: absolute;
+	display: block;
+	border-left: 1px solid #ccc;
+	left: 2px;
+	top: -5px;
+	height: 18px;
+	width: 7px;
+	border-bottom: 1px solid #ccc;
+}
+.tree-folder > li::after {
+	content: ' ';
+	position: absolute;
+	display: block;
+	border-left: 1px solid #ccc;
+	left: 2px;
+	bottom: -7px;
+	top: 0;
+}
+
+/* current-file needs to adjustment for .notice styles */
+#templateside .current-file {
+	margin: -4px 0 -2px;
+}
+.tree-folder > .current-file::before {
+	left: 4px;
+	height: 15px;
+	width: 6px;
+	border-left: none;
+	top: 3px;
+}
+.tree-folder > .current-file::after {
+	bottom: -4px;
+	height: 7px;
+	left: 2px;
+	top: auto;
+}
+
+/* Lines shouldn't continue on last item */
+.tree-folder > li:last-child::after,
+.tree-folder li:last-child > .tree-folder::after {
+	display: none;
+}
+
+
 #theme-plugin-editor-label {
 	display: inline-block;
 	margin-bottom: 1em;
@@ -3653,6 +3764,35 @@
 		width: 100%;
 	}
 
+	#templateside ul ul {
+		padding-left: 1.5em;
+	}
+	[role="treeitem"] .folder-label {
+		display: block;
+		padding: 5px;
+	}
+	.tree-folder > li::before,
+	.tree-folder > li::after,
+	.tree-folder .tree-folder::after {
+		left: -8px;
+	}
+	.tree-folder > li::before {
+		top: 0px;
+		height: 13px;
+	}
+	.tree-folder > .current-file::before {
+		left: -5px;
+		top: 7px;
+		width: 4px;
+	}
+	.tree-folder > .current-file::after {
+		height: 9px;
+		left: -8px;
+	}
+	.wrap #templateside span.notice {
+		margin-left: -14px;
+	}
+
 	.fileedit-sub .alignright {
 		margin-top: 15px;
 	}
Index: src/wp-admin/includes/misc.php
===================================================================
--- src/wp-admin/includes/misc.php	(revision 41811)
+++ src/wp-admin/includes/misc.php	(working copy)
@@ -270,6 +270,167 @@
 }
 
 /**
+ * Makes a tree structure for the Theme Editor's file list.
+ *
+ * @since 4.9.0
+ * @access private
+ *
+ * @param string $allowed_files List of theme file paths.
+ *
+ * @return array Tree structure for listing theme files.
+ */
+function wp_make_theme_file_tree( $allowed_files ) {
+	$tree_list = array();
+	foreach ( $allowed_files as $file_name => $absolute_filename ) {
+		$list = explode( '/', $file_name );
+		$last_dir = &$tree_list;
+		foreach ( $list as $dir ) {
+			$last_dir =& $last_dir[ $dir ];
+		}
+		$last_dir = $file_name;
+	}
+	return $tree_list;
+}
+
+/**
+ * Outputs the formatted file list for the Theme Editor.
+ *
+ * @since 4.9.0
+ * @access private
+ *
+ * @param array  $tree  List of file/folder paths.
+ * @param string $label Name of file or folder to print.
+ * @param int    $level Current level.
+ * @param int    $index Current level.
+ * @param int    $size  Current level.
+ */
+function wp_print_theme_file_tree( $tree, $label = false, $level = 2, $index = 1, $size = 1 ) {
+	global $relative_file, $stylesheet;
+
+	if ( is_array( $tree ) ) {
+		$index = 0;
+		$size = count( $tree );
+		foreach ( $tree as $label => $theme_file ) :
+			$index++;
+			if ( ! is_array( $theme_file ) ) {
+				wp_print_theme_file_tree( $theme_file, $label, $level, $index, $size );
+				continue;
+			}
+			?>
+			<li role="treeitem" aria-expanded="true" tabindex="-1"
+				aria-level="<?php echo (string) $level; ?>"
+				aria-setsize="<?php echo (string) $size; ?>"
+				aria-posinset="<?php echo (string) $index; ?>">
+				<span class="folder-label"><?php echo esc_html( $label ); ?> <span class="screen-reader-text">folder</span><span aria-hidden="true" class="icon"></span></span>
+				<ul role="group" class="tree-folder"><?php wp_print_theme_file_tree( $theme_file, false, $level + 1, $index, $size ); ?></ul>
+			</li>
+			<?php
+		endforeach;
+	} else {
+		$filename = $tree;
+		?>
+		<li role="none" class="<?php echo $relative_file === $filename ? 'current-file' : ''; ?>">
+			<a role="treeitem" tabindex="<?php echo $relative_file === $filename ? '0' : '-1'; ?>"
+				href="theme-editor.php?file=<?php echo urlencode( $tree ); ?>&amp;theme=<?php echo urlencode( $stylesheet ); ?>"
+				aria-level="<?php echo (string) $level; ?>"
+				aria-setsize="<?php echo (string) $size; ?>"
+				aria-posinset="<?php echo (string) $index; ?>">
+				<?php
+				$file_description = esc_html( get_file_description( $filename ) );
+				if ( $file_description !== $filename && $file_description !== basename($filename) ) {
+					$file_description .= '<br /><span class="nonessential">(' . esc_html( $filename ) . ')</span>';
+				}
+
+				if ( $relative_file === $filename ) {
+					echo '<span class="notice notice-info">' . $file_description . '</span>';
+				} else {
+					echo $file_description;
+				}
+				?>
+			</a>
+		</li>
+		<?php
+	}
+}
+
+/**
+ * Makes a tree structure for the Plugin Editor's file list.
+ *
+ * @since 4.9.0
+ * @access private
+ *
+ * @param string $plugin_editable_files List of plugin file paths.
+ *
+ * @return array Tree structure for listing plugin files.
+ */
+function wp_make_plugin_file_tree( $plugin_editable_files ) {
+	$tree_list = array();
+	foreach ( $plugin_editable_files as $plugin_file ) {
+		$list = explode( '/', preg_replace( '#^.+?/#', '', $plugin_file ) );
+		$last_dir = &$tree_list;
+		foreach ( $list as $dir ) {
+			$last_dir =& $last_dir[ $dir ];
+		}
+		$last_dir = $plugin_file;
+	}
+	return $tree_list;
+}
+
+/**
+ * Outputs the formatted file list for the Plugin Editor.
+ *
+ * @since 4.9.0
+ * @access private
+ *
+ * @param array  $tree  List of file/folder paths.
+ * @param string $label Name of file or folder to print.
+ * @param int    $level Current level.
+ * @param int    $index Current level.
+ * @param int    $size  Current level.
+ */
+function wp_print_plugin_file_tree( $tree, $label = false, $level = 2, $index = 1, $size = 1 ) {
+	global $file, $plugin;
+	if ( is_array( $tree ) ) {
+		$index = 0;
+		$size = count( $tree );
+		foreach ( $tree as $label => $plugin_file ) :
+			$index++;
+			if ( ! is_array( $plugin_file ) ) {
+				wp_print_plugin_file_tree( $plugin_file, $label, $level, $index, $size );
+				continue;
+			}
+			?>
+			<li role="treeitem" aria-expanded="true" tabindex="-1"
+				aria-level="<?php echo (string) $level; ?>"
+				aria-setsize="<?php echo (string) $size; ?>"
+				aria-posinset="<?php echo (string) $index; ?>">
+				<span class="folder-label"><?php echo esc_html( $label ); ?> <span class="screen-reader-text">folder</span><span aria-hidden="true" class="icon"></span></span>
+				<ul role="group" class="tree-folder"><?php wp_print_plugin_file_tree( $plugin_file, false, $level + 1, $index, $size ); ?></ul>
+			</li>
+			<?php
+		endforeach;
+	} else {
+		?>
+		<li role="none" class="<?php echo $file === $tree ? 'current-file' : ''; ?>">
+			<a role="treeitem" tabindex="<?php echo $file === $tree ? '0' : '-1'; ?>"
+				href="plugin-editor.php?file=<?php echo urlencode( $tree ); ?>&amp;plugin=<?php echo urlencode( $plugin ); ?>"
+				aria-level="<?php echo (string) $level; ?>"
+				aria-setsize="<?php echo (string) $size; ?>"
+				aria-posinset="<?php echo (string) $index; ?>">
+				<?php
+				if ( $file === $tree ) {
+					echo '<span class="notice notice-info">' . esc_html( $label ) . '</span>';
+				} else {
+					echo esc_html( $label );
+				}
+				?>
+			</a>
+		</li>
+		<?php
+	}
+}
+
+/**
  * Flushes rewrite rules if siteurl, home or page_on_front changed.
  *
  * @since 2.1.0
Index: src/wp-admin/js/tree-links.js
===================================================================
--- src/wp-admin/js/tree-links.js	(nonexistent)
+++ src/wp-admin/js/tree-links.js	(working copy)
@@ -0,0 +1,261 @@
+/*
+*   This content is licensed according to the W3C Software License at
+*   https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+*
+*   File:   TreeLinks.js
+*
+*   Desc:   Tree widget that implements ARIA Authoring Practices
+*           for a tree being used as a file viewer
+*
+*   Author: Jon Gunderson, Ku Ja Eun and Nicholas Hoyt
+*/
+
+/**
+ * ARIA Treeview example
+ * @function onload
+ * @desc  after page has loaded initializ all treeitems based on the role=treeitem
+ */
+
+window.addEventListener('load', function () {
+
+    var trees = document.querySelectorAll('[role="tree"]');
+
+    for (var i = 0; i < trees.length; i++) {
+        var t = new TreeLinks(trees[i]);
+        t.init();
+    }
+
+});
+
+/*
+*   @constructor
+*
+*   @desc
+*       Tree item object for representing the state and user interactions for a
+*       tree widget
+*
+*   @param node
+*       An element with the role=tree attribute
+*/
+
+var TreeLinks = function (node) {
+    // Check whether node is a DOM element
+    if (typeof node !== 'object') {
+        return;
+    }
+
+    this.domNode = node;
+
+    this.treeitems = [];
+    this.firstChars = [];
+
+    this.firstTreeitem = null;
+    this.lastTreeitem = null;
+
+};
+
+TreeLinks.prototype.init = function () {
+
+    function findTreeitems(node, tree, group) {
+
+        var elem = node.firstElementChild;
+        var ti = group;
+
+        while (elem) {
+
+            if ((elem.tagName.toLowerCase() === 'li' && elem.firstElementChild.tagName.toLowerCase() === 'span') || elem.tagName.toLowerCase() === 'a') {
+                ti = new TreeitemLink(elem, tree, group);
+                ti.init();
+                tree.treeitems.push(ti);
+                tree.firstChars.push(ti.label.substring(0, 1).toLowerCase());
+            }
+
+            if (elem.firstElementChild) {
+                findTreeitems(elem, tree, ti);
+            }
+
+            elem = elem.nextElementSibling;
+        }
+    }
+
+    // initialize pop up menus
+    if (!this.domNode.getAttribute('role')) {
+        this.domNode.setAttribute('role', 'tree');
+    }
+
+    findTreeitems(this.domNode, this, false);
+
+    this.updateVisibleTreeitems();
+
+    this.firstTreeitem.domNode.tabIndex = 0;
+
+};
+
+TreeLinks.prototype.setFocusToItem = function (treeitem) {
+
+    for (var i = 0; i < this.treeitems.length; i++) {
+        var ti = this.treeitems[i];
+
+        if (ti === treeitem) {
+            ti.domNode.tabIndex = 0;
+            ti.domNode.focus();
+        }
+        else {
+            ti.domNode.tabIndex = -1;
+        }
+    }
+
+};
+
+TreeLinks.prototype.setFocusToNextItem = function (currentItem) {
+
+    var nextItem = false;
+
+    for (var i = (this.treeitems.length - 1); i >= 0; i--) {
+        var ti = this.treeitems[i];
+        if (ti === currentItem) {
+            break;
+        }
+        if (ti.isVisible) {
+            nextItem = ti;
+        }
+    }
+
+    if (nextItem) {
+        this.setFocusToItem(nextItem);
+    }
+
+};
+
+TreeLinks.prototype.setFocusToPreviousItem = function (currentItem) {
+
+    var prevItem = false;
+
+    for (var i = 0; i < this.treeitems.length; i++) {
+        var ti = this.treeitems[i];
+        if (ti === currentItem) {
+            break;
+        }
+        if (ti.isVisible) {
+            prevItem = ti;
+        }
+    }
+
+    if (prevItem) {
+        this.setFocusToItem(prevItem);
+    }
+};
+
+TreeLinks.prototype.setFocusToParentItem = function (currentItem) {
+
+    if (currentItem.groupTreeitem) {
+        this.setFocusToItem(currentItem.groupTreeitem);
+    }
+};
+
+TreeLinks.prototype.setFocusToFirstItem = function () {
+    this.setFocusToItem(this.firstTreeitem);
+};
+
+TreeLinks.prototype.setFocusToLastItem = function () {
+    this.setFocusToItem(this.lastTreeitem);
+};
+
+TreeLinks.prototype.expandTreeitem = function (currentItem) {
+
+    if (currentItem.isExpandable) {
+        currentItem.domNode.setAttribute('aria-expanded', true);
+        this.updateVisibleTreeitems();
+    }
+
+};
+
+TreeLinks.prototype.expandAllSiblingItems = function (currentItem) {
+    for (var i = 0; i < this.treeitems.length; i++) {
+        var ti = this.treeitems[i];
+
+        if ((ti.groupTreeitem === currentItem.groupTreeitem) && ti.isExpandable) {
+            this.expandTreeitem(ti);
+        }
+    }
+
+};
+
+TreeLinks.prototype.collapseTreeitem = function (currentItem) {
+
+    var groupTreeitem = false;
+
+    if (currentItem.isExpanded()) {
+        groupTreeitem = currentItem;
+    }
+    else {
+        groupTreeitem = currentItem.groupTreeitem;
+    }
+
+    if (groupTreeitem) {
+        groupTreeitem.domNode.setAttribute('aria-expanded', false);
+        this.updateVisibleTreeitems();
+        this.setFocusToItem(groupTreeitem);
+    }
+
+};
+
+TreeLinks.prototype.updateVisibleTreeitems = function () {
+
+    this.firstTreeitem = this.treeitems[0];
+
+    for (var i = 0; i < this.treeitems.length; i++) {
+        var ti = this.treeitems[i];
+
+        var parent = ti.domNode.parentNode;
+
+        ti.isVisible = true;
+
+        while (parent && (parent !== this.domNode)) {
+
+            if (parent.getAttribute('aria-expanded') == 'false') {
+                ti.isVisible = false;
+            }
+            parent = parent.parentNode;
+        }
+
+        if (ti.isVisible) {
+            this.lastTreeitem = ti;
+        }
+    }
+
+};
+
+TreeLinks.prototype.setFocusByFirstCharacter = function (currentItem, char) {
+    var start, index, char = char.toLowerCase();
+
+    // Get start index for search based on position of currentItem
+    start = this.treeitems.indexOf(currentItem) + 1;
+    if (start === this.treeitems.length) {
+        start = 0;
+    }
+
+    // Check remaining slots in the menu
+    index = this.getIndexFirstChars(start, char);
+
+    // If not found in remaining slots, check from beginning
+    if (index === -1) {
+        index = this.getIndexFirstChars(0, char);
+    }
+
+    // If match was found...
+    if (index > -1) {
+        this.setFocusToItem(this.treeitems[index]);
+    }
+};
+
+TreeLinks.prototype.getIndexFirstChars = function (startIndex, char) {
+    for (var i = startIndex; i < this.firstChars.length; i++) {
+        if (this.treeitems[i].isVisible) {
+            if (char === this.firstChars[i]) {
+                return i;
+            }
+        }
+    }
+    return -1;
+};
\ No newline at end of file
Index: src/wp-admin/js/treeitem-links.js
===================================================================
--- src/wp-admin/js/treeitem-links.js	(nonexistent)
+++ src/wp-admin/js/treeitem-links.js	(working copy)
@@ -0,0 +1,269 @@
+/*
+*   This content is licensed according to the W3C Software License at
+*   https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+*
+*   File:   TreeitemLink.js
+*
+*   Desc:   Treeitem widget that implements ARIA Authoring Practices
+*           for a tree being used as a file viewer
+*
+*   Author: Jon Gunderson, Ku Ja Eun and Nicholas Hoyt
+*/
+
+/*
+*   @constructor
+*
+*   @desc
+*       Treeitem object for representing the state and user interactions for a
+*       treeItem widget
+*
+*   @param node
+*       An element with the role=tree attribute
+*/
+
+var TreeitemLink = function (node, treeObj, group) {
+
+    // Check whether node is a DOM element
+    if (typeof node !== 'object') {
+        return;
+    }
+
+    node.tabIndex = -1;
+    this.tree = treeObj;
+    this.groupTreeitem = group;
+    this.domNode = node;
+    this.label = node.textContent.trim();
+    this.stopDefaultClick = false;
+
+    if (node.getAttribute('aria-label')) {
+        this.label = node.getAttribute('aria-label').trim();
+    }
+
+    this.isExpandable = false;
+    this.isVisible = false;
+    this.inGroup = false;
+
+    if (group) {
+        this.inGroup = true;
+    }
+
+    var elem = node.firstElementChild;
+
+    while (elem) {
+
+        if (elem.tagName.toLowerCase() == 'ul') {
+            elem.setAttribute('role', 'group');
+            this.isExpandable = true;
+            break;
+        }
+
+        elem = elem.nextElementSibling;
+    }
+
+    this.keyCode = Object.freeze({
+        RETURN: 13,
+        SPACE: 32,
+        PAGEUP: 33,
+        PAGEDOWN: 34,
+        END: 35,
+        HOME: 36,
+        LEFT: 37,
+        UP: 38,
+        RIGHT: 39,
+        DOWN: 40
+    });
+};
+
+TreeitemLink.prototype.init = function () {
+    this.domNode.tabIndex = -1;
+
+    if (!this.domNode.getAttribute('role')) {
+        this.domNode.setAttribute('role', 'treeitem');
+    }
+
+    this.domNode.addEventListener('keydown', this.handleKeydown.bind(this));
+    this.domNode.addEventListener('click', this.handleClick.bind(this));
+    this.domNode.addEventListener('focus', this.handleFocus.bind(this));
+    this.domNode.addEventListener('blur', this.handleBlur.bind(this));
+
+    if (this.isExpandable) {
+        this.domNode.firstElementChild.addEventListener('mouseover', this.handleMouseOver.bind(this));
+        this.domNode.firstElementChild.addEventListener('mouseout', this.handleMouseOut.bind(this));
+    }
+    else {
+        this.domNode.addEventListener('mouseover', this.handleMouseOver.bind(this));
+        this.domNode.addEventListener('mouseout', this.handleMouseOut.bind(this));
+    }
+};
+
+TreeitemLink.prototype.isExpanded = function () {
+
+    if (this.isExpandable) {
+        return this.domNode.getAttribute('aria-expanded') === 'true';
+    }
+
+    return false;
+
+};
+
+/* EVENT HANDLERS */
+
+TreeitemLink.prototype.handleKeydown = function (event) {
+    var tgt = event.currentTarget,
+        flag = false,
+        char = event.key,
+        clickEvent;
+
+    function isPrintableCharacter(str) {
+        return str.length === 1 && str.match(/\S/);
+    }
+
+    function printableCharacter(item) {
+        if (char == '*') {
+            item.tree.expandAllSiblingItems(item);
+            flag = true;
+        }
+        else {
+            if (isPrintableCharacter(char)) {
+                item.tree.setFocusByFirstCharacter(item, char);
+                flag = true;
+            }
+        }
+    }
+
+    this.stopDefaultClick = false;
+
+    if (event.altKey || event.ctrlKey || event.metaKey) {
+        return;
+    }
+
+    if (event.shift) {
+        if (event.keyCode == this.keyCode.SPACE || event.keyCode == this.keyCode.RETURN) {
+            event.stopPropagation();
+            this.stopDefaultClick = true;
+        }
+        else {
+            if (isPrintableCharacter(char)) {
+                printableCharacter(this);
+            }
+        }
+    }
+    else {
+        switch (event.keyCode) {
+            case this.keyCode.SPACE:
+            case this.keyCode.RETURN:
+                if (this.isExpandable) {
+                    if (this.isExpanded()) {
+                        this.tree.collapseTreeitem(this);
+                    }
+                    else {
+                        this.tree.expandTreeitem(this);
+                    }
+                    flag = true;
+                }
+                else {
+                    event.stopPropagation();
+                    this.stopDefaultClick = true;
+                }
+                break;
+
+            case this.keyCode.UP:
+                this.tree.setFocusToPreviousItem(this);
+                flag = true;
+                break;
+
+            case this.keyCode.DOWN:
+                this.tree.setFocusToNextItem(this);
+                flag = true;
+                break;
+
+            case this.keyCode.RIGHT:
+                if (this.isExpandable) {
+                    if (this.isExpanded()) {
+                        this.tree.setFocusToNextItem(this);
+                    }
+                    else {
+                        this.tree.expandTreeitem(this);
+                    }
+                }
+                flag = true;
+                break;
+
+            case this.keyCode.LEFT:
+                if (this.isExpandable && this.isExpanded()) {
+                    this.tree.collapseTreeitem(this);
+                    flag = true;
+                }
+                else {
+                    if (this.inGroup) {
+                        this.tree.setFocusToParentItem(this);
+                        flag = true;
+                    }
+                }
+                break;
+
+            case this.keyCode.HOME:
+                this.tree.setFocusToFirstItem();
+                flag = true;
+                break;
+
+            case this.keyCode.END:
+                this.tree.setFocusToLastItem();
+                flag = true;
+                break;
+
+            default:
+                if (isPrintableCharacter(char)) {
+                    printableCharacter(this);
+                }
+                break;
+        }
+    }
+
+    if (flag) {
+        event.stopPropagation();
+        event.preventDefault();
+    }
+};
+
+TreeitemLink.prototype.handleClick = function (event) {
+
+    // only process click events that directly happened on this treeitem
+    if (event.target !== this.domNode && event.target !== this.domNode.firstElementChild) {
+        return;
+    }
+
+    if (this.isExpandable) {
+        if (this.isExpanded()) {
+            this.tree.collapseTreeitem(this);
+        }
+        else {
+            this.tree.expandTreeitem(this);
+        }
+        event.stopPropagation();
+    }
+};
+
+TreeitemLink.prototype.handleFocus = function (event) {
+    var node = this.domNode;
+    if (this.isExpandable) {
+        node = node.firstElementChild;
+    }
+    node.classList.add('focus');
+};
+
+TreeitemLink.prototype.handleBlur = function (event) {
+    var node = this.domNode;
+    if (this.isExpandable) {
+        node = node.firstElementChild;
+    }
+    node.classList.remove('focus');
+};
+
+TreeitemLink.prototype.handleMouseOver = function (event) {
+    event.currentTarget.classList.add('hover');
+};
+
+TreeitemLink.prototype.handleMouseOut = function (event) {
+    event.currentTarget.classList.remove('hover');
+};
\ No newline at end of file
Index: src/wp-admin/plugin-editor.php
===================================================================
--- src/wp-admin/plugin-editor.php	(revision 41811)
+++ src/wp-admin/plugin-editor.php	(working copy)
@@ -144,6 +144,9 @@
 	wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function( $ ) { wp.themePluginEditor.init( $( "#template" ), %s ); } )', wp_json_encode( $settings ) ) );
 	wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'wp.themePluginEditor.themeOrPlugin = "plugin";' ) );
 
+	wp_enqueue_script( 'tree-links' );
+	wp_add_inline_script( 'tree-links', 'jQuery( function( $ ) { $(\'#templateside [role="group"]\').parent().attr(\'aria-expanded\', false); $(\'#templateside .notice\').parents(\'[aria-expanded]\').attr(\'aria-expanded\', true); } )' );
+
 	require_once(ABSPATH . 'wp-admin/admin-header.php');
 
 	update_recently_edited(WP_PLUGIN_DIR . '/' . $file);
@@ -231,7 +234,7 @@
 </div>
 
 <div id="templateside">
-	<h2><?php _e( 'Plugin Files' ); ?></h2>
+	<h2 id="plugin-files-label"><?php _e( 'Plugin Files' ); ?></h2>
 
 	<?php
 	$plugin_editable_files = array();
@@ -241,12 +244,14 @@
 		}
 	}
 	?>
-	<ul>
-	<?php foreach ( $plugin_editable_files as $plugin_file ) : ?>
-		<li class="<?php echo esc_attr( $file === $plugin_file ? 'notice notice-info' : '' ); ?>">
-			<a href="plugin-editor.php?file=<?php echo urlencode( $plugin_file ); ?>&amp;plugin=<?php echo urlencode( $plugin ); ?>"><?php echo esc_html( preg_replace( '#^.+?/#', '', $plugin_file ) ); ?></a>
-		</li>
-	<?php endforeach; ?>
+	<ul role="tree" aria-labelledby="plugin-files-label">
+	<li role="treeitem" tabindex="-1" aria-expanded="true"
+		aria-level="1"
+		aria-posinset="1"
+		aria-setsize="1">
+		<ul role="group" style="padding-left: 0;">
+			<?php wp_print_plugin_file_tree( wp_make_plugin_file_tree( $plugin_editable_files ) ); ?>
+		</ul>
 	</ul>
 </div>
 <form name="template" id="template" action="plugin-editor.php" method="post">
Index: src/wp-admin/theme-editor.php
===================================================================
--- src/wp-admin/theme-editor.php	(revision 41811)
+++ src/wp-admin/theme-editor.php	(working copy)
@@ -89,6 +89,14 @@
 	}
 }
 
+// Move functions.php and style.css to the top.
+if ( isset( $allowed_files['functions.php'] ) ) {
+	$allowed_files = array( 'functions.php' => $allowed_files['functions.php'] ) + $allowed_files;
+}
+if ( isset( $allowed_files['style.css'] ) ) {
+	$allowed_files = array( 'style.css' => $allowed_files['style.css'] ) + $allowed_files;
+}
+
 if ( empty( $file ) ) {
 	$relative_file = 'style.css';
 	$file = $allowed_files['style.css'];
@@ -129,6 +137,9 @@
 	wp_add_inline_script( 'wp-theme-plugin-editor', sprintf( 'jQuery( function( $ ) { wp.themePluginEditor.init( $( "#template" ), %s ); } )', wp_json_encode( $settings ) ) );
 	wp_add_inline_script( 'wp-theme-plugin-editor', 'wp.themePluginEditor.themeOrPlugin = "theme";' );
 
+	wp_enqueue_script( 'tree-links' );
+	wp_add_inline_script( 'tree-links', 'jQuery( function( $ ) { $(\'#templateside [role="group"]\').parent().attr(\'aria-expanded\', false); $(\'#templateside .notice\').parents(\'[aria-expanded]\').attr(\'aria-expanded\', true); } )' );
+
 	require_once( ABSPATH . 'wp-admin/admin-header.php' );
 
 	update_recently_edited( $file );
@@ -205,63 +216,33 @@
 if ( $theme->errors() )
 	echo '<div class="error"><p><strong>' . __( 'This theme is broken.' ) . '</strong> ' . $theme->errors()->get_error_message() . '</p></div>';
 ?>
-	<div id="templateside">
-<?php
-if ( $allowed_files ) :
-	$previous_file_type = '';
-
-	foreach ( $allowed_files as $filename => $absolute_filename ) :
-		$file_type = substr( $filename, strrpos( $filename, '.' ) );
-
-		if ( $file_type !== $previous_file_type ) {
-			if ( '' !== $previous_file_type ) {
-				echo "\t</ul>\n";
-			}
-
-			switch ( $file_type ) {
-				case '.php':
-					if ( $has_templates || $theme->parent() ) :
-						echo "\t<h2>" . __( 'Templates' ) . "</h2>\n";
-						if ( $theme->parent() ) {
-							echo '<p class="howto">' . sprintf( __( 'This child theme inherits templates from a parent theme, %s.' ),
-								sprintf( '<a href="%s">%s</a>',
-									self_admin_url( 'theme-editor.php?theme=' . urlencode( $theme->get_template() ) ),
-									$theme->parent()->display( 'Name' )
-								)
-							) . "</p>\n";
-						}
-					endif;
-					break;
-				case '.css':
-					echo "\t<h2>" . _x( 'Styles', 'Theme stylesheets in theme editor' ) . "</h2>\n";
-					break;
-				default:
-					/* translators: %s: file extension */
-					echo "\t<h2>" . sprintf( __( '%s files' ), $file_type ) . "</h2>\n";
-					break;
-			}
-
-			echo "\t<ul>\n";
+<div id="templateside">
+	<h2 id="theme-files-label"><?php _e( 'Theme Files' ); ?></h2>
+	<?php
+	if ( $has_templates || $theme->parent() ) :
+		if ( $theme->parent() ) {
+			/* translators: %s: link to edit parent theme */
+			echo '<p class="howto">' . sprintf( __( 'This child theme inherits templates from a parent theme, %s.' ),
+				sprintf( '<a href="%s">%s</a>',
+					self_admin_url( 'theme-editor.php?theme=' . urlencode( $theme->get_template() ) ),
+					$theme->parent()->display( 'Name' )
+				)
+			) . "</p>\n";
 		}
+	endif;
+	?>
+	<ul role="tree" aria-labelledby="theme-files-label">
+		<li role="treeitem" tabindex="-1" aria-expanded="true"
+			aria-level="1"
+			aria-posinset="1"
+			aria-setsize="1">
+			<ul role="group" style="padding-left: 0;">
+				<?php wp_print_theme_file_tree( wp_make_theme_file_tree( $allowed_files ) ); ?>
+			</ul>
+		</li>
+	</ul>
+</div>
 
-		$file_description = esc_html( get_file_description( $filename ) );
-		if ( $filename !== basename( $absolute_filename ) || $file_description !== $filename ) {
-			$file_description .= '<br /><span class="nonessential">(' . esc_html( $filename ) . ')</span>';
-		}
-
-		if ( $absolute_filename === $file ) {
-			$file_description = '<span class="notice notice-info">' . $file_description . '</span>';
-		}
-
-		$previous_file_type = $file_type;
-?>
-		<li><a href="theme-editor.php?file=<?php echo urlencode( $filename ) ?>&amp;theme=<?php echo urlencode( $stylesheet ) ?>"><?php echo $file_description; ?></a></li>
-<?php
-	endforeach;
-?>
-</ul>
-<?php endif; ?>
-</div>
 <?php if ( $error ) :
 	echo '<div class="error"><p>' . __('Oops, no such file exists! Double check the name and try again, merci.') . '</p></div>';
 else : ?>
Index: src/wp-includes/script-loader.php
===================================================================
--- src/wp-includes/script-loader.php	(revision 41811)
+++ src/wp-includes/script-loader.php	(working copy)
@@ -886,6 +886,9 @@
 		$scripts->add( 'media-gallery', "/wp-admin/js/media-gallery$suffix.js", array('jquery'), false, 1 );
 
 		$scripts->add( 'svg-painter', '/wp-admin/js/svg-painter.js', array( 'jquery' ), false, 1 );
+
+		$scripts->add( 'treeitem-links', "/wp-admin/js/treeitem-links$suffix.js", array( 'jquery' ), false, 1 );
+		$scripts->add( 'tree-links', "/wp-admin/js/tree-links$suffix.js", array( 'treeitem-links' ), false, 1 );
 	}
 }
 
