<commit_before>package org.jetbrains.plugins.scala.editor.copy

import com.intellij.codeInsight.editorActions.CopyPastePreProcessor
import com.intellij.openapi.editor.{Caret, Editor, RawText}
import com.intellij.openapi.project.Project
import com.intellij.psi.{PsiElement, PsiFile, PsiWhiteSpace}
import org.jetbrains.plugins.scala.ScalaLanguage
import org.jetbrains.plugins.scala.extensions.{ObjectExt, PsiElementExt, PsiFileExt}

class Scala3IndentationBasedSyntaxCopyPastePreProcessor extends CopyPastePreProcessor {
  override def preprocessOnCopy(file: PsiFile, startOffsets: Array[Int], endOffsets: Array[Int], text: String): String = {
    // TODO make this work for multiple selections
    // TODO what about negative indents
    // if multi-line string, strip first-line indentation from all lines
    if (file.useIndentationBasedSyntax && startOffsets.length == 1 && text.contains('\n')) {
      var firstElement = file.findElementAt(startOffsets(0)).toOption
      file.getTextRange
      while (firstElement.exists(_.isWhitespace))
        firstElement = firstElement.get.nextVisibleLeaf
      if (firstElement.nonEmpty) {
        val leadingSpaceOnLine = firstElement.get.getIndentWhitespace()
        text.concat("\n").linesIterator.map(_.stripPrefix(leadingSpaceOnLine)).mkString("\n")
      } else null
    } else null
  }

  override def preprocessOnPaste(project: Project, file: PsiFile, editor: Editor, text: String, rawText: RawText): String = {
    // TODO make this work for multiple carets
    // if multi-line string and caret on new line, add caret indentation to all lines
    if (file.useIndentationBasedSyntax && text.contains('\n')) {
      val caret = editor.getCaretModel.getCurrentCaret
      val indentWhitespace = file.elementAt(caret.getSelectionStart) match {
        case Some(ws: PsiWhiteSpace) => {
          if (ws.textContains('\n')) {
            var wsText = ws.getText
            wsText = wsText.substring(0, caret.getSelectionStart - ws.startOffset)
            wsText.substring(wsText.lastIndexOf('\n') + 1)
          } else ""
        }
        // TODO what about paste in text (caret not preceded by whitespace)
        case Some(el) => el.getIndentWhitespace()
        case _ => ""
      }
      text.linesIterator.map(indentWhitespace + _).mkString("\n").stripLeading()
    } else text
  }
}<commit_msg>Fix caret at EOF bug in Scala3IndentationBasedSyntaxCopyPastePreProcessor #SCL-18901<commit_after>package org.jetbrains.plugins.scala.editor.copy

import com.intellij.codeInsight.editorActions.CopyPastePreProcessor
import com.intellij.openapi.editor.{Editor, RawText}
import com.intellij.openapi.project.Project
import com.intellij.psi.{PsiFile, PsiWhiteSpace}
import org.jetbrains.plugins.scala.editor.ScalaEditorUtils.findElementAtCaret_WithFixedEOF
import org.jetbrains.plugins.scala.extensions.{ObjectExt, PsiElementExt, PsiFileExt}

class Scala3IndentationBasedSyntaxCopyPastePreProcessor extends CopyPastePreProcessor {
  override def preprocessOnCopy(file: PsiFile, startOffsets: Array[Int], endOffsets: Array[Int], text: String): String = {
    // TODO make this work for multiple selections
    // TODO what about negative indents
    // if multi-line string, strip first-line indentation from all lines
    if (file.useIndentationBasedSyntax && startOffsets.length == 1 && text.contains('\n')) {
      var firstElement = file.findElementAt(startOffsets(0)).toOption
      file.getTextRange
      while (firstElement.exists(_.isWhitespace))
        firstElement = firstElement.get.nextVisibleLeaf
      if (firstElement.nonEmpty) {
        val leadingSpaceOnLine = firstElement.get.getIndentWhitespace()
        text.concat("\n").linesIterator.map(_.stripPrefix(leadingSpaceOnLine)).mkString("\n")
      } else null
    } else null
  }

  override def preprocessOnPaste(project: Project, file: PsiFile, editor: Editor, text: String, rawText: RawText): String = {
    // TODO make this work for multiple carets
    // if multi-line string and caret on new line, add caret indentation to all lines
    if (file.useIndentationBasedSyntax && text.contains('\n')) {
      val caret = editor.getCaretModel.getCurrentCaret
      val elementAtCaret = findElementAtCaret_WithFixedEOF(file, editor.getDocument, caret.getSelectionStart)
      val indentWhitespace = elementAtCaret match {
        case null => ""
        case ws: PsiWhiteSpace =>
          var wsText = ws.getText
          wsText = wsText.substring(0, caret.getSelectionStart - ws.startOffset)
          if (wsText.contains('\n'))
            wsText.substring(wsText.lastIndexOf('\n') + 1)
          else ""
        // TODO what if caret not preceded by whitespace - take line indentation
        case el => el.getIndentWhitespace()
      }
      text.linesIterator.map(indentWhitespace + _).mkString("\n").stripLeading()
    } else text
  }
}
{'fraction_non_alphanumeric': 0.1011721159777915, 'fraction_numerical': 0.00329015011309891, 'mean_word_length': 3.7269193391642372, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, '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': '4160313', 'n_tokens_mistral': 1457, 'n_tokens_neox': 1418, 'n_words': 325}