From 24e20e51f78d757a825841b7adb4d55d6691d716 Mon Sep 17 00:00:00 2001 From: Nicholas Stonecipher Date: Wed, 17 Jun 2020 08:19:46 -0400 Subject: [PATCH] Improve upon `//signsearch` --- src/main/kotlin/Find.kt | 17 ++++++--- src/main/kotlin/RedstoneTools.kt | 30 ++++++++++----- src/main/kotlin/SignSearch.kt | 63 ++++++++++++++++++++++---------- src/main/kotlin/Util.kt | 18 +++++++-- 4 files changed, 90 insertions(+), 38 deletions(-) diff --git a/src/main/kotlin/Find.kt b/src/main/kotlin/Find.kt index 96f9367..375fb9c 100644 --- a/src/main/kotlin/Find.kt +++ b/src/main/kotlin/Find.kt @@ -13,14 +13,14 @@ import com.sk89q.worldedit.function.RegionFunction import com.sk89q.worldedit.function.RegionMaskingFilter import com.sk89q.worldedit.function.operation.Operations import com.sk89q.worldedit.function.visitor.RegionVisitor -import com.sk89q.worldedit.math.BlockVector3 import com.sk89q.worldedit.util.formatting.component.InvalidComponentException +import com.sk89q.worldedit.util.formatting.text.TextComponent import org.bukkit.entity.Player import java.util.* import kotlin.collections.HashMap import kotlin.math.ceil -val findResults = HashMap>() +val findResults = HashMap>() @CommandAlias("/find") @Description("Find some shid in selecton") @@ -60,21 +60,26 @@ class Find(private val worldEdit: WorldEdit) : BaseCommand() { } catch (e: IncompleteRegionException) { throw RedstoneToolsException(MAKE_SELECTION_FIRST) } - val locations = mutableListOf() + val locations = mutableListOf() val maskFactory = MaskFactory(worldEdit) val parserContext = ParserContext().apply { extent = session.selectionWorld } val blockMask = maskFactory.parseFromInput(arg, parserContext) val regionFunction = RegionFunction { position -> - locations.add(position) + locations.add(LocationContainer(position, TextComponent.of(position.toString()))) false } val regionMaskingFilter = RegionMaskingFilter(blockMask, regionFunction) val regionVisitor = RegionVisitor(selection, regionMaskingFilter) Operations.complete(regionVisitor) - findResults[player.uniqueId] = locations - page(BukkitAdapter.adapt(player), 1) + if (locations.isNotEmpty()) { + findResults[player.uniqueId] = locations + page(BukkitAdapter.adapt(player), 1) + } else { + findResults.remove(player.uniqueId) + player.printInfo(TextComponent.of("No results found.")) + } } } diff --git a/src/main/kotlin/RedstoneTools.kt b/src/main/kotlin/RedstoneTools.kt index 3851b09..163cc33 100644 --- a/src/main/kotlin/RedstoneTools.kt +++ b/src/main/kotlin/RedstoneTools.kt @@ -2,6 +2,7 @@ package redstonetools import co.aikar.commands.* import com.sk89q.worldedit.WorldEdit +import com.sk89q.worldedit.WorldEditException import com.sk89q.worldedit.bukkit.WorldEditPlugin import com.sk89q.worldedit.extension.factory.MaskFactory import com.sk89q.worldedit.math.BlockVector3 @@ -27,14 +28,17 @@ class RedstoneTools : JavaPlugin() { args: List, throwable: Throwable ): Boolean { - val exception = throwable as? RedstoneToolsException - if (exception == null) { - logger.log(Level.SEVERE, "Error in ACF", throwable) - return false + return when (throwable) { + is RedstoneToolsException, is WorldEditException -> { + val message = throwable.message ?: "Something went wrong." + sender.sendMessage("${ChatColor.DARK_GRAY}[${ChatColor.GRAY}RedstoneTools${ChatColor.DARK_GRAY}]${ChatColor.GRAY} $message") + true + } + else -> { + logger.log(Level.SEVERE, "Error in ACF", throwable) + false + } } - val message = exception.message ?: "Something went wrong." - sender.sendMessage("${ChatColor.DARK_GRAY}[${ChatColor.GRAY}RedstoneTools${ChatColor.DARK_GRAY}]${ChatColor.GRAY} $message") - return true } override fun onEnable() { @@ -130,7 +134,9 @@ class MaskCompletionHandler(worldEdit: WorldEdit) : } } -class LocationsPaginationBox(private val locations: MutableList, title: String, command: String) : +data class LocationContainer(val location: BlockVector3, val match: TextComponent) + +class LocationsPaginationBox(private val locations: MutableList, title: String, command: String) : PaginationBox("${ChatColor.LIGHT_PURPLE}$title", command) { init { @@ -139,9 +145,13 @@ class LocationsPaginationBox(private val locations: MutableList, t override fun getComponent(number: Int): Component { if (number > locations.size) throw IllegalArgumentException("Invalid location index.") - return TextComponent.of("${number+1}: ${locations[number]}") + return TextComponent.of("${number+1}: ") + .append(locations[number].match) .color(TextColor.LIGHT_PURPLE) - .clickEvent(ClickEvent.runCommand("/tp ${locations[number].x} ${locations[number].y} ${locations[number].z}")) + .clickEvent(ClickEvent.runCommand("/tp" + + " ${locations[number].location.x}" + + " ${locations[number].location.y}" + + " ${locations[number].location.z}")) .hoverEvent(HoverEvent.showText(TextComponent.of("Click to teleport"))) } diff --git a/src/main/kotlin/SignSearch.kt b/src/main/kotlin/SignSearch.kt index d1c392e..115c4a5 100644 --- a/src/main/kotlin/SignSearch.kt +++ b/src/main/kotlin/SignSearch.kt @@ -13,17 +13,19 @@ import com.sk89q.worldedit.function.RegionMaskingFilter import com.sk89q.worldedit.function.mask.BlockCategoryMask import com.sk89q.worldedit.function.operation.Operations import com.sk89q.worldedit.function.visitor.RegionVisitor -import com.sk89q.worldedit.math.BlockVector3 import com.sk89q.worldedit.util.formatting.component.InvalidComponentException import com.sk89q.worldedit.util.formatting.text.TextComponent +import com.sk89q.worldedit.util.formatting.text.event.HoverEvent +import com.sk89q.worldedit.util.formatting.text.format.TextColor import com.sk89q.worldedit.util.formatting.text.serializer.gson.GsonComponentSerializer +import com.sk89q.worldedit.world.block.BaseBlock import com.sk89q.worldedit.world.block.BlockCategories import org.bukkit.entity.Player import java.util.* import kotlin.collections.HashMap import kotlin.math.ceil -val searchResults = HashMap>() +val searchResults = HashMap>() @CommandAlias("/signsearch|/ss") @Description("Search for text of signs within a selection using a regular expression") @@ -45,8 +47,8 @@ class SignSearch(private val worldEdit: WorldEdit) : BaseCommand() { player: Player, page: Int ) { - val locations = searchResults[player.uniqueId] ?: throw RedstoneToolsException(MAKE_SELECTION_FIRST) - val paginationBox = LocationsPaginationBox(locations, "Search Results", "//signsearch -p %page%") + val results = searchResults[player.uniqueId] ?: throw RedstoneToolsException(MAKE_SELECTION_FIRST) + val paginationBox = LocationsPaginationBox(results, "Search Results", "//signsearch -p %page%") val component = try { paginationBox.create(page) } catch (e: InvalidComponentException) { @@ -67,36 +69,59 @@ class SignSearch(private val worldEdit: WorldEdit) : BaseCommand() { } catch (e: IncompleteRegionException) { throw RedstoneToolsException(MAKE_SELECTION_FIRST) } - val matches = mutableListOf() + val matchMap = mutableListOf() val blockMask = BlockCategoryMask(session.selectionWorld, BlockCategories.SIGNS) val regionFunction = RegionFunction { position -> val baseBlock = session.selectionWorld.getFullBlock(position) - if (baseBlock.hasNbtData()) { - val compoundTag = baseBlock.nbtData!! - val content = buildString { - for (i in 1..4) { - val textTag = compoundTag.value["Text$i"] as StringTag - val component = GsonComponentSerializer.INSTANCE.deserialize(textTag.value) as TextComponent - append(component.getAllContent()) - } - } - if (content.contains(pattern)) { - matches.add(position) - } + val parsedMatch = parseMatch(baseBlock, pattern) + if (parsedMatch != null) { + matchMap.add(LocationContainer(position, parsedMatch)) } false } val regionMaskingFilter = RegionMaskingFilter(blockMask, regionFunction) val regionVisitor = RegionVisitor(selection, regionMaskingFilter) Operations.complete(regionVisitor) - if (matches.isNotEmpty()) { - searchResults[player.uniqueId] = matches + if (matchMap.isNotEmpty()) { + searchResults[player.uniqueId] = matchMap page(BukkitAdapter.adapt(player), 1) } else { searchResults.remove(player.uniqueId) player.printInfo(TextComponent.of("No results found.")) } } + + private fun parseMatch(baseBlock: BaseBlock, pattern: Regex): TextComponent? { + if (!baseBlock.hasNbtData()) { + return null + } + val compoundTag = baseBlock.nbtData!! + var localMatch: TextComponent? = null + buildString { + for (i in 1..4) { + val textTag = compoundTag.value["Text$i"] as StringTag + val component = GsonComponentSerializer.INSTANCE.deserialize(textTag.value) as TextComponent + val currentLine = component.getAllContent() + append("${currentLine}\n") + val localMatches = pattern.find(currentLine) + if (localMatches != null) { + localMatch = TextComponent.of("Line $i: ") + .color(TextColor.GRAY) + .append(currentLine.getHighlightedReplacement(localMatches.groupValues.first())) + break + } + } + }.removeSuffix("\n").apply { + if (localMatch != null) { + return localMatch + } else { + val match = pattern.find(this) ?: return null + return TextComponent.of("Multi-line match") + .color(TextColor.GRAY) + .hoverEvent(HoverEvent.showText(getHighlightedReplacement(match.groupValues.first()))) + } + } + } } class SearchPageCompletionHandler : diff --git a/src/main/kotlin/Util.kt b/src/main/kotlin/Util.kt index 293f923..28c535b 100644 --- a/src/main/kotlin/Util.kt +++ b/src/main/kotlin/Util.kt @@ -1,13 +1,25 @@ package redstonetools import com.sk89q.worldedit.util.formatting.text.TextComponent +import com.sk89q.worldedit.util.formatting.text.format.TextColor -fun TextComponent.getAllContent(): String { - return if (this.children().isEmpty()) { +fun TextComponent.getAllContent(): String = + if (this.children().isEmpty()) { this.content() } else { this.children().filterIsInstance().joinToString(separator = "") { textComponent -> textComponent.getAllContent() } } -} + +fun String.getHighlightedReplacement(replacement: String): TextComponent = + TextComponent.of(this.substringBefore(replacement)) + .color(TextColor.WHITE) + .append( + TextComponent.of(replacement) + .color(TextColor.YELLOW) + ) + .append( + TextComponent.of(this.substringAfter(replacement)) + .color(TextColor.WHITE) + )