docs: function getDocCommentTemplateAtPosition(fileName, position) {
              var sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
              // Check if in a context where we don't want to perform any insertion
              if (ts.isInString(sourceFile, position) || ts.isInComment(sourceFile, position) || ts.hasDocComment(sourceFile, position)) {
                  return undefined;
              }
              var tokenAtPos = ts.getTokenAtPosition(sourceFile, position);
              var tokenStart = tokenAtPos.getStart();
              if (!tokenAtPos || tokenStart < position) {
                  return undefined;
              }
              // TODO: add support for:
              // - enums/enum members
              // - interfaces
              // - property declarations
              // - potentially property assignments
              var commentOwner;
              findOwner: for (commentOwner = tokenAtPos; commentOwner; commentOwner = commentOwner.parent) {
                  switch (commentOwner.kind) {
                      case 220 /* FunctionDeclaration */:
                      case 147 /* MethodDeclaration */:
                      case 148 /* Constructor */:
                      case 221 /* ClassDeclaration */:
                      case 200 /* VariableStatement */:
                          break findOwner;
                      case 256 /* SourceFile */:
                          return undefined;
                      case 225 /* ModuleDeclaration */:
                          // If in walking up the tree, we hit a a nested namespace declaration,
                          // then we must be somewhere within a dotted namespace name; however we don't
                          // want to give back a JSDoc template for the 'b' or 'c' in 'namespace a.b.c { }'.
                          if (commentOwner.parent.kind === 225 /* ModuleDeclaration */) {
                              return undefined;
                          }
                          break findOwner;
                  }
              }
              if (!commentOwner || commentOwner.getStart() < position) {
                  return undefined;
              }
              var parameters = getParametersForJsDocOwningNode(commentOwner);
              var posLineAndChar = sourceFile.getLineAndCharacterOfPosition(position);
              var lineStart = sourceFile.getLineStarts()[posLineAndChar.line];
              var indentationStr = sourceFile.text.substr(lineStart, posLineAndChar.character);
              var newLine = ts.getNewLineOrDefaultFromHost(host);
              var docParams = "";
              for (var i = 0, numParams = parameters.length; i < numParams; i++) {
                  var currentName = parameters[i].name;
                  var paramName = currentName.kind === 69 /* Identifier */ ?
                      currentName.text :
                      "param" + i;
                  docParams += indentationStr + " * @param " + paramName + newLine;
              }
              // A doc comment consists of the following
              // * The opening comment line
              // * the first line (without a param) for the object's untagged info (this is also where the caret ends up)
              // * the '@param'-tagged lines
              // * TODO: other tags.
              // * the closing comment line
              // * if the caret was directly in front of the object, then we add an extra line and indentation.
              var preamble = "/**" + newLine +
                  indentationStr + " * ";
              var result = preamble + newLine +
                  docParams +
                  indentationStr + " */" +
                  (tokenStart === position ? newLine + indentationStr : "");
              return { newText: result, caretOffset: preamble.length };
          }