diff --git a/mode/yacas/yacas.js b/mode/yacas/yacas.js
index 51399fbf5942c6fad4e4436208e87badab61d00c..30bd60b2ffadf340ac753a2406e7c38b1190947e 100644
--- a/mode/yacas/yacas.js
+++ b/mode/yacas/yacas.js
@@ -16,6 +16,19 @@
 
 CodeMirror.defineMode('yacas', function(_config, _parserConfig) {
 
+  function words(str) {
+    var obj = {}, words = str.split(" ");
+    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+    return obj;
+  }
+
+  var bodiedOps = words("Assert BackQuote D Defun Deriv For ForEach FromFile " +
+                        "FromString Function Integrate InverseTaylor Limit " +
+                        "LocalSymbols Macro MacroRule MacroRulePattern " +
+                        "NIntegrate Rule RulePattern Subst TD TExplicitSum " +
+                        "TSum Taylor Taylor1 Taylor2 Taylor3 ToFile " +
+                        "ToStdout ToString TraceRule Until While");
+
   // patterns
   var pFloatForm  = "(?:(?:\\.\\d+|\\d+\\.\\d*|\\d+)(?:[eE][+-]?\\d+)?)";
   var pIdentifier = "(?:[a-zA-Z\\$'][a-zA-Z0-9\\$']*)";
@@ -53,6 +66,33 @@ CodeMirror.defineMode('yacas', function(_config, _parserConfig) {
     // go back one character
     stream.backUp(1);
 
+    // update scope info
+    var m = stream.match(/^(\w+)\s*\(/, false);
+    if (m !== null && bodiedOps.hasOwnProperty(m[1]))
+      state.scopes.push('bodied');
+
+    var scope = currentScope(state);
+
+    if (scope === 'bodied' && ch === '[')
+      state.scopes.pop();
+
+    if (ch === '[' || ch === '{' || ch === '(')
+      state.scopes.push(ch);
+
+    scope = currentScope(state);
+
+    if (scope === '[' && ch === ']' ||
+        scope === '{' && ch === '}' ||
+        scope === '(' && ch === ')')
+      state.scopes.pop();
+
+    if (ch === ';') {
+      while (scope === 'bodied') {
+        state.scopes.pop();
+        scope = currentScope(state);
+      }
+    }
+
     // look for ordered rules
     if (stream.match(/\d+ *#/, true, false)) {
       return 'qualifier';
@@ -120,12 +160,37 @@ CodeMirror.defineMode('yacas', function(_config, _parserConfig) {
     return 'comment';
   }
 
+  function currentScope(state) {
+    var scope = null;
+    if (state.scopes.length > 0)
+      scope = state.scopes[state.scopes.length - 1];
+    return scope;
+  }
+
   return {
-    startState: function() {return {tokenize: tokenBase, commentLevel: 0};},
+    startState: function() {
+      return {
+        tokenize: tokenBase,
+        scopes: []
+      };
+    },
     token: function(stream, state) {
       if (stream.eatSpace()) return null;
       return state.tokenize(stream, state);
     },
+    indent: function(state, textAfter) {
+      if (state.tokenize !== tokenBase && state.tokenize !== null)
+        return CodeMirror.Pass;
+
+      var delta = 0;
+      if (textAfter === ']' || textAfter === '];' ||
+          textAfter === '}' || textAfter === '};' ||
+          textAfter === ');')
+        delta = -1;
+
+      return (state.scopes.length + delta) * _config.indentUnit;
+    },
+    electricChars: "{}[]();",
     blockCommentStart: "/*",
     blockCommentEnd: "*/",
     lineComment: "//"