<commit_before>package com.fwdekker.randomness

import com.fwdekker.randomness.array.ArraySettings
import com.intellij.codeInsight.hint.HintManager
import com.intellij.openapi.actionSystem.ActionGroup
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.options.ShowSettingsUtil
import icons.RandomnessIcons
import java.awt.event.InputEvent
import javax.swing.Icon
import kotlin.random.Random


/**
 * Thrown if a random datum could not be generated.
 *
 * @param message the detail message
 * @param cause the cause
 */
class DataGenerationException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)


/**
 * A group of actions for a particular type of random data that can be generated.
 *
 * @param icon the icon to display with the action
 */
abstract class DataGroupAction(private val icon: Icon = RandomnessIcons.Data.Base) : ActionGroup() {
    /**
     * The action used to insert a single datum.
     */
    abstract val insertAction: DataInsertAction
    /**
     * The action used to insert an array of data.
     */
    abstract val insertArrayAction: DataInsertArrayAction
    /**
     * The action used to edit the generator settings for this data type.
     */
    abstract val settingsAction: DataSettingsAction<*>


    /**
     * Returns the insert action, array insert action, and settings action.
     *
     * @param event carries information on the invocation place
     * @return the insert action, array insert action, and settings action
     */
    override fun getChildren(event: AnActionEvent?) =
        arrayOf(insertAction, insertArrayAction, settingsAction)

    /**
     * Returns `true`.
     *
     * @param context carries information about the context of the invocation
     * @return `true`
     */
    override fun canBePerformed(context: DataContext) = true

    /**
     * Chooses one of the three actions to execute based on the key modifiers in [event].
     *
     * @param event carries information on the invocation place
     */
    override fun actionPerformed(event: AnActionEvent) {
        // alt behavior is handled by implementation of `actionPerformed`
        val ctrlPressed = event.modifiers and (InputEvent.CTRL_MASK or InputEvent.CTRL_DOWN_MASK) != 0
        val shiftPressed = event.modifiers and (InputEvent.SHIFT_MASK or InputEvent.SHIFT_DOWN_MASK) != 0

        when {
            ctrlPressed -> settingsAction.actionPerformed(event)
            shiftPressed -> insertArrayAction.actionPerformed(event)
            else -> insertAction.actionPerformed(event)
        }
    }

    /**
     * Sets the title of this action.
     *
     * @param event carries information on the invocation place
     */
    override fun update(event: AnActionEvent) {
        super.update(event)

        event.presentation.text = insertAction.name
        event.presentation.icon = icon
    }

    /**
     * Returns `true`.
     *
     * @return `true`
     */
    override fun isPopup() = true
}


/**
 * Inserts a randomly generated string at the positions of the event's editor's carets.
 *
 * @param icon the icon to display with the action
 */
abstract class DataInsertAction(private val icon: Icon) : AnAction() {
    /**
     * The name of the action to display.
     */
    abstract val name: String

    /**
     * The random generator used to generate random values.
     */
    var random: Random = Random.Default


    /**
     * Sets the title of this action and disables this action if no editor is currently opened.
     *
     * @param event carries information on the invocation place
     */
    override fun update(event: AnActionEvent) {
        val presentation = event.presentation
        val editor = event.getData(CommonDataKeys.EDITOR)

        presentation.text = name
        presentation.icon = icon
        presentation.isEnabled = editor != null
    }

    /**
     * Inserts the data generated by [generateStrings] at the caret(s) in the editor; one datum for each caret.
     *
     * @param event carries information on the invocation place
     */
    @Suppress("ReturnCount") // Result of null checks at start
    override fun actionPerformed(event: AnActionEvent) {
        val editor = event.getData(CommonDataKeys.EDITOR)
            ?: return
        val project = event.getData(CommonDataKeys.PROJECT)
            ?: return

        val data =
            try {
                val altPressed = event.modifiers and (InputEvent.ALT_MASK or InputEvent.ALT_DOWN_MASK) != 0
                if (altPressed)
                    generateString().let { string -> List(editor.caretModel.caretCount) { string } }
                else
                    generateStrings(editor.caretModel.caretCount)
            } catch (e: DataGenerationException) {
                HintManager.getInstance().showErrorHint(
                    editor,
                    """
                        Randomness was unable to generate random data.
                        ${if (!e.message.isNullOrBlank()) "The following error was encountered: ${e.message}\n" else ""}
                        Check your Randomness settings and try again.
                    """.trimIndent()
                )
                return
            }

        WriteCommandAction.runWriteCommandAction(project) {
            editor.caretModel.allCarets.forEachIndexed { i, caret ->
                val start = caret.selectionStart
                val end = caret.selectionEnd
                val newEnd = start + data[i].length

                editor.document.replaceString(start, end, data[i])
                caret.setSelection(start, newEnd)
            }
        }
    }

    /**
     * Generates a random datum.
     *
     * @return a random datum
     * @throws DataGenerationException if data could not be generated
     */
    @Throws(DataGenerationException::class)
    fun generateString() = generateStrings(1).first()

    /**
     * Generates random data.
     *
     * @param count the number of data to generate
     * @return random data
     * @throws DataGenerationException if data could not be generated
     */
    @Throws(DataGenerationException::class)
    abstract fun generateStrings(count: Int = 1): List<String>
}


/**
 * Inserts a randomly generated array of strings at the positions of the event's editor's carets.
 *
 * @param arraySettings the settings to use for generating arrays
 * @param dataInsertAction the action to generate data with
 * @param icon the icon to display with the action
 */
abstract class DataInsertArrayAction(
    private val arraySettings: ArraySettings,
    private val dataInsertAction: DataInsertAction,
    icon: Icon = RandomnessIcons.Data.Array
) : DataInsertAction(icon) {
    /**
     * Generates array-like strings of random data.
     *
     * @param count the number of array-like strings to generate
     * @return array-like strings of random data
     * @throws DataGenerationException if data could not be generated
     */
    @Throws(DataGenerationException::class)
    override fun generateStrings(count: Int): List<String> {
        if (arraySettings.count <= 0)
            throw DataGenerationException("Array cannot have fewer than 1 element.")

        return dataInsertAction.generateStrings(count * arraySettings.count)
            .chunked(arraySettings.count)
            .map { arraySettings.arrayify(it) }
    }
}


/**
 * Opens the settings window for changing settings.
 *
 * @param icon the icon to display with the action
 */
abstract class DataSettingsAction<S : Settings<S>>(private val icon: Icon = RandomnessIcons.Data.Settings) :
    AnAction() {
    /**
     * The name of the action.
     */
    protected abstract val title: String

    /**
     * The class of the configurable maintaining the settings.
     */
    protected abstract val configurableClass: Class<out SettingsConfigurable<S>>


    /**
     * Sets the title of this action.
     *
     * @param event carries information on the invocation place
     */
    override fun update(event: AnActionEvent) {
        super.update(event)

        event.presentation.text = title
        event.presentation.icon = icon
    }

    /**
     * Opens the IntelliJ settings menu at the right location to adjust the configurable of type [configurableClass].
     *
     * @param event carries information on the invocation place
     */
    override fun actionPerformed(event: AnActionEvent) =
        ShowSettingsUtil.getInstance().showSettingsDialog(event.project, configurableClass)
}
<commit_msg>Remove basic action from action groups<commit_after>package com.fwdekker.randomness

import com.fwdekker.randomness.array.ArraySettings
import com.intellij.codeInsight.hint.HintManager
import com.intellij.openapi.actionSystem.ActionGroup
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.options.ShowSettingsUtil
import icons.RandomnessIcons
import java.awt.event.InputEvent
import javax.swing.Icon
import kotlin.random.Random


/**
 * Thrown if a random datum could not be generated.
 *
 * @param message the detail message
 * @param cause the cause
 */
class DataGenerationException(message: String? = null, cause: Throwable? = null) : Exception(message, cause)


/**
 * A group of actions for a particular type of random data that can be generated.
 *
 * @param icon the icon to display with the action
 */
abstract class DataGroupAction(private val icon: Icon = RandomnessIcons.Data.Base) : ActionGroup() {
    /**
     * The action used to insert a single datum.
     */
    abstract val insertAction: DataInsertAction
    /**
     * The action used to insert an array of data.
     */
    abstract val insertArrayAction: DataInsertArrayAction
    /**
     * The action used to edit the generator settings for this data type.
     */
    abstract val settingsAction: DataSettingsAction<*>


    /**
     * Returns the insert action, array insert action, and settings action.
     *
     * @param event carries information on the invocation place
     * @return the insert action, array insert action, and settings action
     */
    override fun getChildren(event: AnActionEvent?) = arrayOf(insertArrayAction, settingsAction)

    /**
     * Returns `true`.
     *
     * @param context carries information about the context of the invocation
     * @return `true`
     */
    override fun canBePerformed(context: DataContext) = true

    /**
     * Chooses one of the three actions to execute based on the key modifiers in [event].
     *
     * @param event carries information on the invocation place
     */
    override fun actionPerformed(event: AnActionEvent) {
        // alt behavior is handled by implementation of `actionPerformed`
        val ctrlPressed = event.modifiers and (InputEvent.CTRL_MASK or InputEvent.CTRL_DOWN_MASK) != 0
        val shiftPressed = event.modifiers and (InputEvent.SHIFT_MASK or InputEvent.SHIFT_DOWN_MASK) != 0

        when {
            ctrlPressed -> settingsAction.actionPerformed(event)
            shiftPressed -> insertArrayAction.actionPerformed(event)
            else -> insertAction.actionPerformed(event)
        }
    }

    /**
     * Sets the title of this action.
     *
     * @param event carries information on the invocation place
     */
    override fun update(event: AnActionEvent) {
        super.update(event)

        event.presentation.text = insertAction.name
        event.presentation.icon = icon
    }

    /**
     * Returns `true`.
     *
     * @return `true`
     */
    override fun isPopup() = true
}


/**
 * Inserts a randomly generated string at the positions of the event's editor's carets.
 *
 * @param icon the icon to display with the action
 */
abstract class DataInsertAction(private val icon: Icon) : AnAction() {
    /**
     * The name of the action to display.
     */
    abstract val name: String

    /**
     * The random generator used to generate random values.
     */
    var random: Random = Random.Default


    /**
     * Sets the title of this action and disables this action if no editor is currently opened.
     *
     * @param event carries information on the invocation place
     */
    override fun update(event: AnActionEvent) {
        val presentation = event.presentation
        val editor = event.getData(CommonDataKeys.EDITOR)

        presentation.text = name
        presentation.icon = icon
        presentation.isEnabled = editor != null
    }

    /**
     * Inserts the data generated by [generateStrings] at the caret(s) in the editor; one datum for each caret.
     *
     * @param event carries information on the invocation place
     */
    @Suppress("ReturnCount") // Result of null checks at start
    override fun actionPerformed(event: AnActionEvent) {
        val editor = event.getData(CommonDataKeys.EDITOR)
            ?: return
        val project = event.getData(CommonDataKeys.PROJECT)
            ?: return

        val data =
            try {
                val altPressed = event.modifiers and (InputEvent.ALT_MASK or InputEvent.ALT_DOWN_MASK) != 0
                if (altPressed)
                    generateString().let { string -> List(editor.caretModel.caretCount) { string } }
                else
                    generateStrings(editor.caretModel.caretCount)
            } catch (e: DataGenerationException) {
                HintManager.getInstance().showErrorHint(
                    editor,
                    """
                        Randomness was unable to generate random data.
                        ${if (!e.message.isNullOrBlank()) "The following error was encountered: ${e.message}\n" else ""}
                        Check your Randomness settings and try again.
                    """.trimIndent()
                )
                return
            }

        WriteCommandAction.runWriteCommandAction(project) {
            editor.caretModel.allCarets.forEachIndexed { i, caret ->
                val start = caret.selectionStart
                val end = caret.selectionEnd
                val newEnd = start + data[i].length

                editor.document.replaceString(start, end, data[i])
                caret.setSelection(start, newEnd)
            }
        }
    }

    /**
     * Generates a random datum.
     *
     * @return a random datum
     * @throws DataGenerationException if data could not be generated
     */
    @Throws(DataGenerationException::class)
    fun generateString() = generateStrings(1).first()

    /**
     * Generates random data.
     *
     * @param count the number of data to generate
     * @return random data
     * @throws DataGenerationException if data could not be generated
     */
    @Throws(DataGenerationException::class)
    abstract fun generateStrings(count: Int = 1): List<String>
}


/**
 * Inserts a randomly generated array of strings at the positions of the event's editor's carets.
 *
 * @param arraySettings the settings to use for generating arrays
 * @param dataInsertAction the action to generate data with
 * @param icon the icon to display with the action
 */
abstract class DataInsertArrayAction(
    private val arraySettings: ArraySettings,
    private val dataInsertAction: DataInsertAction,
    icon: Icon = RandomnessIcons.Data.Array
) : DataInsertAction(icon) {
    /**
     * Generates array-like strings of random data.
     *
     * @param count the number of array-like strings to generate
     * @return array-like strings of random data
     * @throws DataGenerationException if data could not be generated
     */
    @Throws(DataGenerationException::class)
    override fun generateStrings(count: Int): List<String> {
        if (arraySettings.count <= 0)
            throw DataGenerationException("Array cannot have fewer than 1 element.")

        return dataInsertAction.generateStrings(count * arraySettings.count)
            .chunked(arraySettings.count)
            .map { arraySettings.arrayify(it) }
    }
}


/**
 * Opens the settings window for changing settings.
 *
 * @param icon the icon to display with the action
 */
abstract class DataSettingsAction<S : Settings<S>>(private val icon: Icon = RandomnessIcons.Data.Settings) :
    AnAction() {
    /**
     * The name of the action.
     */
    protected abstract val title: String

    /**
     * The class of the configurable maintaining the settings.
     */
    protected abstract val configurableClass: Class<out SettingsConfigurable<S>>


    /**
     * Sets the title of this action.
     *
     * @param event carries information on the invocation place
     */
    override fun update(event: AnActionEvent) {
        super.update(event)

        event.presentation.text = title
        event.presentation.icon = icon
    }

    /**
     * Opens the IntelliJ settings menu at the right location to adjust the configurable of type [configurableClass].
     *
     * @param event carries information on the invocation place
     */
    override fun actionPerformed(event: AnActionEvent) =
        ShowSettingsUtil.getInstance().showSettingsDialog(event.project, configurableClass)
}

{'fraction_non_alphanumeric': 0.08095156597638199, 'fraction_numerical': 0.0007986764789776941, 'mean_word_length': 2.5593908629441624, 'pattern_counts': {'":': 0, '<': 19, '<?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': '976115', 'n_tokens_mistral': 4583, 'n_tokens_neox': 4426, 'n_words': 1476}