<commit_before>package scala.tools.eclipse.ui

import scala.tools.eclipse.ScalaImages
import scala.tools.eclipse.ScalaWordFinder
import scala.tools.eclipse.completion.CompletionContext
import scala.tools.eclipse.completion.CompletionProposal
import scala.tools.eclipse.completion.MemberKind
import scala.tools.eclipse.completion.prefixMatches
import scala.tools.eclipse.refactoring.EditorHelpers
import scala.tools.eclipse.refactoring.EditorHelpers._
import scala.tools.refactoring.common.TextChange
import scala.tools.refactoring.implementations.AddImportStatement

import org.eclipse.jdt.internal.ui.JavaPlugin
import org.eclipse.jdt.internal.ui.JavaPluginImages
import org.eclipse.jdt.ui.PreferenceConstants
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal
import org.eclipse.jface.preference.PreferenceConverter
import org.eclipse.jface.text.DocumentEvent
import org.eclipse.jface.text.IDocument
import org.eclipse.jface.text.IRegion
import org.eclipse.jface.text.ITextPresentationListener
import org.eclipse.jface.text.ITextViewer
import org.eclipse.jface.text.ITextViewerExtension2
import org.eclipse.jface.text.ITextViewerExtension4
import org.eclipse.jface.text.ITextViewerExtension5
import org.eclipse.jface.text.Position
import org.eclipse.jface.text.TextPresentation
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6
import org.eclipse.jface.text.contentassist.IContextInformation
import org.eclipse.jface.text.link._
import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags
import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy
import org.eclipse.jface.viewers.ISelectionProvider
import org.eclipse.jface.viewers.StyledString
import org.eclipse.swt.SWT
import org.eclipse.swt.custom.StyleRange
import org.eclipse.swt.events.VerifyEvent
import org.eclipse.swt.graphics.Color
import org.eclipse.ui.texteditor.link.EditorLinkedModeUI

/** A UI class for displaying completion proposals.
 *
 *  It adds parenthesis at the end of a proposal if it has parameters, and places the caret
 *  between them.
 */
class ScalaCompletionProposal(proposal: CompletionProposal, selectionProvider: ISelectionProvider)
  extends IJavaCompletionProposal
  with ICompletionProposalExtension
  with ICompletionProposalExtension2
  with ICompletionProposalExtension6 {

  import proposal._
  import ScalaCompletionProposal._

  private var cachedStyleRange: StyleRange = null
  private val ScalaProposalCategory = "ScalaProposal"

  override def getRelevance = relevance

  private lazy val image = {
    import MemberKind._

    kind match {
      case Def           => defImage
      case Class         => classImage
      case Trait         => traitImage
      case Package       => packageImage
      case PackageObject => packageObjectImage
      case Object =>
        if (isJava) javaClassImage
        else objectImage
      case Type => typeImage
      case _    => valImage
    }
  }

  override def getImage = image

  /** `getParamNames` is expensive, save this result once computed.
   *
   *  @note This field should be lazy to avoid unnecessary computation.
   */
  private lazy val explicitParamNames = getParamNames()

  /** The string that will be inserted in the document if this proposal is chosen.
   *  By default, it consists of the method name, followed by all explicit parameter sections,
   *  and inside each section the parameter names, delimited by commas. If `overwrite`
   *  is on, it won't add parameter names
   *
   *  @note It triggers the potentially expensive `getParameterNames` operation.
   */
  def completionString(overwrite: Boolean) = {
    if (context.contextType == CompletionContext.ImportContext || explicitParamNames.isEmpty || overwrite)
      completion
    else {
      val buffer = new StringBuffer(completion)

      for (section <- explicitParamNames)
        buffer.append(section.mkString("(", ", ", ")"))
      buffer.toString
    }
  }

  /** Position after the opening parenthesis of this proposal */
  val startOfArgumentList = startPos + completion.length + 1

  /** The information that is displayed in a small hover window above the completion, showing parameter names and types. */
  override def getContextInformation(): IContextInformation =
    if (context.contextType != CompletionContext.ImportContext && tooltip.length > 0)
      new ScalaContextInformation(display, tooltip, image, startOfArgumentList)
    else null

  /** A simple display string
   */
  override def getDisplayString() = display

  /** A display string with grayed out extra details
   */
  override def getStyledDisplayString(): StyledString = {
    val styledString = new StyledString(display)
    if (displayDetail != null && displayDetail.length > 0)
      styledString.append(" - ", StyledString.QUALIFIER_STYLER).append(displayDetail, StyledString.QUALIFIER_STYLER)
    styledString
  }

  /** Some additional info (like javadoc ...)
   */
  override def getAdditionalProposalInfo() = null
  override def getSelection(d: IDocument) = null
  override def apply(d: IDocument) { throw new IllegalStateException("Shouldn't be called") }

  override def apply(d: IDocument, trigger: Char, offset: Int) {
    throw new IllegalStateException("Shouldn't be called")
  }

  override def apply(viewer: ITextViewer, trigger: Char, stateMask: Int, offset: Int): Unit = {
    val d: IDocument = viewer.getDocument()
    val overwrite = !insertCompletion ^ ((stateMask & SWT.CTRL) != 0)

    val tooltipsOnly = context.contextType == CompletionContext.NewContext || context.contextType == CompletionContext.ApplyContext

    val completionFullString = completionString(overwrite)
    val importSize = withScalaFileAndSelection { (scalaSourceFile, textSelection) =>
      var changes: List[TextChange] = Nil

      if (!tooltipsOnly) {
        scalaSourceFile.withSourceFile { (sourceFile, _) =>
          val endPos = if (overwrite) startPos + existingIdentifier(d, offset).getLength() else offset
          changes :+= TextChange(sourceFile, startPos, endPos, completionFullString)
        }
      }

      val importStmt = if (needImport) { // add an import statement if required
        scalaSourceFile.withSourceFile { (_, compiler) =>
          val refactoring = new AddImportStatement { val global = compiler }
          refactoring.addImport(scalaSourceFile.file, fullyQualifiedName)
        } getOrElse (Nil)
      } else {
        Nil
      }

      changes ++= importStmt


      // Apply the two changes in one step, if done separately we would need an
      // another `waitLoadedType` to update the positions for the refactoring
      // to work properly.
      EditorHelpers.applyChangesToFileWhileKeepingSelection(
        d, textSelection, scalaSourceFile.file, changes)

      importStmt.headOption.map(_.text.length)
    }

    if (!tooltipsOnly && context.contextType != CompletionContext.ImportContext) {
      if (!overwrite) selectionProvider match {
        case viewer: ITextViewer if explicitParamNames.flatten.nonEmpty =>
          addArgumentTemplates(d, viewer, completionFullString)
        case _ => ()
      }
      else
        EditorHelpers.doWithCurrentEditor { editor =>
          editor.selectAndReveal(startPos + completionFullString.length() + importSize.getOrElse(0), 0)
      }
    }
  }

  override def getTriggerCharacters = null
  override def getContextInformationPosition = startOfArgumentList

  override def isValidFor(d: IDocument, pos: Int) =
    prefixMatches(completion.toArray, d.get.substring(startPos, pos).toArray)

  /** Insert a completion proposal, with placeholders for each explicit argument.
   *  For each argument, it inserts its name, and puts the editor in linked mode.
   *  This means that TAB can be used to navigate to the next argument, and Enter or Esc<commit_msg>Correct completion overwrite behavior when nothing is overwritten<commit_after>package scala.tools.eclipse.ui

import scala.tools.eclipse.ScalaImages
import scala.tools.eclipse.ScalaWordFinder
import scala.tools.eclipse.completion.CompletionContext
import scala.tools.eclipse.completion.CompletionProposal
import scala.tools.eclipse.completion.MemberKind
import scala.tools.eclipse.completion.prefixMatches
import scala.tools.eclipse.refactoring.EditorHelpers
import scala.tools.eclipse.refactoring.EditorHelpers._
import scala.tools.refactoring.common.TextChange
import scala.tools.refactoring.implementations.AddImportStatement

import org.eclipse.jdt.internal.ui.JavaPlugin
import org.eclipse.jdt.internal.ui.JavaPluginImages
import org.eclipse.jdt.ui.PreferenceConstants
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal
import org.eclipse.jface.preference.PreferenceConverter
import org.eclipse.jface.text.DocumentEvent
import org.eclipse.jface.text.IDocument
import org.eclipse.jface.text.IRegion
import org.eclipse.jface.text.ITextPresentationListener
import org.eclipse.jface.text.ITextViewer
import org.eclipse.jface.text.ITextViewerExtension2
import org.eclipse.jface.text.ITextViewerExtension4
import org.eclipse.jface.text.ITextViewerExtension5
import org.eclipse.jface.text.Position
import org.eclipse.jface.text.TextPresentation
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2
import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6
import org.eclipse.jface.text.contentassist.IContextInformation
import org.eclipse.jface.text.link._
import org.eclipse.jface.text.link.LinkedModeUI.ExitFlags
import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy
import org.eclipse.jface.viewers.ISelectionProvider
import org.eclipse.jface.viewers.StyledString
import org.eclipse.swt.SWT
import org.eclipse.swt.custom.StyleRange
import org.eclipse.swt.events.VerifyEvent
import org.eclipse.swt.graphics.Color
import org.eclipse.ui.texteditor.link.EditorLinkedModeUI

/** A UI class for displaying completion proposals.
 *
 *  It adds parenthesis at the end of a proposal if it has parameters, and places the caret
 *  between them.
 */
class ScalaCompletionProposal(proposal: CompletionProposal, selectionProvider: ISelectionProvider)
  extends IJavaCompletionProposal
  with ICompletionProposalExtension
  with ICompletionProposalExtension2
  with ICompletionProposalExtension6 {

  import proposal._
  import ScalaCompletionProposal._

  private var cachedStyleRange: StyleRange = null
  private val ScalaProposalCategory = "ScalaProposal"

  override def getRelevance = relevance

  private lazy val image = {
    import MemberKind._

    kind match {
      case Def           => defImage
      case Class         => classImage
      case Trait         => traitImage
      case Package       => packageImage
      case PackageObject => packageObjectImage
      case Object =>
        if (isJava) javaClassImage
        else objectImage
      case Type => typeImage
      case _    => valImage
    }
  }

  override def getImage = image

  /** `getParamNames` is expensive, save this result once computed.
   *
   *  @note This field should be lazy to avoid unnecessary computation.
   */
  private lazy val explicitParamNames = getParamNames()

  /** The string that will be inserted in the document if this proposal is chosen.
   *  By default, it consists of the method name, followed by all explicit parameter sections,
   *  and inside each section the parameter names, delimited by commas. If `overwrite`
   *  is on, it won't add parameter names
   *
   *  @note It triggers the potentially expensive `getParameterNames` operation.
   */
  def completionString(overwrite: Boolean, doParamsProbablyExist: => Boolean) = {
    if (context.contextType == CompletionContext.ImportContext || ((explicitParamNames.isEmpty || overwrite) && doParamsProbablyExist))
      completion
    else {
      val buffer = new StringBuffer(completion)

      for (section <- explicitParamNames)
        buffer.append(section.mkString("(", ", ", ")"))
      buffer.toString
    }
  }

  /** Position after the opening parenthesis of this proposal */
  val startOfArgumentList = startPos + completion.length + 1

  /** The information that is displayed in a small hover window above the completion, showing parameter names and types. */
  override def getContextInformation(): IContextInformation =
    if (context.contextType != CompletionContext.ImportContext && tooltip.length > 0)
      new ScalaContextInformation(display, tooltip, image, startOfArgumentList)
    else null

  /** A simple display string
   */
  override def getDisplayString() = display

  /** A display string with grayed out extra details
   */
  override def getStyledDisplayString(): StyledString = {
    val styledString = new StyledString(display)
    if (displayDetail != null && displayDetail.length > 0)
      styledString.append(" - ", StyledString.QUALIFIER_STYLER).append(displayDetail, StyledString.QUALIFIER_STYLER)
    styledString
  }

  /** Some additional info (like javadoc ...)
   */
  override def getAdditionalProposalInfo() = null
  override def getSelection(d: IDocument) = null
  override def apply(d: IDocument) { throw new IllegalStateException("Shouldn't be called") }

  override def apply(d: IDocument, trigger: Char, offset: Int) {
    throw new IllegalStateException("Shouldn't be called")
  }

  override def apply(viewer: ITextViewer, trigger: Char, stateMask: Int, offset: Int): Unit = {
    val d: IDocument = viewer.getDocument()
    val overwrite = !insertCompletion ^ ((stateMask & SWT.CTRL) != 0)

    val tooltipsOnly = context.contextType == CompletionContext.NewContext || context.contextType == CompletionContext.ApplyContext

    /**
     * This is a heuristic that is only called when 'completion overwrite' is enabled.
     * It checks if an expression _probably_ has already its parameter list. Without
     * this heuristic the IDE would in cases like
     * {{{
     *   List(1).map^
     * }}}
     * not know if the parameter list should be added or not.
     *
     * Because this is a heuristic it will only work in some cases, but hopefully in
     * the most important ones.
     */
    lazy val doParamsProbablyExist = {
      // - inner method exists to make a return possible
      // - lazy val necessary because the operation may be unnecessary and the
      //   underlying document changes during completion insertion
      def check: Boolean = {
        def terminatesExprProbably(c: Char) =
          c.toString matches "[a-zA-Z_;)},.\n]"

        val terminationChar = Iterator
          .from(offset)
          // prevent BadLocationException at end of file
          .filter(c => if (c < d.getLength()) true else return false)
          .map(d.getChar)
          .dropWhile(Character.isJavaIdentifierPart)
          .dropWhile(" \t" contains _)
          .next()

        !terminatesExprProbably(terminationChar)
      }
      check
    }

    val completionFullString = completionString(overwrite, doParamsProbablyExist)
    val importSize = withScalaFileAndSelection { (scalaSourceFile, textSelection) =>
      var changes: List[TextChange] = Nil

      if (!tooltipsOnly) {
        scalaSourceFile.withSourceFile { (sourceFile, _) =>
          val endPos = if (overwrite) startPos + existingIdentifier(d, offset).getLength() else offset
          changes :+= TextChange(sourceFile, startPos, endPos, completionFullString)
        }
      }

      val importStmt = if (needImport) { // add an import statement if required
        scalaSourceFile.withSourceFile { (_, compiler) =>
          val refactoring = new AddImportStatement { val global = compiler }
          refactoring.addImport(scalaSourceFile.file, fullyQualifiedName)
        } getOrElse (Nil)
      } else {
        Nil
      }

      changes ++= importStmt

      // Apply the two changes in one step, if done separately we would need an
      // another `waitLoadedType` to update the positions for the refactoring
      // to work properly.
      EditorHelpers.applyChangesToFileWhileKeepingSelection(
        d, textSelection, scalaSourceFile.file, changes)

      importStmt.headOption.map(_.text.length)
    }

    def adjustCursorPosition() = EditorHelpers.doWithCurrentEditor { editor =>
      editor.selectAndReveal(startPos + completionFullString.length() + importSize.getOrElse(0), 0)
    }

    if (!tooltipsOnly && context.contextType != CompletionContext.ImportContext) {
      if (!overwrite || !doParamsProbablyExist) selectionProvider match {
        case viewer: ITextViewer if explicitParamNames.flatten.nonEmpty =>
          addArgumentTemplates(d, viewer, completionFullString)
        case _ =>
          adjustCursorPosition()
      }
      else
        adjustCursorPosition()
    }
  }

  override def getTriggerCharacters = null
  override def getContextInformationPosition = startOfArgumentList

  override def isValidFor(d: IDocument, pos: Int) =
    prefixMatches(completion.toArray, d.get.substring(startPos, pos).toArray)

  /** Insert a completion proposal, with placeholders for each explicit argument.
   *  For each argument, it inserts its name, and puts the editor in linked mode.
   *  This means that TAB can be used to navigate to the next argument, and Enter or Esc
{'fraction_non_alphanumeric': 0.08080110497237569, 'fraction_numerical': 0.001553867403314917, 'mean_word_length': 3.758214676889376, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4444717', 'n_tokens_mistral': 4802, 'n_tokens_neox': 4654, 'n_words': 1314}