docs: function indentLine(cm, n, how, aggressive) {
      var doc = cm.doc, state;
      if (how == null) how = "add";
      if (how == "smart") {
        // Fall back to "prev" when the mode doesn't have an indentation
        // method.
        if (!doc.mode.indent) how = "prev";
        else state = getStateBefore(cm, n);
      }
  
      var tabSize = cm.options.tabSize;
      var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
      if (line.stateAfter) line.stateAfter = null;
      var curSpaceString = line.text.match(/^\s*/)[0], indentation;
      if (!aggressive && !/\S/.test(line.text)) {
        indentation = 0;
        how = "not";
      } else if (how == "smart") {
        indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
        if (indentation == Pass || indentation > 150) {
          if (!aggressive) return;
          how = "prev";
        }
      }
      if (how == "prev") {
        if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
        else indentation = 0;
      } else if (how == "add") {
        indentation = curSpace + cm.options.indentUnit;
      } else if (how == "subtract") {
        indentation = curSpace - cm.options.indentUnit;
      } else if (typeof how == "number") {
        indentation = curSpace + how;
      }
      indentation = Math.max(0, indentation);
  
      var indentString = "", pos = 0;
      if (cm.options.indentWithTabs)
        for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
      if (pos < indentation) indentString += spaceStr(indentation - pos);
  
      if (indentString != curSpaceString) {
        replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
      } else {
        // Ensure that, if the cursor was in the whitespace at the start
        // of the line, it is moved to the end of that space.
        for (var i = 0; i < doc.sel.ranges.length; i++) {
          var range = doc.sel.ranges[i];
          if (range.head.line == n && range.head.ch < curSpaceString.length) {
            var pos = Pos(n, curSpaceString.length);
            replaceOneSelection(doc, i, new Range(pos, pos));
            break;
          }
        }
      }
      line.stateAfter = null;
    }