diff --git a/demo/kitchen-sink/docs/c9search.c9search_results b/demo/kitchen-sink/docs/c9search.c9search_results
index bf0fdb87..0ad9ccd4 100644
--- a/demo/kitchen-sink/docs/c9search.c9search_results
+++ b/demo/kitchen-sink/docs/c9search.c9search_results
@@ -1,4 +1,4 @@
-Searching for 'var' in /workspace/configs
+Searching for var in/.c9/metadata/workspace/pluginsregexp, case sensitive, whole word
 
 configs/default.js:
     1: var fs = require("fs");
diff --git a/lib/ace/autocomplete/popup.js b/lib/ace/autocomplete/popup.js
index 891cf1e8..76deb274 100644
--- a/lib/ace/autocomplete/popup.js
+++ b/lib/ace/autocomplete/popup.js
@@ -209,6 +209,9 @@ var AcePopup = function(parentNode) {
     }
 
     // public
+    popup.isOpen = false;
+    popup.isTopdown = false;
+    
     popup.data = [];
     popup.setData = function(list) {
         popup.data = list || [];
@@ -233,26 +236,33 @@ var AcePopup = function(parentNode) {
                 popup._signal("select");
         }
     };
+    
+    popup.on("changeSelection", function() {
+        if (popup.isOpen)
+            popup.setRow(popup.selection.lead.row);
+    });
 
     popup.hide = function() {
         this.container.style.display = "none";
         this._signal("hide");
         popup.isOpen = false;
     };
-    popup.show = function(pos, lineHeight) {
+    popup.show = function(pos, lineHeight, topdownOnly) {
         var el = this.container;
         var screenHeight = window.innerHeight;
         var renderer = this.renderer;
         // var maxLines = Math.min(renderer.$maxLines, this.session.getLength());
         var maxH = renderer.$maxLines * lineHeight * 1.4;
         var top = pos.top + this.$borderSize;
-        if (top + maxH > screenHeight - lineHeight) {
+        if (top + maxH > screenHeight - lineHeight && !topdownOnly) {
             el.style.top = "";
             el.style.bottom = screenHeight - top + "px";
+            popup.isTopdown = false;
         } else {
             top += lineHeight;
             el.style.top = top + "px";
             el.style.bottom = "";
+            popup.isTopdown = true;
         }
 
         el.style.left = pos.left + "px";
diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js
index 9c663d18..0ba9956c 100644
--- a/lib/ace/commands/default_commands.js
+++ b/lib/ace/commands/default_commands.js
@@ -35,12 +35,13 @@ var lang = require("../lib/lang");
 var config = require("../config");
 
 function bindKey(win, mac) {
-    return {
-        win: win,
-        mac: mac
-    };
+    return {win: win, mac: mac};
 }
 
+/*
+    multiSelectAction: "forEach"|"forEachLine"|function|undefined,
+    scrollIntoView: true|"cursor"|"center"|"selectionPart"
+*/
 exports.commands = [{
     name: "showSettingsMenu",
     bindKey: bindKey("Ctrl-,", "Command-,"),
@@ -75,21 +76,46 @@ exports.commands = [{
     name: "fold",
     bindKey: bindKey("Alt-L|Ctrl-F1", "Command-Alt-L|Command-F1"),
     exec: function(editor) { editor.session.toggleFold(false); },
+    scrollIntoView: "center",
     readOnly: true
 }, {
     name: "unfold",
     bindKey: bindKey("Alt-Shift-L|Ctrl-Shift-F1", "Command-Alt-Shift-L|Command-Shift-F1"),
     exec: function(editor) { editor.session.toggleFold(true); },
+    scrollIntoView: "center",
+    readOnly: true
+}, {
+    name: "toggleFoldWidget",
+    bindKey: bindKey("F2", "F2"),
+    exec: function(editor) { editor.session.toggleFoldWidget(); },
+    scrollIntoView: "center",
+    readOnly: true
+}, {
+    name: "toggleParentFoldWidget",
+    bindKey: bindKey("Alt-F2", "Alt-F2"),
+    exec: function(editor) { editor.session.toggleFoldWidget(true); },
+    scrollIntoView: "center",
     readOnly: true
 }, {
     name: "foldall",
-    bindKey: bindKey("Alt-0", "Command-Option-0"),
+    bindKey: bindKey("Ctrl-Alt-0", "Ctrl-Command-Option-0"),
     exec: function(editor) { editor.session.foldAll(); },
+    scrollIntoView: "center",
+    readOnly: true
+}, {
+    name: "foldOther",
+    bindKey: bindKey("Alt-0", "Command-Option-0"),
+    exec: function(editor) { 
+        editor.session.foldAll();
+        editor.session.unfold(editor.selection.getAllRanges());
+    },
+    scrollIntoView: "center",
     readOnly: true
 }, {
     name: "unfoldall",
     bindKey: bindKey("Alt-Shift-0", "Command-Option-Shift-0"),
     exec: function(editor) { editor.session.unfold(); },
+    scrollIntoView: "center",
     readOnly: true
 }, {
     name: "findnext",
@@ -345,7 +371,8 @@ exports.commands = [{
     name: "togglecomment",
     bindKey: bindKey("Ctrl-/", "Command-/"),
     exec: function(editor) { editor.toggleCommentLines(); },
-    multiSelectAction: "forEachLine"
+    multiSelectAction: "forEachLine",
+    scrollIntoView: "selectionPart"
 }, {
     name: "toggleBlockComment",
     bindKey: bindKey("Ctrl-Shift-/", "Command-Shift-/"),
@@ -439,26 +466,31 @@ exports.commands = [{
     name: "outdent",
     bindKey: bindKey("Shift-Tab", "Shift-Tab"),
     exec: function(editor) { editor.blockOutdent(); },
-    multiSelectAction: "forEach"
+    multiSelectAction: "forEach",
+    scrollIntoView: "selectionPart"
 }, {
     name: "indent",
     bindKey: bindKey("Tab", "Tab"),
     exec: function(editor) { editor.indent(); },
-    multiSelectAction: "forEach"
-},{
+    multiSelectAction: "forEach",
+    scrollIntoView: "selectionPart"
+}, {
     name: "blockoutdent",
     bindKey: bindKey("Ctrl-[", "Ctrl-["),
     exec: function(editor) { editor.blockOutdent(); },
-    multiSelectAction: "forEachLine"
-},{
+    multiSelectAction: "forEachLine",
+    scrollIntoView: "selectionPart"
+}, {
     name: "blockindent",
     bindKey: bindKey("Ctrl-]", "Ctrl-]"),
     exec: function(editor) { editor.blockIndent(); },
-    multiSelectAction: "forEachLine"
+    multiSelectAction: "forEachLine",
+    scrollIntoView: "selectionPart"
 }, {
     name: "insertstring",
     exec: function(editor, str) { editor.insert(str); },
-    multiSelectAction: "forEach"
+    multiSelectAction: "forEach",
+    scrollIntoView: "cursor"
 }, {
     name: "inserttext",
     exec: function(editor, args) {
diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css
index b291c11f..65d80d3d 100644
--- a/lib/ace/css/editor.css
+++ b/lib/ace/css/editor.css
@@ -158,7 +158,7 @@
     z-index: 1;
     position: absolute;
     overflow: hidden;
-    white-space: nowrap;
+    white-space: pre;
     height: 100%;
     width: 100%;
     -moz-box-sizing: border-box;
diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js
index a9b2ddd1..e6725a51 100644
--- a/lib/ace/edit_session/folding.js
+++ b/lib/ace/edit_session/folding.js
@@ -108,6 +108,18 @@ function Folding() {
 
         return foundFolds;
     };
+
+    this.getFoldsInRangeList = function(ranges) {
+        if (Array.isArray(ranges)) {
+            var folds = [];
+            ranges.forEach(function(range) {
+                folds = folds.concat(this.getFoldsInRange(range));
+            }, this);
+        } else {
+            var folds = this.getFoldsInRange(ranges);
+        }
+        return folds;
+    }
     
     /*
      * Returns all folds in the document
@@ -413,7 +425,7 @@ function Folding() {
     };
 
     this.expandFold = function(fold) {
-        this.removeFold(fold);        
+        this.removeFold(fold);
         fold.subFolds.forEach(function(subFold) {
             fold.restoreRange(subFold);
             this.addFold(subFold);
@@ -441,8 +453,8 @@ function Folding() {
             range = Range.fromPoints(location, location);
         else
             range = location;
-
-        folds = this.getFoldsInRange(range);
+        
+        folds = this.getFoldsInRangeList(range);
         if (expandInner) {
             this.removeFolds(folds);
         } else {
@@ -450,7 +462,7 @@ function Folding() {
             // expandFolds several times.
             while (folds.length) {
                 this.expandFolds(folds);
-                folds = this.getFoldsInRange(range);
+                folds = this.getFoldsInRangeList(range);
             }
         }
     };
@@ -636,17 +648,16 @@ function Folding() {
                 continue;
 
             var range = this.getFoldWidgetRange(row);
-            var rangeEndRow = range.end.row;
             // sometimes range can be incompatible with existing fold
             // TODO change addFold to return null istead of throwing
             if (range && range.isMultiLine()
-                && rangeEndRow <= endRow
+                && range.end.row <= endRow
                 && range.start.row >= startRow
             ) try {
                 var fold = this.addFold("...", range);
                 fold.collapseChildren = depth;
                 // addFold can change the range
-                row = rangeEndRow;
+                row = range.end.row;
             } catch(e) {}
         }
     };
@@ -727,25 +738,34 @@ function Folding() {
     }
 
     this.onFoldWidgetClick = function(row, e) {
+        e = e.domEvent;
+        var options = {
+            children: e.shiftKey,
+            all: e.ctrlKey || e.metaKey,
+            siblings: e.altKey
+        };
+        
+        var range = this.$toggleFoldWidget(row, options);
+        if (!range)
+            (e.target || e.srcElement).className += " ace_invalid";
+    };
+    
+    this.$toggleFoldWidget = function(row, options) {
         var type = this.getFoldWidget(row);
         var line = this.getLine(row);
-        e = e.domEvent;
-        var children = e.shiftKey;
-        var all = e.ctrlKey || e.metaKey;
-        var siblings = e.altKey;
 
         var dir = type === "end" ? -1 : 1;
         var fold = this.getFoldAt(row, dir === -1 ? 0 : line.length, dir);
 
         if (fold) {
-            if (children || all)
+            if (options.children || options.all)
                 this.removeFold(fold);
             else
                 this.expandFold(fold);
             return;
         }
 
-        var range = this.getFoldWidgetRange(row);
+        var range = this.getFoldWidgetRange(row, true);
         // sometimes singleline folds can be missed by the code above
         if (range && !range.isMultiLine()) {
             fold = this.getFoldAt(range.start.row, range.start.column, 1);
@@ -755,24 +775,48 @@ function Folding() {
             }
         }
         
-        if (siblings) {
+        if (options.siblings) {
             var data = this.getParentFoldRangeData(row);
             if (data.range) {
                 var startRow = data.range.start.row + 1;
                 var endRow = data.range.end.row;
             }
-            this.foldAll(startRow, endRow, all ? 10000 : 0);
-        } else if (children) {
-            var endRow = range ? range.end.row : this.getLength();
-            this.foldAll(row + 1, range.end.row, all ? 10000 : 0);
+            this.foldAll(startRow, endRow, options.all ? 10000 : 0);
+        } else if (options.children) {
+            endRow = range ? range.end.row : this.getLength();
+            this.foldAll(row + 1, range.end.row, options.all ? 10000 : 0);
         } else if (range) {
-            if (all) 
+            if (options.all) 
                 range.collapseChildren = 10000;
             this.addFold("...", range);
         }
         
-        if (!range)
-            (e.target || e.srcElement).className += " ace_invalid"
+        return range;
+    };
+    
+    
+    
+    this.toggleFoldWidget = function(toggleParent) {
+        var row = this.selection.getCursor().row;
+        row = this.getRowFoldStart(row);
+        var range = this.$toggleFoldWidget(row, {});
+        
+        if (range)
+            return;
+        // handle toggleParent
+        var data = this.getParentFoldRangeData(row, true);
+        range = data.range || data.firstRange;
+        
+        if (range) {
+            row = range.start.row;
+            var fold = this.getFoldAt(row, this.getLine(row).length, 1);
+
+            if (fold) {
+                this.removeFold(fold);
+            } else {
+                this.addFold("...", range);
+            }
+        }
     };
 
     this.updateFoldWidgets = function(e) {
diff --git a/lib/ace/editor.js b/lib/ace/editor.js
index 4ff568c9..5ce8135a 100644
--- a/lib/ace/editor.js
+++ b/lib/ace/editor.js
@@ -170,12 +170,33 @@ var Editor = function(renderer, session) {
             command: commadEvent.command || {},
             args: commadEvent.args
         };
+        
+        var command = this.curOp.command;
+        if (command && command.scrollIntoView)
+            this.$blockScrolling++;
 
         this.selections.push(this.selection.toJSON());
     };
 
     this.endOperation = function() {
         if (this.curOp) {
+            var command = this.curOp.command;
+            if (command && command.scrollIntoView) {
+                this.$blockScrolling--;
+                switch (command.scrollIntoView) {
+                    case "center":
+                        this.renderer.scrollCursorIntoView(null, 0.5);
+                        break;
+                    case "cursor":
+                        this.renderer.scrollCursorIntoView();
+                        break;
+                    case "selectionPart":
+                        this.renderer.scrollCursorIntoView();
+                        break;
+                    default:
+                        break;
+                }
+            }
             this.prevOp = this.curOp;
             this.curOp = null;
         }
@@ -1643,8 +1664,8 @@ var Editor = function(renderer, session) {
         var range = this.getSelectionRange().collapseRows();
 
         return {
-            first: range.start.row,
-            last: range.end.row
+            first: this.session.getRowFoldStart(range.start.row),
+            last: this.session.getRowFoldEnd(range.end.row)
         };
     };
 
diff --git a/lib/ace/keyboard/hash_handler.js b/lib/ace/keyboard/hash_handler.js
index baa88158..cb02f170 100644
--- a/lib/ace/keyboard/hash_handler.js
+++ b/lib/ace/keyboard/hash_handler.js
@@ -119,6 +119,9 @@ function HashHandler(config, platform) {
             if (typeof command === "function")
                 command = { exec: command };
 
+            if (typeof command !== "object")
+                return;
+
             if (!command.name)
                 command.name = name;
 
diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js
index c1130d8f..cd8622d8 100644
--- a/lib/ace/layer/gutter.js
+++ b/lib/ace/layer/gutter.js
@@ -75,8 +75,7 @@ var Gutter = function(parentEl) {
 
     this.setAnnotations = function(annotations) {
         // iterate over sparse array
-        this.$annotations = []
-        var rowInfo, row;
+        this.$annotations = [];
         for (var i = 0; i < annotations.length; i++) {
             var annotation = annotations[i];
             var row = annotation.row;
@@ -112,7 +111,7 @@ var Gutter = function(parentEl) {
         } else if (delta.action == "removeText" || delta.action == "removeLines") {
             this.$annotations.splice(firstRow, len + 1, null);
         } else {
-            var args = Array(len + 1);
+            var args = new Array(len + 1);
             args.unshift(firstRow, 1);
             this.$annotations.splice.apply(this.$annotations, args);
         }
@@ -121,13 +120,16 @@ var Gutter = function(parentEl) {
     this.update = function(config) {
         var firstRow = config.firstRow;
         var lastRow = config.lastRow;
-        var fold = this.session.getNextFoldLine(firstRow);
+        var session = this.session;
+        var fold = session.getNextFoldLine(firstRow);
         var foldStart = fold ? fold.start.row : Infinity;
-        var foldWidgets = this.$showFoldWidgets && this.session.foldWidgets;
-        var breakpoints = this.session.$breakpoints;
-        var decorations = this.session.$decorations;
-        var firstLineNumber = this.session.$firstLineNumber;
+        var foldWidgets = this.$showFoldWidgets && session.foldWidgets;
+        var breakpoints = session.$breakpoints;
+        var decorations = session.$decorations;
+        var firstLineNumber = session.$firstLineNumber;
         var lastLineNumber = 0;
+        
+        var gutterRenderer = session.gutterRenderer;
 
         var cell = null;
         var index = -1;
@@ -135,7 +137,7 @@ var Gutter = function(parentEl) {
         while (true) {
             if (row > foldStart) {
                 row = fold.end.row + 1;
-                fold = this.session.getNextFoldLine(row, fold);
+                fold = session.getNextFoldLine(row, fold);
                 foldStart = fold ? fold.start.row : Infinity;
             }
             if (row > lastRow) {
@@ -166,19 +168,15 @@ var Gutter = function(parentEl) {
             if (cell.element.className != className)
                 cell.element.className = className;
 
-            var height = this.session.getRowLength(row) * config.lineHeight + "px";
+            var height = session.getRowLength(row) * config.lineHeight + "px";
             if (height != cell.element.style.height)
                 cell.element.style.height = height;
 
-            var text = lastLineNumber = row + firstLineNumber;
-            if (text != cell.textNode.data)
-                cell.textNode.data  = text;
-
             if (foldWidgets) {
                 var c = foldWidgets[row];
                 // check if cached value is invalidated and we need to recompute
                 if (c == null)
-                    c = foldWidgets[row] = this.session.getFoldWidget(row);
+                    c = foldWidgets[row] = session.getFoldWidget(row);
             }
 
             if (c) {
@@ -198,21 +196,30 @@ var Gutter = function(parentEl) {
                 if (cell.foldWidget.style.height != height)
                     cell.foldWidget.style.height = height;
             } else {
-                if (cell.foldWidget != null) {
+                if (cell.foldWidget) {
                     cell.element.removeChild(cell.foldWidget);
                     cell.foldWidget = null;
                 }
             }
+            
+            var text = lastLineNumber = gutterRenderer
+                ? gutterRenderer.getText(session, row)
+                : row + firstLineNumber;
+            if (text != cell.textNode.data)
+                cell.textNode.data = text;
 
             row++;
         }
 
         this.element.style.height = config.minHeight + "px";
 
-        if (this.$fixedWidth || this.session.$useWrapMode)
-            lastLineNumber = this.session.getLength();
+        if (this.$fixedWidth || session.$useWrapMode)
+            lastLineNumber = session.getLength();
 
-        var gutterWidth = lastLineNumber.toString().length * config.characterWidth;
+        var gutterWidth = gutterRenderer 
+            ? gutterRenderer.getWidth(session, lastLineNumber, config)
+            : lastLineNumber.toString().length * config.characterWidth;
+        
         var padding = this.$padding || this.$computePadding();
         gutterWidth += padding.left + padding.right;
         if (gutterWidth !== this.gutterWidth && !isNaN(gutterWidth)) {
diff --git a/lib/ace/lib/lang.js b/lib/ace/lib/lang.js
index 4e6ebf2c..4405b5c2 100644
--- a/lib/ace/lib/lang.js
+++ b/lib/ace/lib/lang.js
@@ -78,14 +78,16 @@ exports.copyArray = function(array){
 };
 
 exports.deepCopy = function (obj) {
-    if (typeof obj != "object") {
+    if (typeof obj !== "object" || !obj)
+        return obj;
+    var cons = obj.constructor;
+    if (cons === RegExp)
         return obj;
-    }
     
-    var copy = obj.constructor();
+    var copy = cons();
     for (var key in obj) {
-        if (typeof obj[key] == "object") {
-            copy[key] = this.deepCopy(obj[key]);
+        if (typeof obj[key] === "object") {
+            copy[key] = exports.deepCopy(obj[key]);
         } else {
             copy[key] = obj[key];
         }
@@ -170,6 +172,10 @@ exports.deferredCall = function(fcn) {
         timer = null;
         return deferred;
     };
+    
+    deferred.isPending = function() {
+        return timer;
+    };
 
     return deferred;
 };
@@ -183,15 +189,15 @@ exports.delayedCall = function(fcn, defaultTimeout) {
     };
 
     var _self = function(timeout) {
+        if (timer == null)
+            timer = setTimeout(callback, timeout || defaultTimeout);
+    };
+
+    _self.delay = function(timeout) {
         timer && clearTimeout(timer);
         timer = setTimeout(callback, timeout || defaultTimeout);
     };
-
-    _self.delay = _self;
-    _self.schedule = function(timeout) {
-        if (timer == null)
-            timer = setTimeout(callback, timeout || 0);
-    };
+    _self.schedule = _self;
 
     _self.call = function() {
         this.cancel();
diff --git a/lib/ace/mode/_test/highlight_rules_test.js b/lib/ace/mode/_test/highlight_rules_test.js
index be6656d8..c9d2aa15 100644
--- a/lib/ace/mode/_test/highlight_rules_test.js
+++ b/lib/ace/mode/_test/highlight_rules_test.js
@@ -50,9 +50,13 @@ function generateTestData() {
             var data = tokenizer.getLineTokens(line, state);
             var tmp = [];
             tmp.push(JSON.stringify(data.state));
+            var tokenizedLine = "";
             data.tokens.forEach(function(x) {
+                tokenizedLine += x.value;
                 tmp.push(JSON.stringify([x.type, x.value]));
             });
+            if (tokenizedLine != line)
+                tmp.push(JSON.stringify(line));
             state = data.state;
             return tmp.join(",\n  ");
         });
@@ -85,12 +89,15 @@ function testMode(modeName, i) {
         lineData.values = [];
         lineData.types = [];
         lineData.state = lineData.shift();
+        var line = null;
+        if (typeof lineData[lineData.length - 1] == "string")
+            line = lineData.pop()
         lineData.forEach(function(x) {
             lineData.types.push(x[0]);
             lineData.values.push(x[1]);
         });
-
-        var line = lineData.values.join("");
+        if (typeof line != "string")
+            line = lineData.values.join("");
 
         var tokens = tokenizer.getLineTokens(line, state);
         var values = tokens.tokens.map(function(x) {return x.value;});
@@ -98,7 +105,7 @@ function testMode(modeName, i) {
 
         var success = true;
         var err = testEqual([
-            lineData.state, tokens.state,
+            JSON.stringify(lineData.state), JSON.stringify(tokens.state),
             lineData.types, types,
             lineData.values, values]);
         
@@ -107,7 +114,7 @@ function testMode(modeName, i) {
             throw "error";
         }
 
-        state = lineData.state;
+        state = tokens.state;
     });
 }
 function testEqual(a) {
diff --git a/lib/ace/mode/_test/tokens_c9search.json b/lib/ace/mode/_test/tokens_c9search.json
index 98052746..902d8dce 100644
--- a/lib/ace/mode/_test/tokens_c9search.json
+++ b/lib/ace/mode/_test/tokens_c9search.json
@@ -1,104 +1,131 @@
 [[
-   "start",
-  ["text","Searching for 'var' in /workspace/configs"]
+   ["start",{}],
+  ["text","Searching for '"],
+  ["text","var"],
+  ["text","' in"],
+  ["text"," /.c9/metadata/workspace/plugins "],
+  ["text","(regexp, case sensitive, whole word)"],
+  "Searching for \u0001var\u0001 in\u0001/.c9/metadata/workspace/plugins\u0001\u0001regexp, case sensitive, whole word\u0001"
 ],[
-   "start"
+   ["start",{}]
 ],[
-   "start",
+   ["start",{}],
   ["string","configs/default.js"],
   ["text",":"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","    1"],
   ["c9searchresults.text",": "],
-  ["c9searchresults.text","var fs = require(\"fs\");"]
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," fs = require(\"fs\");"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t2"],
   ["c9searchresults.text",": "],
-  ["c9searchresults.text","var argv = require('optimist').argv;"]
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," argv = require('optimist').argv;"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t3"],
   ["c9searchresults.text",": "],
-  ["c9searchresults.text","var path = require(\"path\");"]
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," path = require(\"path\");"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t5"],
   ["c9searchresults.text",": "],
-  ["c9searchresults.text","var clientExtensions = {};"]
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," clientExtensions = {};"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t6"],
   ["c9searchresults.text",": "],
-  ["c9searchresults.text","var clientDirs = fs.readdirSync(__dirname + \"/../plugins-client\");"]
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," clientDirs = fs.readdirSync(__dirname + \"/../plugins-client\");"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t7"],
   ["c9searchresults.text",": "],
-  ["c9searchresults.text","for (var i = 0; i < clientDirs.length; i++) {"]
+  ["c9searchresults.text","for ("],
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," i = 0; i < clientDirs.length; i++) {"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t8"],
-  ["c9searchresults.text",":     "],
-  ["c9searchresults.text","var dir = clientDirs[i];"]
+  ["c9searchresults.text",": "],
+  ["c9searchresults.text","    "],
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," dir = clientDirs[i];"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t12"],
-  ["c9searchresults.text",":     "],
-  ["c9searchresults.text","var name = dir.split(\".\")[1];"]
+  ["c9searchresults.text",": "],
+  ["c9searchresults.text","    "],
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," name = dir.split(\".\")[1];"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t16"],
   ["c9searchresults.text",": "],
-  ["c9searchresults.text","var projectDir = (argv.w && path.resolve(process.cwd(), argv.w)) || process.cwd();"]
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," projectDir = (argv.w && path.resolve(process.cwd(), argv.w)) || process.cwd();"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t17"],
   ["c9searchresults.text",": "],
-  ["c9searchresults.text","var fsUrl = \"/workspace\";"]
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," fsUrl = \"/workspace\";"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t19"],
   ["c9searchresults.text",": "],
-  ["c9searchresults.text","var port = argv.p || process.env.PORT || 3131;"]
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," port = argv.p || process.env.PORT || 3131;"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t20"],
   ["c9searchresults.text",": "],
-  ["c9searchresults.text","var host = argv.l || \"localhost\";"]
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," host = argv.l || \"localhost\";"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t22"],
   ["c9searchresults.text",": "],
-  ["c9searchresults.text","var config = {"]
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," config = {"]
 ],[
-   "start"
+   ["start",{}]
 ],[
-   "start",
+   ["start",{}],
   ["string","configs/local.js"],
   ["text",":"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t2"],
   ["c9searchresults.text",": "],
-  ["c9searchresults.text","var config = require(\"./default\");"]
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," config = require(\"./default\");"]
 ],[
-   "start"
+   ["start",{}]
 ],[
-   "start",
+   ["start",{}],
   ["string","configs/packed.js"],
   ["text",":"]
 ],[
-   "start",
+   ["start",{}],
   ["c9searchresults.constant.numeric","\t1"],
   ["c9searchresults.text",": "],
-  ["c9searchresults.text","var config = require(\"./default\");"]
+  ["c9searchresults.keyword","var"],
+  ["c9searchresults.text"," config = require(\"./default\");"]
 ],[
-   "start"
+   ["start",{}]
 ],[
-   "start"
+   ["start",{}]
 ],[
-   "start",
-  ["text","Found 15 matches in 3 files"]
+   ["start",{}],
+  ["text","Found "],
+  ["constant.numeric","15"],
+  ["text"," matches in "],
+  ["constant.numeric","3"],
+  ["text"," files"]
 ]]
\ No newline at end of file
diff --git a/lib/ace/mode/c9search_highlight_rules.js b/lib/ace/mode/c9search_highlight_rules.js
index df31112b..07273bbb 100644
--- a/lib/ace/mode/c9search_highlight_rules.js
+++ b/lib/ace/mode/c9search_highlight_rules.js
@@ -32,8 +32,15 @@ define(function(require, exports, module) {
 "use strict";
 
 var oop = require("../lib/oop");
+var lang = require("../lib/lang");
 var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
 
+function safeCreateRegexp(source, flag) {
+    try {
+        return new RegExp(source, flag);
+    } catch(e) {}
+}
+
 var C9SearchHighlightRules = function() {
 
     // regexp must not have capturing parentheses. Use (?:) instead.
@@ -41,12 +48,120 @@ var C9SearchHighlightRules = function() {
     this.$rules = {
         "start" : [
             {
-                token : ["c9searchresults.constant.numeric", "c9searchresults.text", "c9searchresults.text"],
-                regex : "(^\\s+[0-9]+)(:\\s*)(.+)"
+                tokenNames : ["c9searchresults.constant.numeric", "c9searchresults.text", "c9searchresults.text", "c9searchresults.keyword"],
+                regex : "(^\\s+[0-9]+)(:\\s)(.+)",
+                onMatch : function(val, state, stack) {
+                    var values = this.splitRegex.exec(val);
+                    var types = this.tokenNames;
+                    var tokens = [{
+                        type: types[0],
+                        value: values[1]
+                    },{
+                        type: types[1],
+                        value: values[2]
+                    }];
+                    
+                    var regex = stack[1];
+                    var str = values[3];
+                    if (regex && str) 
+                        values = str.split(regex); // this doesn't work on ie8 but we don't care:)
+                    else
+                        values = [str];
+                    
+                    for (var i = 0, l = values.length; i < l; i+=2) {
+                        if (values[i])
+                            tokens.push({
+                                type: types[2],
+                                value: values[i]
+                            });
+                        if (values[i+1])
+                            tokens.push({
+                                type: types[3],
+                                value: values[i + 1]
+                            });
+                    }
+                    return tokens;
+                }
             },
             {
                 token : ["string", "text"], // single line
-                regex : "(.+)(:$)"
+                regex : "(\\S.*)(:$)"
+            },
+            {
+                regex : "Searching for .*$",
+                onMatch: function(val, state, stack) {
+                    var parts = val.split("\x01");
+                    var search = parts[1];
+                    if (parts.length < 3)
+                        return "text";
+                    var options = parts[2] == " in" ? parts[5] : parts[6];
+
+                    if (!/regex/.test(options))
+                        search = lang.escapeRegExp(search);
+                    if (/whole/.test(options))
+                        search = "\\b" + search + "\\b";
+                    var regex = safeCreateRegexp(
+                        "(" + search + ")",
+                        / sensitive/.test(options) ? "" : "i"
+                    );
+                    if (regex) {
+                        stack[0] = state;
+                        stack[1] = regex;
+                    }
+                    
+                    var i = 0;
+                    var tokens = [
+                        {
+                            value: parts[i++] + "'",
+                            type: "text"
+                        },
+                        {
+                            value: parts[i++],
+                            type: "text" // "c9searchresults.keyword"
+                        },
+                        {
+                            value: "'" + parts[i++],
+                            type: "text"
+                        }
+                    ];
+                    
+                    // replaced
+                    if (parts[2] !== " in") {
+                        tokens.push({
+                            value: "'" + parts[i++] + "'",
+                            type: "text"
+                        }, {
+                            value: parts[i++],
+                            type: "text"
+                        });
+                    }
+                    // path
+                    tokens.push({
+                        value: " " + parts[i++] + " ",
+                        type: "text"
+                    });
+                    // options
+                    if (parts[i+1]) {
+                        tokens.push({
+                            value: "(" + parts[i+1] + ")",
+                            type: "text"
+                        });
+                        i += 1;
+                    } else {
+                        i -= 1;
+                    }
+                    while (i++ < parts.length)
+                        parts[i] && tokens.push({
+                            value: parts[i],
+                            type: "text"
+                        });
+                    
+                    return tokens;
+                }
+            },
+            {
+                regex : "\\d+",
+                token: "constant.numeric"
             }
         ]
     };
@@ -56,4 +171,4 @@ oop.inherits(C9SearchHighlightRules, TextHighlightRules);
 
 exports.C9SearchHighlightRules = C9SearchHighlightRules;
 
-});
+});
\ No newline at end of file
diff --git a/lib/ace/mode/folding/cstyle.js b/lib/ace/mode/folding/cstyle.js
index 8be20d27..dd151e24 100644
--- a/lib/ace/mode/folding/cstyle.js
+++ b/lib/ace/mode/folding/cstyle.js
@@ -52,7 +52,7 @@ oop.inherits(FoldMode, BaseFoldMode);
     this.foldingStartMarker = /(\{|\[)[^\}\]]*$|^\s*(\/\*)/;
     this.foldingStopMarker = /^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/;
 
-    this.getFoldWidgetRange = function(session, foldStyle, row) {
+    this.getFoldWidgetRange = function(session, foldStyle, row, forceMultiline) {
         var line = session.getLine(row);
         var match = line.match(this.foldingStartMarker);
         if (match) {
@@ -60,11 +60,20 @@ oop.inherits(FoldMode, BaseFoldMode);
 
             if (match[1])
                 return this.openingBracketBlock(session, match[1], row, i);
-
-            return session.getCommentFoldRange(row, i + match[0].length, 1);
+                
+            var range = session.getCommentFoldRange(row, i + match[0].length, 1);
+            
+            if (range && !range.isMultiLine()) {
+                if (forceMultiline) {
+                    range = this.getSectionRange(session, row);
+                } else if (foldStyle != "all")
+                    range = null;
+            }
+            
+            return range;
         }
 
-        if (foldStyle !== "markbeginend")
+        if (foldStyle === "markbegin")
             return;
 
         var match = line.match(this.foldingStopMarker);
@@ -77,6 +86,38 @@ oop.inherits(FoldMode, BaseFoldMode);
             return session.getCommentFoldRange(row, i, -1);
         }
     };
+    
+    this.getSectionRange = function(session, row) {
+        var line = session.getLine(row);
+        var startIndent = line.search(/\S/);
+        var startRow = row;
+        var startColumn = line.length;
+        row = row + 1;
+        var endRow = row;
+        var maxRow = session.getLength();
+        while (++row < maxRow) {
+            line = session.getLine(row);
+            var indent = line.search(/\S/);
+            if (indent === -1)
+                continue;
+            if  (startIndent > indent)
+                break;
+            var subRange = this.getFoldWidgetRange(session, "all", row);
+            
+            if (subRange) {
+                if (subRange.start.row <= startRow) {
+                    break;
+                } else if (subRange.isMultiLine()) {
+                    row = subRange.end.row;
+                } else if (startIndent == indent) {
+                    break;
+                }
+            }
+            endRow = row;
+        }
+        
+        return new Range(startRow, startColumn, endRow, session.getLine(endRow).length);
+    };
 
 }).call(FoldMode.prototype);
 
diff --git a/lib/ace/mode/folding/cstyle_test.js b/lib/ace/mode/folding/cstyle_test.js
index e007c792..f199c916 100644
--- a/lib/ace/mode/folding/cstyle_test.js
+++ b/lib/ace/mode/folding/cstyle_test.js
@@ -76,6 +76,30 @@ module.exports = {
         
         assert.range(session.getFoldWidgetRange(0), 0, 2, 2, 7);
         assert.range(session.getFoldWidgetRange(2), 0, 2, 2, 7);
+    },
+    
+    "test: fold sections": function() {
+        var session = new EditSession([
+            '/* section0 */',
+            '{',
+            '    /* section1 */',
+            '    stuff',
+            '       ',
+            '    /* section2 */',
+            '       ',
+            '    stuff',
+            '       ',
+            '     }',
+            'foo'
+        ]);
+        
+        var mode = new JavaScriptMode();
+        session.setFoldStyle("markbegin");
+        session.setMode(mode);
+        
+        assert.range(session.getFoldWidgetRange(0, true), 0, 14, 10, 3);
+        assert.range(session.getFoldWidgetRange(2, true), 2, 18, 3, 9);
+        assert.range(session.getFoldWidgetRange(5, true), 5, 18, 7, 9);
     }
 };
 
diff --git a/lib/ace/mode/folding/fold_mode.js b/lib/ace/mode/folding/fold_mode.js
index 9272cdcd..7a6e9ec8 100644
--- a/lib/ace/mode/folding/fold_mode.js
+++ b/lib/ace/mode/folding/fold_mode.js
@@ -94,7 +94,7 @@ var FoldMode = exports.FoldMode = function() {};
 
         var fw = session.foldWidgets[end.row];
         if (fw == null)
-            fw = this.getFoldWidget(session, end.row);
+            fw = session.getFoldWidget(end.row);
 
         if (fw == "start" && end.row > start.row) {
             end.row --;
diff --git a/lib/ace/mode/sh.js b/lib/ace/mode/sh.js
index c66c2619..7620deee 100644
--- a/lib/ace/mode/sh.js
+++ b/lib/ace/mode/sh.js
@@ -36,9 +36,11 @@ var TextMode = require("./text").Mode;
 var Tokenizer = require("../tokenizer").Tokenizer;
 var ShHighlightRules = require("./sh_highlight_rules").ShHighlightRules;
 var Range = require("../range").Range;
+var CStyleFoldMode = require("./folding/cstyle").FoldMode;
 
 var Mode = function() {
     this.HighlightRules = ShHighlightRules;
+    this.foldingRules = new CStyleFoldMode();
 };
 oop.inherits(Mode, TextMode);
 
diff --git a/lib/ace/mouse/fold_handler.js b/lib/ace/mouse/fold_handler.js
index d47f6d53..45aae96f 100644
--- a/lib/ace/mouse/fold_handler.js
+++ b/lib/ace/mouse/fold_handler.js
@@ -73,7 +73,7 @@ function FoldHandler(editor) {
             var range = data.range || data.firstRange;
 
             if (range) {
-                var row = range.start.row;
+                row = range.start.row;
                 var fold = session.getFoldAt(row, session.getLine(row).length, 1);
 
                 if (fold) {
diff --git a/lib/ace/multi_select.js b/lib/ace/multi_select.js
index a811b6be..2152275e 100644
--- a/lib/ace/multi_select.js
+++ b/lib/ace/multi_select.js
@@ -693,7 +693,7 @@ var Editor = require("./editor").Editor;
 
         var range = sel.toOrientedRange();
         if (range.isEmpty()) {
-            var range = session.getWordRange(range.start.row, range.start.column);
+            range = session.getWordRange(range.start.row, range.start.column);
             range.cursor = dir == -1 ? range.start : range.end;
             this.multiSelect.addRange(range);
             return;
@@ -703,7 +703,11 @@ var Editor = require("./editor").Editor;
         var newRange = find(session, needle, dir);
         if (newRange) {
             newRange.cursor = dir == -1 ? newRange.start : newRange.end;
+            this.$blockScrolling += 1;
+            this.session.unfold(newRange);
             this.multiSelect.addRange(newRange);
+            this.$blockScrolling -= 1;
+            this.renderer.scrollCursorIntoView(null, 0.5);
         }
         if (skip)
             this.multiSelect.substractPoint(range.cursor);
@@ -721,11 +725,27 @@ var Editor = require("./editor").Editor;
         if (!ranges.length) {
             var range = this.selection.getRange();
             var fr = range.start.row, lr = range.end.row;
+            var guessRange = fr == lr;
+            if (guessRange) {
+                var max = this.session.getLength();
+                var line;
+                do {
+                    line = this.session.getLine(lr);
+                } while (/[=:]/.test(line) && ++lr < max);
+                do {
+                    line = this.session.getLine(fr);
+                } while (/[=:]/.test(line) && --fr > 0);
+                
+                if (fr < 0) fr = 0;
+                if (lr >= max) lr = max - 1;
+            }
             var lines = this.session.doc.removeLines(fr, lr);
-            lines = this.$reAlignText(lines);
-            this.session.doc.insertLines(fr, lines);
-            range.start.column = 0;
-            range.end.column = lines[lines.length - 1].length;
+            lines = this.$reAlignText(lines, guessRange);
+            this.session.doc.insert({row: fr, column: 0}, lines.join("\n") + "\n");
+            if (!guessRange) {
+                range.start.column = 0;
+                range.end.column = lines[lines.length - 1].length;
+            }
             this.selection.setRange(range);
         } else {
             // filter out ranges on same row
@@ -771,7 +791,7 @@ var Editor = require("./editor").Editor;
         }
     };
 
-    this.$reAlignText = function(lines) {
+    this.$reAlignText = function(lines, forceLeft) {
         var isLeftAligned = true, isRightAligned = true;
         var startW, textW, endW;
 
@@ -800,7 +820,8 @@ var Editor = require("./editor").Editor;
                 endW = m[3].length;
 
             return m;
-        }).map(isLeftAligned ? isRightAligned ? alignRight : alignLeft : unAlign);
+        }).map(forceLeft ? alignLeft :
+            isLeftAligned ? isRightAligned ? alignRight : alignLeft : unAlign);
 
         function spaces(n) {
             return lang.stringRepeat(" ", n);
diff --git a/lib/ace/theme/tomorrow_night_bright.css b/lib/ace/theme/tomorrow_night_bright.css
index f092a44f..76c9a760 100644
--- a/lib/ace/theme/tomorrow_night_bright.css
+++ b/lib/ace/theme/tomorrow_night_bright.css
@@ -32,7 +32,14 @@
 
 .ace-tomorrow-night-bright .ace_marker-layer .ace_bracket {
   margin: -1px 0 0 -1px;
-  border: 1px solid #343434
+  border: 1px solid #888888
+}
+.ace-tomorrow-night-bright .ace_marker-layer .ace_highlight {
+    border: 1px solid rgb(110, 119, 0);
+    border-bottom: 0;
+    box-shadow: inset 0 -1px rgb(110, 119, 0);
+    margin: -1px 0 0 -1px;
+    background: rgba(255, 235, 0, 0.1);
 }
 
 .ace-tomorrow-night-bright .ace_marker-layer .ace_active-line {
@@ -48,7 +55,7 @@
 }
 
 .ace-tomorrow-night-bright .ace_marker-layer .ace_selected-word {
-  border: 1px solid #424242
+  border: 1px solid #888888
 }
 
 .ace-tomorrow-night-bright .ace_invisible {
@@ -123,6 +130,10 @@
   color: #969896
 }
 
+.ace-tomorrow-night-bright .ace_c9searchresults.ace_keyword {
+    color: #C2C280;
+}
+
 .ace-tomorrow-night-bright .ace_indent-guide {
   background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYFBXV/8PAAJoAXX4kT2EAAAAAElFTkSuQmCC) right repeat-y;
 }
\ No newline at end of file
diff --git a/lib/ace/tokenizer.js b/lib/ace/tokenizer.js
index 6d63b075..5a9aaad4 100644
--- a/lib/ace/tokenizer.js
+++ b/lib/ace/tokenizer.js
@@ -310,7 +310,7 @@ var Tokenizer = function(rules) {
                     token = {
                         value: line.substring(lastIndex, lastIndex += 2000),
                         type: "overflow"
-                    }    
+                    };
                 }
                 currentState = "start";
                 stack = [];
diff --git a/lib/ace/undomanager.js b/lib/ace/undomanager.js
index 12aaa01d..304dac23 100644
--- a/lib/ace/undomanager.js
+++ b/lib/ace/undomanager.js
@@ -52,14 +52,14 @@ var UndoManager = function() {
 (function() {
 
     /**
-    * Provides a means for implementing your own undo manager. `options` has one property, `args`, an [[Array `Array`]], with two elements:
-    *
-    * - `args[0]` is an array of deltas
-    * - `args[1]` is the document to associate with
-    *
-    * @param {Object} options Contains additional properties
-    *
-    **/
+     * Provides a means for implementing your own undo manager. `options` has one property, `args`, an [[Array `Array`]], with two elements:
+     *
+     * - `args[0]` is an array of deltas
+     * - `args[1]` is the document to associate with
+     *
+     * @param {Object} options Contains additional properties
+     *
+     **/
     this.execute = function(options) {
         var deltas = options.args[0];
         this.$doc  = options.args[1];
@@ -78,12 +78,12 @@ var UndoManager = function() {
     };
 
     /**
-    * [Perform an undo operation on the document, reverting the last change.]{: #UndoManager.undo}
-    * @param {Boolean} dontSelect {:dontSelect}
-    *
-    * 
-    * @returns {Range} The range of the undo.
-    **/
+     * [Perform an undo operation on the document, reverting the last change.]{: #UndoManager.undo}
+     * @param {Boolean} dontSelect {:dontSelect}
+     *
+     * 
+     * @returns {Range} The range of the undo.
+     **/
     this.undo = function(dontSelect) {
         var deltas = this.$undoStack.pop();
         var undoSelectionRange = null;
@@ -98,11 +98,11 @@ var UndoManager = function() {
     };
 
     /**
-    * [Perform a redo operation on the document, reimplementing the last change.]{: #UndoManager.redo}
-    * @param {Boolean} dontSelect {:dontSelect}
-    *
-    * 
-    **/
+     * [Perform a redo operation on the document, reimplementing the last change.]{: #UndoManager.redo}
+     * @param {Boolean} dontSelect {:dontSelect}
+     *
+     * 
+     **/
     this.redo = function(dontSelect) {
         var deltas = this.$redoStack.pop();
         var redoSelectionRange = null;
@@ -117,9 +117,9 @@ var UndoManager = function() {
     };
 
     /**
-    * 
-    * Destroys the stack of undo and redo redo operations.
-    **/
+     * 
+     * Destroys the stack of undo and redo redo operations.
+     **/
     this.reset = function() {
         this.$undoStack = [];
         this.$redoStack = [];
@@ -127,36 +127,36 @@ var UndoManager = function() {
     };
 
     /**
-    * 
-    * Returns `true` if there are undo operations left to perform.
-    * @returns {Boolean}
-    **/
+     * 
+     * Returns `true` if there are undo operations left to perform.
+     * @returns {Boolean}
+     **/
     this.hasUndo = function() {
         return this.$undoStack.length > 0;
     };
 
     /**
-    * 
-    * Returns `true` if there are redo operations left to perform.
-    * @returns {Boolean}
-    **/
+     * 
+     * Returns `true` if there are redo operations left to perform.
+     * @returns {Boolean}
+     **/
     this.hasRedo = function() {
         return this.$redoStack.length > 0;
     };
 
     /**
-    *
-    * Marks the current status clean
-    **/
+     *
+     * Marks the current status clean
+     **/
     this.markClean = function() {
         this.dirtyCounter = 0;
     };
 
     /**
-    *
-    * Returns if the current status is clean
-    * @returns {Boolean}
-    **/
+     *
+     * Returns if the current status is clean
+     * @returns {Boolean}
+     **/
     this.isClean = function() {
         return this.dirtyCounter === 0;
     };
diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js
index b2d64a5c..f08e24cb 100644
--- a/lib/ace/virtual_renderer.js
+++ b/lib/ace/virtual_renderer.js
@@ -134,7 +134,8 @@ var VirtualRenderer = function(container, theme) {
         width: 0,
         height: 0,
         scrollerHeight: 0,
-        scrollerWidth: 0
+        scrollerWidth: 0,
+        $dirty: true
     };
 
     this.layerConfig = {
@@ -223,6 +224,9 @@ var VirtualRenderer = function(container, theme) {
     **/
     this.setSession = function(session) {
         this.session = session;
+        
+        if (this.scrollMargin.top && session.getScrollTop() <= 0)
+            session.setScrollTop(-this.scrollMargin.top);
 
         this.scroller.className = "ace_scroller";
 
@@ -232,7 +236,6 @@ var VirtualRenderer = function(container, theme) {
         this.$gutterLayer.setSession(session);
         this.$textLayer.setSession(session);
         this.$loop.schedule(this.CHANGE_FULL);
-
     };
 
     /**
@@ -300,6 +303,12 @@ var VirtualRenderer = function(container, theme) {
     };
 
     this.$changes = 0;
+    this.$updateSizeAsync = function() {
+        if (this.$loop.pending)
+            this.$size.$dirty = true;
+        else
+            this.onResize();
+    };
     /**
     * [Triggers a resize of the editor.]{: #VirtualRenderer.onResize}
     * @param {Boolean} force If `true`, recomputes the size, even if the height and width haven't changed
@@ -325,7 +334,6 @@ var VirtualRenderer = function(container, theme) {
             height = el.clientHeight || el.scrollHeight;
         if (!width)
             width = el.clientWidth || el.scrollWidth;
-
         var changes = this.$updateCachedSize(force, gutterWidth, width, height);
         
         // console.log("resizing to", width, height, JSON.stringify(this.$size))
@@ -342,7 +350,7 @@ var VirtualRenderer = function(container, theme) {
             this.$gutterLayer.$padding = null;
 
         if (force)
-            this.$renderChanges(changes, true);
+            this.$renderChanges(changes | this.$changes, true);
         else
             this.$loop.schedule(changes | this.$changes);
 
@@ -371,7 +379,7 @@ var VirtualRenderer = function(container, theme) {
             this.scrollBarV.element.style.bottom = this.scrollBarH.getHeight() + "px";
 
             if (this.session) {
-                this.session.setScrollTop(this.getScrollTop());
+                //this.session.setScrollTop(this.getScrollTop());
                 changes = changes | this.CHANGE_SCROLL;
             }
         }
@@ -399,6 +407,9 @@ var VirtualRenderer = function(container, theme) {
                 changes = changes | this.CHANGE_FULL;
         }
         
+        if (size.$dirty)
+            size.$dirty = !width && !height;
+
         if (changes)
             this._signal("resize", oldSize);
 
@@ -410,9 +421,11 @@ var VirtualRenderer = function(container, theme) {
         if (gutterWidth != this.gutterWidth)
             this.$changes |= this.$updateCachedSize(true, gutterWidth, this.$size.width, this.$size.height);
 
-        if (this.session.getUseWrapMode() && this.adjustWrapLimit())
+        if (this.session.getUseWrapMode() && this.adjustWrapLimit()) {
             this.$loop.schedule(this.CHANGE_FULL);
-        else {
+        } else if (this.$size.$dirty) {
+            this.$loop.schedule(this.CHANGE_FULL);
+        } else {
             this.$computeLayerConfig();
             this.$loop.schedule(this.CHANGE_MARKER);
         }
@@ -692,6 +705,8 @@ var VirtualRenderer = function(container, theme) {
         sm.left = left|0;
         sm.v = sm.top + sm.bottom;
         sm.h = sm.left + sm.right;
+        if (sm.top && this.scrollTop <= 0 && this.session)
+            this.session.setScrollTop(sm.top);
         this.updateFull();
     };
 
@@ -744,7 +759,7 @@ var VirtualRenderer = function(container, theme) {
             this.$changes |= changes;
             return; 
         } 
-        if (!this.$size.width) {
+        if (this.$size.$dirty) {
             this.$changes |= changes;
             return this.onResize(true);
         }
@@ -972,6 +987,7 @@ var VirtualRenderer = function(container, theme) {
         this.content.style.height = minHeight + "px";
 
         return changes;
+        return changes;
     };
 
     this.$updateLines = function() {
diff --git a/lib/ace/worker/mirror.js b/lib/ace/worker/mirror.js
index c521f8fd..7a3318fb 100644
--- a/lib/ace/worker/mirror.js
+++ b/lib/ace/worker/mirror.js
@@ -13,7 +13,9 @@ var Mirror = exports.Mirror = function(sender) {
     var _self = this;
     sender.on("change", function(e) {
         doc.applyDeltas(e.data);
-        deferredUpdate.schedule(_self.$timeout);
+        if (_self.$timeout)
+            return deferredUpdate.schedule(_self.$timeout);
+        _self.onUpdate();
     });
 };
 
@@ -38,6 +40,10 @@ var Mirror = exports.Mirror = function(sender) {
         // abstract method
     };
     
+    this.isPending = function() {
+        return this.deferredUpdate.isPending();
+    };
+    
 }).call(Mirror.prototype);
 
 });
diff --git a/lib/ace/worker/worker.js b/lib/ace/worker/worker.js
index 2f250afc..04296156 100644
--- a/lib/ace/worker/worker.js
+++ b/lib/ace/worker/worker.js
@@ -16,6 +16,10 @@ window.console.trace = window.console;
 window.window = window;
 window.ace = window;
 
+window.onerror = function(message, file, line, col, err) {
+    console.error("Worker " + err.stack);
+};
+
 window.normalizeModule = function(parentId, moduleName) {
     // normalize plugin requires
     if (moduleName.indexOf("!") !== -1) {
diff --git a/lib/ace/worker/worker_client.js b/lib/ace/worker/worker_client.js
index 3411d509..798f1b4b 100644
--- a/lib/ace/worker/worker_client.js
+++ b/lib/ace/worker/worker_client.js
@@ -39,7 +39,6 @@ var WorkerClient = function(topLevelNamespaces, mod, classname) {
     this.$sendDeltaQueue = this.$sendDeltaQueue.bind(this);
     this.changeListener = this.changeListener.bind(this);
     this.onMessage = this.onMessage.bind(this);
-    this.onError = this.onError.bind(this);
 
     // nameToUrl is renamed to toUrl in requirejs 2
     if (require.nameToUrl && !require.toUrl)
@@ -69,7 +68,6 @@ var WorkerClient = function(topLevelNamespaces, mod, classname) {
     this.callbackId = 1;
     this.callbacks = {};
 
-    this.$worker.onerror = this.onError;
     this.$worker.onmessage = this.onMessage;
 };
 
@@ -77,11 +75,6 @@ var WorkerClient = function(topLevelNamespaces, mod, classname) {
 
     oop.implement(this, EventEmitter);
 
-    this.onError = function(e) {
-        window.console && console.log && console.log(e);
-        throw e;
-    };
-
     this.onMessage = function(e) {
         var msg = e.data;
         switch(msg.type) {
@@ -157,7 +150,7 @@ var WorkerClient = function(topLevelNamespaces, mod, classname) {
     this.changeListener = function(e) {
         if (!this.deltaQueue) {
             this.deltaQueue = [e.data];
-            setTimeout(this.$sendDeltaQueue, 1);
+            setTimeout(this.$sendDeltaQueue, 0);
         } else
             this.deltaQueue.push(e.data);
     };
