Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,9 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
api("org.jetbrains.kotlin:kotlin-reflect")

testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:${properties("mockitoKotlinVersion")}")
testImplementation("org.assertj:assertj-core:${properties("assertjVersion")}")
testImplementation("org.assertj:assertj-swing-junit:${properties("assertjSwingVersion")}")
testImplementation("org.junit.platform:junit-platform-runner:${properties("junitRunnerVersion")}")
testRuntimeOnly("org.junit.platform:junit-platform-runner:${properties("junitRunnerVersion")}")
testImplementation("org.junit.jupiter:junit-jupiter-api:${properties("junitVersion")}")
testImplementation("org.junit.jupiter:junit-jupiter-engine:${properties("junitVersion")}")
testImplementation("org.junit.vintage:junit-vintage-engine:${properties("junitVersion")}")
Expand Down
1 change: 0 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ emojiVersion = 5.1.1
jacocoVersion = 0.8.7
junitVersion = 5.7.2
junitRunnerVersion = 1.7.2
mockitoKotlinVersion = 2.2.0
spekVersion = 2.0.15
uuidGeneratorVersion = 3.3.0

Expand Down
11 changes: 11 additions & 0 deletions src/main/kotlin/com/fwdekker/randomness/ActuallyPrivate.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.fwdekker.randomness


/**
* Indicates that the target is to be considered marked as private and should not be used externally, even though it is
* not marked as such.
*
* @property reason Explains why the target is not marked as private.
*/
@Retention(AnnotationRetention.SOURCE)
annotation class ActuallyPrivate(val reason: String)
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ enum class CapitalizationMode(val descriptor: String, val transform: (String) ->
/**
* Makes each letter randomly uppercase or lowercase.
*/
RANDOM("random", { string -> string.toCharArray().map { it.toRandomCase() }.joinToString("") });
RANDOM("random", { string -> string.toCharArray().map { it.toRandomCase() }.joinToString("") }),

/**
* Unused in production code.
*/
DUMMY("dummy", { string -> string });


/**
Expand Down
92 changes: 92 additions & 0 deletions src/main/kotlin/com/fwdekker/randomness/InsertAction.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.fwdekker.randomness

import com.intellij.codeInsight.hint.HintManager
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.command.WriteCommandAction
import javax.swing.Icon


/**
* Inserts strings in the editor.
*
* @property repeat True if and only if the same value should be inserted at each caret.
*/
abstract class InsertAction(private val repeat: Boolean = false) : AnAction() {
/**
* The icon to display as representing this action.
*/
abstract val icon: Icon?

/**
* The name of this action.
*/
abstract val name: String


/**
* 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.icon = icon
presentation.text = name
presentation.isEnabled = editor != null
}

/**
* Inserts the data generated by the scheme 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 {
generateTimely {
if (repeat)
generateStrings(1).single().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 the given number of strings.
*
* @param count the number of strings to generate
* @return the given number of strings
*/
abstract fun generateStrings(count: Int): List<String>
}
78 changes: 20 additions & 58 deletions src/main/kotlin/com/fwdekker/randomness/Scheme.kt
Original file line number Diff line number Diff line change
@@ -1,42 +1,29 @@
package com.fwdekker.randomness

import com.fwdekker.randomness.array.ArraySchemeDecorator
import com.intellij.util.xmlb.XmlSerializerUtil
import com.intellij.util.xmlb.annotations.Transient
import icons.RandomnessIcons
import javax.swing.Icon
import kotlin.random.Random


/**
* A scheme is a configurable random number generator.
* A scheme is a [State] that is also a configurable random number generator.
*
* Schemes can additionally be given [SchemeDecorator]s that extend their functionality.
*/
abstract class Scheme {
abstract class Scheme : State() {
/**
* Settings that determine whether the output should be an array of values.
*/
abstract val decorator: ArraySchemeDecorator?

/**
* The name of the scheme as shown to the user.
* The icon for this scheme; depends on whether its array decorator is enabled.
*/
abstract val name: String

/**
* The icons that represent schemes of this type.
*/
@Transient
open val icons: RandomnessIcons? = null

/**
* The icon that represents this scheme instance.
*/
@get:Transient
open val icon: Icon?
get() = if (decorator?.enabled == true) icons?.Array
else icons?.Base
override val icon: Icon?
get() =
if (decorator?.enabled == true) icons?.Array
else icons?.Base

/**
* The random number generator used to generate random values.
Expand All @@ -53,11 +40,18 @@ abstract class Scheme {
* @throws DataGenerationException if data could not be generated
*/
@Throws(DataGenerationException::class)
fun generateStrings(count: Int = 1): List<String> =
decorator?.let {
it.generator = ::generateUndecoratedStrings
it.generateStrings(count)
} ?: generateUndecoratedStrings(count)
fun generateStrings(count: Int = 1): List<String> {
doValidate()?.also { throw DataGenerationException(it) }

return decorator.let { decorator ->
if (decorator == null) {
generateUndecoratedStrings(count)
} else {
decorator.generator = ::generateUndecoratedStrings
decorator.generateStrings(count)
}
}
}

/**
* Generates random data according to the settings in this scheme, ignoring settings from decorators.
Expand All @@ -70,39 +64,7 @@ abstract class Scheme {
abstract fun generateUndecoratedStrings(count: Int = 1): List<String>


/**
* Validates the scheme, and indicates whether and why it is invalid.
*
* @return `null` if the scheme is valid, or a string explaining why the scheme is invalid
*/
open fun doValidate(): String? = null

/**
* Copies the given scheme into this scheme.
*
* Works by copying all references in a [deepCopy] of [scheme] into `this`.
*
* @param scheme the scheme to copy into this scheme; should be a subclass of this scheme
*/
fun copyFrom(scheme: Scheme) = XmlSerializerUtil.copyBean(scheme.deepCopy(), this)

/**
* Returns a deep copy of this scheme.
*
* @return a deep copy of this scheme
*/
abstract fun deepCopy(): Scheme


/**
* Holds constants.
*/
companion object {
/**
* The default value of the [name] field.
*/
const val DEFAULT_NAME: String = "Unnamed scheme"
}
abstract override fun deepCopy(): Scheme
}

/**
Expand Down
77 changes: 0 additions & 77 deletions src/main/kotlin/com/fwdekker/randomness/SchemeEditor.kt

This file was deleted.

63 changes: 63 additions & 0 deletions src/main/kotlin/com/fwdekker/randomness/SettingsConfigurable.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.fwdekker.randomness

import com.intellij.openapi.options.Configurable
import com.intellij.openapi.options.ConfigurationException
import javax.swing.JComponent


/**
* Tells IntelliJ how to use a [StateEditor] in the settings dialog.
*/
@Suppress("LateinitUsage") // `createComponent` is invoked before any of the other methods
abstract class SettingsConfigurable : Configurable {
/**
* The user interface for changing the settings, displayed in IntelliJ's settings window.
*/
lateinit var editor: StateEditor<*> private set


/**
* Returns true if the settings were modified since they were loaded or they are invalid.
*
* @return true if the settings were modified since they were loaded or they are invalid
*/
override fun isModified() = editor.isModified() || editor.doValidate() != null

/**
* Saves the changes in the settings component to the default settings object.
*
* @throws ConfigurationException if the changes cannot be saved
*/
@Throws(ConfigurationException::class)
override fun apply() {
val validationInfo = editor.doValidate()
if (validationInfo != null)
throw ConfigurationException(validationInfo, "Failed to save settings")

editor.applyState()
}

/**
* Discards unsaved changes in the settings component.
*/
override fun reset() = editor.reset()


/**
* Creates a new editor and returns the root pane of the created editor.
*
* @return the root pane of the created editor
*/
override fun createComponent(): JComponent =
createEditor().let {
editor = it
it.rootComponent
}

/**
* Creates a new editor.
*
* @return a new editor
*/
abstract fun createEditor(): StateEditor<*>
}
Loading