<commit_before>// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.build.output

import com.intellij.build.FilePosition
import com.intellij.build.events.MessageEvent
import com.intellij.build.events.impl.FileMessageEventImpl
import com.intellij.build.events.impl.MessageEventImpl
import com.intellij.openapi.util.text.StringUtil
import java.io.File
import java.util.function.Consumer
import java.util.regex.Pattern


/**
 * TODO should be moved to the kotlin plugin
 *
 * Parses kotlinc's output.
 */
class KotlincOutputParser : BuildOutputParser {

  companion object {
    private const val COMPILER_MESSAGES_GROUP = "Kotlin compiler"
  }

  override fun parse(line: String, reader: BuildOutputInstantReader, consumer: Consumer<in MessageEvent>): Boolean {
    val colonIndex1 = line.colon()

    val severity = if (colonIndex1 >= 0) line.substringBeforeAndTrim(colonIndex1) else return false
    if (!severity.startsWithSeverityPrefix()) return false

    val lineWoSeverity = line.substringAfterAndTrim(colonIndex1)
    val colonIndex2 = lineWoSeverity.colon().skipDriveOnWin(lineWoSeverity)
    if (colonIndex2 >= 0) {
      val path = lineWoSeverity.substringBeforeAndTrim(colonIndex2)
      val file = File(path)

      val fileExtension = file.extension.toLowerCase()
      if (!file.isFile || (fileExtension != "kt" && fileExtension != "java")) {
        return addMessage(createMessage(reader.buildId, getMessageKind(severity), lineWoSeverity.amendNextLinesIfNeeded(reader), line),
                          consumer)
      }

      val lineWoPath = lineWoSeverity.substringAfterAndTrim(colonIndex2)
      val colonIndex3 = lineWoPath.colon()
      if (colonIndex3 >= 0) {
        val position = lineWoPath.substringBeforeAndTrim(colonIndex3)

        val matcher = KOTLIN_POSITION_PATTERN.matcher(position).takeIf { it.matches() } ?: JAVAC_POSITION_PATTERN.matcher(position)
        val relatedNextLines = "".amendNextLinesIfNeeded(reader)
        val message = lineWoPath.substringAfterAndTrim(colonIndex3) + relatedNextLines
        val details = lineWoSeverity + relatedNextLines

        if (matcher.matches()) {
          val lineNumber = matcher.group(1)
          val symbolNumber = if (matcher.groupCount() >= 2) matcher.group(2) else "1"
          if (lineNumber != null) {
            val symbolNumberText = symbolNumber.toInt()
            return addMessage(createMessageWithLocation(
              reader.buildId, getMessageKind(severity), message, path, lineNumber.toInt(), symbolNumberText, details), consumer)
          }
        }

        return addMessage(createMessage(reader.buildId, getMessageKind(severity), message, details), consumer)
      }
      else {
        val text = lineWoSeverity.amendNextLinesIfNeeded(reader)
        return addMessage(createMessage(reader.buildId, getMessageKind(severity), text, text), consumer)
      }
    }

    return false
  }

  private val COLON = ":"
  private val KOTLIN_POSITION_PATTERN = Pattern.compile("\\(([0-9]*), ([0-9]*)\\)")
  private val JAVAC_POSITION_PATTERN = Pattern.compile("([0-9]+)")

  private fun String.amendNextLinesIfNeeded(reader: BuildOutputInstantReader): String {
    var nextLine = reader.readLine()

    val builder = StringBuilder(this)
    while (nextLine != null) {
      if (nextLine.isNextMessage()) {
        reader.pushBack()
        break
      }
      else {
        builder.append("\n").append(nextLine)
        nextLine = reader.readLine()
      }
    }
    return builder.toString()
  }

  private fun String.isNextMessage(): Boolean {
    val colonIndex1 = indexOf(COLON)
    return colonIndex1 == 0
           || (colonIndex1 >= 0 && substring(0, colonIndex1).startsWithSeverityPrefix()) // Next Kotlin message
           || StringUtil.startsWith(this, "Note: ") // Next javac info message candidate
           || StringUtil.containsIgnoreCase(this, "FAILURE")
           || StringUtil.containsIgnoreCase(this, "FAILED")
  }

  private fun String.startsWithSeverityPrefix() = getMessageKind(this) != MessageEvent.Kind.SIMPLE

  private fun getMessageKind(kind: String) = when (kind) {
    "e" -> MessageEvent.Kind.ERROR
    "w" -> MessageEvent.Kind.WARNING
    "i" -> MessageEvent.Kind.INFO
    "v" -> MessageEvent.Kind.SIMPLE
    else -> MessageEvent.Kind.SIMPLE
  }

  private fun String.substringAfterAndTrim(index: Int) = substring(index + 1).trim()
  private fun String.substringBeforeAndTrim(index: Int) = substring(0, index).trim()
  private fun String.colon() = indexOf(COLON)<commit_msg>Build: filter unrelated gradle output messages started with '> Task' for kotlinc output parser<commit_after>// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.build.output

import com.intellij.build.FilePosition
import com.intellij.build.events.MessageEvent
import com.intellij.build.events.impl.FileMessageEventImpl
import com.intellij.build.events.impl.MessageEventImpl
import com.intellij.openapi.util.text.StringUtil
import java.io.File
import java.util.function.Consumer
import java.util.regex.Pattern


/**
 * TODO should be moved to the kotlin plugin
 *
 * Parses kotlinc's output.
 */
class KotlincOutputParser : BuildOutputParser {

  companion object {
    private const val COMPILER_MESSAGES_GROUP = "Kotlin compiler"
  }

  override fun parse(line: String, reader: BuildOutputInstantReader, consumer: Consumer<in MessageEvent>): Boolean {
    val colonIndex1 = line.colon()

    val severity = if (colonIndex1 >= 0) line.substringBeforeAndTrim(colonIndex1) else return false
    if (!severity.startsWithSeverityPrefix()) return false

    val lineWoSeverity = line.substringAfterAndTrim(colonIndex1)
    val colonIndex2 = lineWoSeverity.colon().skipDriveOnWin(lineWoSeverity)
    if (colonIndex2 >= 0) {
      val path = lineWoSeverity.substringBeforeAndTrim(colonIndex2)
      val file = File(path)

      val fileExtension = file.extension.toLowerCase()
      if (!file.isFile || (fileExtension != "kt" && fileExtension != "java")) {
        return addMessage(createMessage(reader.buildId, getMessageKind(severity), lineWoSeverity.amendNextLinesIfNeeded(reader), line),
                          consumer)
      }

      val lineWoPath = lineWoSeverity.substringAfterAndTrim(colonIndex2)
      val colonIndex3 = lineWoPath.colon()
      if (colonIndex3 >= 0) {
        val position = lineWoPath.substringBeforeAndTrim(colonIndex3)

        val matcher = KOTLIN_POSITION_PATTERN.matcher(position).takeIf { it.matches() } ?: JAVAC_POSITION_PATTERN.matcher(position)
        val relatedNextLines = "".amendNextLinesIfNeeded(reader)
        val message = lineWoPath.substringAfterAndTrim(colonIndex3) + relatedNextLines
        val details = lineWoSeverity + relatedNextLines

        if (matcher.matches()) {
          val lineNumber = matcher.group(1)
          val symbolNumber = if (matcher.groupCount() >= 2) matcher.group(2) else "1"
          if (lineNumber != null) {
            val symbolNumberText = symbolNumber.toInt()
            return addMessage(createMessageWithLocation(
              reader.buildId, getMessageKind(severity), message, path, lineNumber.toInt(), symbolNumberText, details), consumer)
          }
        }

        return addMessage(createMessage(reader.buildId, getMessageKind(severity), message, details), consumer)
      }
      else {
        val text = lineWoSeverity.amendNextLinesIfNeeded(reader)
        return addMessage(createMessage(reader.buildId, getMessageKind(severity), text, text), consumer)
      }
    }

    return false
  }

  private val COLON = ":"
  private val KOTLIN_POSITION_PATTERN = Pattern.compile("\\(([0-9]*), ([0-9]*)\\)")
  private val JAVAC_POSITION_PATTERN = Pattern.compile("([0-9]+)")

  private fun String.amendNextLinesIfNeeded(reader: BuildOutputInstantReader): String {
    var nextLine = reader.readLine()

    val builder = StringBuilder(this)
    while (nextLine != null) {
      if (nextLine.isNextMessage()) {
        reader.pushBack()
        break
      }
      else {
        builder.append("\n").append(nextLine)
        nextLine = reader.readLine()
      }
    }
    return builder.toString()
  }

  private fun String.isNextMessage(): Boolean {
    val colonIndex1 = indexOf(COLON)
    return colonIndex1 == 0
           || (colonIndex1 >= 0 && substring(0, colonIndex1).startsWithSeverityPrefix()) // Next Kotlin message
           || StringUtil.startsWith(this, "Note: ") // Next javac info message candidate
           || StringUtil.startsWith(this, "> Task :") // Next gradle message candidate
           || StringUtil.containsIgnoreCase(this, "FAILURE")
           || StringUtil.containsIgnoreCase(this, "FAILED")
  }

  private fun String.startsWithSeverityPrefix() = getMessageKind(this) != MessageEvent.Kind.SIMPLE

  private fun getMessageKind(kind: String) = when (kind) {
    "e" -> MessageEvent.Kind.ERROR
    "w" -> MessageEvent.Kind.WARNING
    "i" -> MessageEvent.Kind.INFO
    "v" -> MessageEvent.Kind.SIMPLE
    else -> MessageEvent.Kind.SIMPLE
  }

  private fun String.substringAfterAndTrim(index: Int) = substring(index + 1).trim()
  private fun String.substringBeforeAndTrim(index: Int) = substring(0, index).trim()
  private fun String.colon() = indexOf(COLON)
{'fraction_non_alphanumeric': 0.10709732256693583, 'fraction_numerical': 0.009349766255843604, 'mean_word_length': 3.5298363811357074, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 27, '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': '4741873', 'n_tokens_mistral': 2886, 'n_tokens_neox': 2763, 'n_words': 611}