override def handleInsert()

in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/completion/handlers/ScalaInsertHandler.scala [137:450]


  override def handleInsert(context: InsertionContext, item: ScalaLookupItem): Unit = {
    val InsertionContextExt(editor, document, file, project) = context
    val model = editor.getCaretModel

    val (originalStartOffset, originalEndOffset) = {
      val startOffset = context.getStartOffset
      val tailOffset = context.getTailOffset

      if (item.isInSimpleString) {
        new StringInsertPreHandler().handleInsert(context, item)
        (startOffset + 2, tailOffset + 2)
      } else if (item.isInInterpolatedString) {
        file.findElementAt(startOffset).getParent match {
          case literal: ScInterpolated =>
            ScalaBasicCompletionProvider.interpolatedStringBounds(literal, startOffset) match {
              case Some((offset, _)) =>
                document.insertString(tailOffset, "}")
                document.insertString(offset + literal.getTextRange.getStartOffset, "{")
                context.commitDocument()

                (offset + 1, tailOffset + 1)
              case _ => return
            }
          case _ => return
        }
      } else (startOffset, tailOffset)
    }

    var endOffset = originalEndOffset

    val completionChar = context.getCompletionChar

    def disableParenthesesCompletionChar(): Unit = {
      if (completionChar == '(' || completionChar == '{') {
        context.setAddCompletionChar(false)
      }
    }

    val element = {
      // InsertionContext::getFile returns wrong file in evaluate expression in debugger (runtime type completion)
      val element = PsiDocumentManager
        .getInstance(project)
        .getPsiFile(document)
        .findElementAt(originalStartOffset)

      val maybeElement = if (completionChar == '\t' &&
        item.getAllLookupStrings.size() > 1 &&
        element.getNode.getElementType == tIDENTIFIER)
        element.getParent match {
          case ref: ScReferenceExpression =>
            ref.getParent match {
              case parentRef: ScReferenceExpression =>
                val newRef = createExpressionFromText(ref.getText, ref)(ref)
                Some(parentRef.replace(newRef).getFirstChild)
              case _ => None
            }
          case _ => None
        }
      else
        None

      maybeElement.getOrElse(element)
    }

    // if the inserted element is a backtick identifier, we want to remove the following backtick
    val newElementHasBackticks = ScalaNamesUtil.BacktickedName.hasBackticks(item.getLookupString)
    if (item.isInStableElementPattern || newElementHasBackticks) {
      val next = element.getNextNonWhitespaceAndNonEmptyLeaf
      if (next != null && next.textMatches("`")) {
        document.deleteString(next.startOffset, next.endOffset)
      }
    }

    // if the inserted element is not a backtick identifier, but it was in a StableElementPattern, we want to add backticks
    if (item.isInStableElementPattern && !newElementHasBackticks) {
      document.insertString(element.endOffset, "`")
      document.insertString(element.startOffset, "`")
      endOffset += 2
    }

    val some = item.someSmartCompletion
    val someNum = if (some) 1 else 0

    if (some) {
      var elem = element
      var parent = elem.getParent
      while (parent match {
        case _: ScStableCodeReference =>
          parent.getParent match {
            case _: ScThisReference | _: ScSuperReference => true
            case _ => false
          }
        case _: ScReferenceExpression => true
        case _: ScThisReference => true
        case _: ScSuperReference => true
        case inf: ScInfixExpr if elem == inf.operation => true
        case pref: ScPrefixExpr if elem == pref.operation => true
        case postf: ScPostfixExpr if elem == postf.operation => true
        case _ => false
      }) {
        elem = parent
        parent = parent.getParent
      }

      val start = elem.getTextRange.getStartOffset
      val end = elem.getTextRange.getEndOffset
      document.insertString(end, ")")
      document.insertString(start, "Some(")
      endOffset += 5
    }

    def moveCaretIfNeeded(): Unit = {
      model.moveToOffset(endOffset + someNum)
    }

    /**
     * insert parentheses in case if it's necessary
     *
     * @param placeInto   "(<caret>)" if true
     * @param openChar    open char like '('
     * @param closeChar   close char like ')'
     * @param withSpace   add " ()" if true
     * @param withSomeNum move caret with additional shift on some completion ending
     */
    @tailrec
    def insertIfNeeded(placeInto: Boolean, openChar: Char, closeChar: Char, withSpace: Boolean, withSomeNum: Boolean): Unit = {
      def shiftEndOffset(shift: Int, withSomeNum: Boolean = withSomeNum): Unit = {
        endOffset += shift
        model.moveToOffset(endOffset + (if (withSomeNum) someNum else 0))
      }

      val documentText = document.getImmutableCharSequence
      val nextChar: Char =
        if (endOffset < document.getTextLength) documentText.charAt(endOffset)
        else 0.toChar
      if (!withSpace && nextChar != openChar) {
        if (CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET) {
          document.insertString(endOffset, s"$openChar$closeChar")
          if (placeInto) {
            shiftEndOffset(1)
          } else {
            shiftEndOffset(2)
          }
        } else {
          document.insertString(endOffset, s"$openChar")
          shiftEndOffset(1)
        }
      } else if (!withSpace && nextChar == openChar) {
        if (placeInto) {
          shiftEndOffset(1)
        } else {
          val nextNextChar = documentText.charAt(endOffset + 1)
          if (nextNextChar == closeChar) {
            shiftEndOffset(2)
          } else {
            shiftEndOffset(1)
          }
        }
      } else if (withSpace && (nextChar != ' ' || documentText.charAt(endOffset + 1) != openChar)) {
        document.insertString(endOffset, " ")
        shiftEndOffset(1, withSomeNum = false)
        insertIfNeeded(placeInto, openChar, closeChar, withSpace = false, withSomeNum = withSomeNum)
      } else if (withSpace && nextChar == ' ') {
        shiftEndOffset(1, withSomeNum = false)
        insertIfNeeded(placeInto, openChar, closeChar, withSpace = false, withSomeNum = withSomeNum)
      }
    }

    item.getPsiElement match {
      case _: PsiClass | _: ScTypeAlias if completionChar == '[' =>
        context.setAddCompletionChar(false)
        insertIfNeeded(placeInto = true, openChar = '[', closeChar = ']', withSpace = false, withSomeNum = false)
      case _: PsiNamedElement if item.isNamedParameterOrAssignment => //some is impossible here
        val shouldAddAssignment = element.getParent match {
          case ref: ScReferenceExpression =>
            ref.getParent match {
              case ass: ScAssignment if ass.leftExpression == ref =>
                ass.getParent match {
                  case _: ScArgumentExprList => false
                  case _ => true
                }
              case _ => true
            }
          case _ => true //should be impossible
        }
        context.setAddCompletionChar(false)

        if (shouldAddAssignment) {
          document.insertString(endOffset, AssignmentText)
          endOffset += AssignmentText.length
          model.moveToOffset(endOffset)
        }
        return
      case _: PsiMethod if item.isInImport => moveCaretIfNeeded()
      case _: ScFun if item.isInImport => moveCaretIfNeeded()
      case fun: ScFunction if fun.name == "classOf" && fun.containingClass != null &&
        fun.containingClass.qualifiedName == "scala.Predef" =>
        context.setAddCompletionChar(false)
        insertIfNeeded(placeInto = true, openChar = '[', closeChar = ']', withSpace = false, withSomeNum = true)
      case method@(_: PsiMethod | _: ScFun) =>
        if (completionChar != '[') {
          val (count, isAccessor) = getItemParametersAndAccessorStatus(method)
          if (count == 0 && !isAccessor) {
            disableParenthesesCompletionChar()
            if (item.etaExpanded) {
              document.insertString(endOffset, " _")
              endOffset += 2
              model.moveToOffset(endOffset)
            } else {
              insertIfNeeded(placeInto = completionChar == '(', openChar = '(', closeChar = ')',
                withSpace = false, withSomeNum = false)
            }
          }
          else if (count > 0) {
            element.getParent match {
              //case for infix expressions
              case (ref: ScReferenceExpression) & Parent(inf: ScInfixExpr) if inf.operation == ref =>
                if (count > 1) {
                  disableParenthesesCompletionChar()
                  if (!item.etaExpanded) {
                    val openChar = if (completionChar == '{') '{' else '('
                    val closeChar = if (completionChar == '{') '}' else ')'
                    insertIfNeeded(placeInto = true, openChar = openChar, closeChar = closeChar, withSpace = true, withSomeNum = false)
                  } else {
                    document.insertString(endOffset, " _")
                    endOffset += 2
                    model.moveToOffset(endOffset)
                  }
                } else {
                  if (completionChar == '{') {
                    insertIfNeeded(placeInto = true, openChar = '{', closeChar = '}', withSpace = true, withSomeNum = false)
                  } else {
                    document.insertString(endOffset, " ")
                    endOffset += 1
                    model.moveToOffset(endOffset)
                  }
                }
              //no braces for interpolated string id
              case _: ScInterpolatedExpressionPrefix =>

              // for reference invocations
              case _ =>
                if (completionChar == ' ') {
                  context.setAddCompletionChar(false)
                  document.insertString(endOffset, " ")
                  endOffset += 1
                  model.moveToOffset(endOffset + someNum)
                } else if (endOffset == document.getTextLength || document.getCharsSequence.charAt(endOffset) != '(') {
                  disableParenthesesCompletionChar()
                  if (!item.etaExpanded) {
                    val (openChar, closeChar, withSpace) = completionChar match {
                      case '{' => (completionChar, '}', ScalaPsiUtil.getSettings(project).SPACE_BEFORE_BRACE_METHOD_CALL)
                      case '(' => (completionChar, ')', false)
                      case _ =>
                        val (openChar, withSpace) = element match {
                          case Parent((ref: ScReferenceExpression) & Parent(call: ScMethodCall)) if call.getInvokedExpr == ref && !call.args.prevSibling.exists(s => s.is[PsiWhiteSpace] && s.textContains('\n')) =>
                            val char = call.args.getNode.getFirstChildNode match {
                              case block: ScBlockExpr => block.getLBrace.fold('(')(_ => '{')
                              case node if node != null && node.getElementType == ScalaTokenTypes.tLBRACE => '{'
                              case _ => '('
                            }
                            val withSpace = char == '{' && ScalaPsiUtil.getSettings(project).SPACE_BEFORE_BRACE_METHOD_CALL ||
                              ref.nextElement.exists(_.is[PsiWhiteSpace])
                            (char, withSpace)
                          case _ => ('(', false)
                        }
                        (openChar, if (openChar == '(') ')' else '}', withSpace)
                    }
                    insertIfNeeded(placeInto = true, openChar = openChar, closeChar = closeChar, withSpace = withSpace, withSomeNum = false)
                  } else {
                    document.insertString(endOffset, " _")
                    endOffset += 2
                    model.moveToOffset(endOffset)
                  }
                  AutoPopupController.getInstance(element.getProject).autoPopupParameterInfo(editor, element)
                } else if (completionChar != ',') {
                  model.moveToOffset(endOffset + 1 + someNum)
                } else moveCaretIfNeeded()
            }
          } else moveCaretIfNeeded()
        } else {
          context.setAddCompletionChar(false)
          insertIfNeeded(placeInto = true, openChar = '[', closeChar = ']', withSpace = false, withSomeNum = false)
          //do not add () or {} in this case, use will choose what he want later
        }
      case _: ScTypeDefinition =>
        if (completionChar != '[') {
          //add space between the added element and the '{' in extends block when necessary
          val documentText = document.getImmutableCharSequence
          val isInTemplateParents =
            getParentOfType(element, classOf[ScTemplateParents], false, classOf[ScExtendsBlock]) != null
          val lBraceAtCaret = endOffset < documentText.length() && documentText.charAt(endOffset) == '{'
          if (lBraceAtCaret && isInTemplateParents) {
            document.insertString(endOffset, " ")
            endOffset += 1
            model.moveToOffset(endOffset)
          }
        }
        moveCaretIfNeeded()
      case _ => moveCaretIfNeeded()
    }

    if (completionChar == ',') {
      endOffset += someNum
      context.setAddCompletionChar(false)
      document.insertString(endOffset, ",")
      endOffset += 1
      model.moveToOffset(endOffset)
    }

    if (item.isInSimpleString) {
      new StringInsertPostHandler().handleInsert(context, item)
    }
  }