🚚 Finalize annotation transition
This commit is contained in:
parent
f7c00244e7
commit
0fbe1fe6a2
21 changed files with 184 additions and 190 deletions
|
|
@ -423,10 +423,9 @@ public class Command<C> {
|
|||
*/
|
||||
public <U, V, O> @NonNull Builder<C> argumentPair(@NonNull final String name,
|
||||
@NonNull final TypeToken<O> outputType,
|
||||
@NonNull final Pair<@NonNull String, @NonNull String> names,
|
||||
@NonNull final Pair<@NonNull Class<U>, @NonNull Class<V>> parserPair,
|
||||
@NonNull final Function<Pair<@NonNull U, @NonNull V>,
|
||||
@NonNull O> mapper,
|
||||
@NonNull final Pair<String, String> names,
|
||||
@NonNull final Pair<Class<U>, Class<V>> parserPair,
|
||||
@NonNull final Function<Pair<U, V>, O> mapper,
|
||||
@NonNull final Description description) {
|
||||
if (this.commandManager == null) {
|
||||
throw new IllegalStateException("This cannot be called from a command that has no command manager attached");
|
||||
|
|
@ -455,10 +454,8 @@ public class Command<C> {
|
|||
* @return Builder instance with the argument inserted
|
||||
*/
|
||||
public <U, V, W> @NonNull Builder<C> argumentTriplet(@NonNull final String name,
|
||||
@NonNull final Triplet<@NonNull String,
|
||||
@NonNull String, @NonNull String> names,
|
||||
@NonNull final Triplet<@NonNull Class<U>,
|
||||
@NonNull Class<V>, @NonNull Class<W>> parserTriplet,
|
||||
@NonNull final Triplet<String, String, String> names,
|
||||
@NonNull final Triplet<Class<U>, Class<V>, Class<W>> parserTriplet,
|
||||
@NonNull final Description description) {
|
||||
if (this.commandManager == null) {
|
||||
throw new IllegalStateException("This cannot be called from a command that has no command manager attached");
|
||||
|
|
@ -489,12 +486,10 @@ public class Command<C> {
|
|||
*/
|
||||
public <U, V, W, O> @NonNull Builder<C> argumentTriplet(@NonNull final String name,
|
||||
@NonNull final TypeToken<O> outputType,
|
||||
@NonNull final Triplet<@NonNull String,
|
||||
@NonNull String, @NonNull String> names,
|
||||
@NonNull final Triplet<@NonNull Class<U>,
|
||||
@NonNull Class<V>, @NonNull Class<W>> parserTriplet,
|
||||
@NonNull final Function<@NonNull Triplet<@NonNull U,
|
||||
@NonNull V, @NonNull W>, @NonNull O> mapper,
|
||||
@NonNull final Triplet<String, String, String> names,
|
||||
@NonNull final Triplet<Class<U>, Class<V>,
|
||||
Class<W>> parserTriplet,
|
||||
@NonNull final Function<Triplet<U, V, W>, O> mapper,
|
||||
@NonNull final Description description) {
|
||||
if (this.commandManager == null) {
|
||||
throw new IllegalStateException("This cannot be called from a command that has no command manager attached");
|
||||
|
|
|
|||
|
|
@ -28,13 +28,31 @@ import cloud.commandframework.arguments.StaticArgument;
|
|||
import cloud.commandframework.arguments.compound.CompoundArgument;
|
||||
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import cloud.commandframework.exceptions.*;
|
||||
import cloud.commandframework.exceptions.AmbiguousNodeException;
|
||||
import cloud.commandframework.exceptions.ArgumentParseException;
|
||||
import cloud.commandframework.exceptions.InvalidCommandSenderException;
|
||||
import cloud.commandframework.exceptions.InvalidSyntaxException;
|
||||
import cloud.commandframework.exceptions.NoCommandInLeafException;
|
||||
import cloud.commandframework.exceptions.NoPermissionException;
|
||||
import cloud.commandframework.exceptions.NoSuchCommandException;
|
||||
import cloud.commandframework.permission.CommandPermission;
|
||||
import cloud.commandframework.permission.OrPermission;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ public class CommandArgument<C, T> implements Comparable<CommandArgument<?, ?>>
|
|||
}
|
||||
|
||||
@Override
|
||||
public final int compareTo(@NonNull CommandArgument<?, ?> o) {
|
||||
public final int compareTo(@NonNull final CommandArgument<?, ?> o) {
|
||||
if (this instanceof StaticArgument) {
|
||||
if (o instanceof StaticArgument) {
|
||||
return (this.getName().compareTo(o.getName()));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alexander Söderberg
|
||||
// Copyright (c) 2020 Alexander Söderberg & Contributors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alexander Söderberg
|
||||
// Copyright (c) 2020 Alexander Söderberg & Contributors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
@ -134,7 +134,7 @@ public class ArgumentTriplet<C, U, V, W, O> extends CompoundArgument<Triplet<U,
|
|||
* @return Argument triplet
|
||||
*/
|
||||
public @NonNull ArgumentTriplet<@NonNull C, @NonNull U, @NonNull V,
|
||||
@NonNull W, Triplet<@NonNull U, @NonNull V, @NonNull W>> simple() {
|
||||
@NonNull W, Triplet<U, V, W>> simple() {
|
||||
return new ArgumentTriplet<>(this.required,
|
||||
this.name,
|
||||
this.names,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alexander Söderberg
|
||||
// Copyright (c) 2020 Alexander Söderberg & Contributors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alexander Söderberg
|
||||
// Copyright (c) 2020 Alexander Söderberg & Contributors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ public final class StringArgument<C> extends CommandArgument<C, String> {
|
|||
* @return Created argument
|
||||
*/
|
||||
public static <C> @NonNull CommandArgument<C, String> optional(@NonNull final String name,
|
||||
@NonNull String defaultString) {
|
||||
@NonNull final String defaultString) {
|
||||
return StringArgument.<C>newBuilder(name).asOptionalWithDefault(defaultString).build();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alexander Söderberg
|
||||
// Copyright (c) 2020 Alexander Söderberg & Contributors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public class Triplet<U, V, W> implements Tuple {
|
|||
*
|
||||
* @return First value
|
||||
*/
|
||||
public final @NonNull U getFirst() {
|
||||
public final U getFirst() {
|
||||
return this.first;
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ public class Triplet<U, V, W> implements Tuple {
|
|||
*
|
||||
* @return Second value
|
||||
*/
|
||||
public final @NonNull V getSecond() {
|
||||
public final V getSecond() {
|
||||
return this.second;
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ public class Triplet<U, V, W> implements Tuple {
|
|||
*
|
||||
* @return Third value
|
||||
*/
|
||||
public final @NonNull W getThird() {
|
||||
public final W getThird() {
|
||||
return this.third;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@ import org.junit.jupiter.api.Assertions;
|
|||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
class CommandPermissionTest {
|
||||
|
||||
private final static CommandManager<TestCommandSender> manager = new PermissionOutputtingCommandManager();
|
||||
|
|
@ -59,12 +57,11 @@ class CommandPermissionTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(@Nonnull final TestCommandSender sender,
|
||||
@Nonnull final String permission) {
|
||||
public boolean hasPermission(final TestCommandSender sender,
|
||||
final String permission) {
|
||||
return acceptOne && permission.equalsIgnoreCase("test.permission.four");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public CommandMeta createDefaultCommandMeta() {
|
||||
return SimpleCommandMeta.empty();
|
||||
|
|
|
|||
|
|
@ -31,8 +31,6 @@ import org.junit.jupiter.api.Assertions;
|
|||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class CommandPostProcessorTest {
|
||||
|
||||
private static CommandManager<TestCommandSender> manager;
|
||||
|
|
@ -56,7 +54,7 @@ public class CommandPostProcessorTest {
|
|||
static final class SamplePostprocessor implements CommandPostprocessor<TestCommandSender> {
|
||||
|
||||
@Override
|
||||
public void accept(@Nonnull final CommandPostprocessingContext<TestCommandSender> context) {
|
||||
public void accept(final CommandPostprocessingContext<TestCommandSender> context) {
|
||||
ConsumerService.interrupt();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@ import org.junit.jupiter.api.Assertions;
|
|||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class CommandPreProcessorTest {
|
||||
|
||||
private static CommandManager<TestCommandSender> manager;
|
||||
|
|
@ -72,7 +70,7 @@ public class CommandPreProcessorTest {
|
|||
static final class SamplePreprocessor implements CommandPreprocessor<TestCommandSender> {
|
||||
|
||||
@Override
|
||||
public void accept(@Nonnull final CommandPreprocessingContext<TestCommandSender> context) {
|
||||
public void accept(final CommandPreprocessingContext<TestCommandSender> context) {
|
||||
try {
|
||||
final int num = Integer.parseInt(context.getInputQueue().removeFirst());
|
||||
context.getCommandContext().store("int", num);
|
||||
|
|
|
|||
|
|
@ -27,8 +27,6 @@ import cloud.commandframework.execution.CommandExecutionCoordinator;
|
|||
import cloud.commandframework.internal.CommandRegistrationHandler;
|
||||
import cloud.commandframework.meta.SimpleCommandMeta;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class TestCommandManager extends CommandManager<TestCommandSender> {
|
||||
|
||||
/**
|
||||
|
|
@ -38,15 +36,14 @@ public class TestCommandManager extends CommandManager<TestCommandSender> {
|
|||
super(CommandExecutionCoordinator.simpleCoordinator(), CommandRegistrationHandler.nullCommandRegistrationHandler());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
||||
@Override
|
||||
public final SimpleCommandMeta createDefaultCommandMeta() {
|
||||
return SimpleCommandMeta.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean hasPermission(@Nonnull final TestCommandSender sender,
|
||||
@Nonnull final String permission) {
|
||||
public final boolean hasPermission(final TestCommandSender sender, final String permission) {
|
||||
System.out.printf("Testing permission: %s\n", permission);
|
||||
return !permission.equalsIgnoreCase("no");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ import cloud.commandframework.extra.confirmation.CommandConfirmationManager;
|
|||
import cloud.commandframework.meta.SimpleCommandMeta;
|
||||
import cloud.commandframework.paper.PaperCommandManager;
|
||||
import cloud.commandframework.types.tuples.Triplet;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
|
|
@ -61,8 +61,8 @@ import org.bukkit.entity.Player;
|
|||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
|
@ -200,8 +200,10 @@ public final class BukkitTest extends JavaPlugin {
|
|||
.argument(StringArgument.required("string"))
|
||||
.handler(c -> c.getSender().sendMessage("Executed the command"))
|
||||
.build())
|
||||
.command(mgr.commandBuilder("annotationass").handler(c -> c.getSender()
|
||||
.sendMessage(ChatColor.YELLOW + "Du e en ananas!")).build())
|
||||
.command(mgr.commandBuilder("annotationass")
|
||||
.handler(c -> c.getSender()
|
||||
.sendMessage(ChatColor.YELLOW + "Du e en ananas!"))
|
||||
.build())
|
||||
.command(mgr.commandBuilder("cloud")
|
||||
.literal("confirm")
|
||||
.handler(confirmationManager.createConfirmationExecutionHandler()).build())
|
||||
|
|
@ -220,13 +222,13 @@ public final class BukkitTest extends JavaPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
private void registerTeleportCommand(@Nonnull final BukkitCommandManager<CommandSender> manager) {
|
||||
private void registerTeleportCommand(@NonNull final BukkitCommandManager<CommandSender> manager) {
|
||||
manager.command(mgr.commandBuilder("teleport")
|
||||
.meta("description", "Takes in a location and teleports the player there")
|
||||
.withSenderType(Player.class)
|
||||
.argument(WorldArgument.required("world"), Description.of("World name"))
|
||||
.argumentTriplet("coords",
|
||||
TypeToken.of(Vector.class),
|
||||
TypeToken.get(Vector.class),
|
||||
Triplet.of("x", "y", "z"),
|
||||
Triplet.of(Double.class, Double.class, Double.class),
|
||||
triplet -> new Vector(triplet.getFirst(), triplet.getSecond(), triplet.getThird()),
|
||||
|
|
@ -244,9 +246,9 @@ private void registerTeleportCommand(@Nonnull final BukkitCommandManager<Command
|
|||
|
||||
@CommandDescription("Test cloud command using @CommandMethod")
|
||||
@CommandMethod(value = "annotation|a <input> [number]", permission = "some.permission.node")
|
||||
private void annotatedCommand(@Nonnull final Player player,
|
||||
private void annotatedCommand(@NonNull final Player player,
|
||||
@Argument(value = "input", description = "Some string") @Completions("one,two,duck")
|
||||
@Nonnull final String input,
|
||||
@NonNull final String input,
|
||||
@Argument(value = "number", defaultValue = "5", description = "A number")
|
||||
@Range(min = "10", max = "100") final int number) {
|
||||
player.sendMessage(ChatColor.GOLD + "Your input was: " + ChatColor.AQUA + input + ChatColor.GREEN + " (" + number + ")");
|
||||
|
|
@ -265,8 +267,7 @@ private void registerTeleportCommand(@Nonnull final BukkitCommandManager<Command
|
|||
Bukkit.broadcastMessage(ChatColor.GRAY + "Calling Thread: " + Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private SimpleCommandMeta metaWithDescription(@Nonnull final String description) {
|
||||
private @NonNull SimpleCommandMeta metaWithDescription(@NonNull final String description) {
|
||||
return BukkitCommandMetaBuilder.builder().withDescription(description).build();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class BukkitPluginRegistrationHandler<C> implements CommandRegistrationHandler {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean registerCommand(@Nonnull final Command<?> command) {
|
||||
public boolean registerCommand(@NonNull final Command<?> command) {
|
||||
/* We only care about the root command argument */
|
||||
final CommandArgument<?, ?> commandArgument = command.getArguments().get(0);
|
||||
if (this.registeredCommands.containsKey(commandArgument)) {
|
||||
|
|
@ -111,9 +111,9 @@ class BukkitPluginRegistrationHandler<C> implements CommandRegistrationHandler {
|
|||
return true;
|
||||
}
|
||||
|
||||
protected void registerExternal(@Nonnull final String label,
|
||||
@Nonnull final Command<?> command,
|
||||
@Nonnull final BukkitCommand<C> bukkitCommand) {
|
||||
protected void registerExternal(@NonNull final String label,
|
||||
@NonNull final Command<?> command,
|
||||
@NonNull final BukkitCommand<C> bukkitCommand) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@ import com.mojang.brigadier.tree.LiteralCommandNode;
|
|||
import me.lucko.commodore.Commodore;
|
||||
import me.lucko.commodore.CommodoreProvider;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
class CloudCommodoreManager<C> extends BukkitPluginRegistrationHandler<C> {
|
||||
|
|
@ -40,7 +39,7 @@ class CloudCommodoreManager<C> extends BukkitPluginRegistrationHandler<C> {
|
|||
private final CloudBrigadierManager brigadierManager;
|
||||
private final Commodore commodore;
|
||||
|
||||
CloudCommodoreManager(@Nonnull final BukkitCommandManager<C> commandManager)
|
||||
CloudCommodoreManager(@NonNull final BukkitCommandManager<C> commandManager)
|
||||
throws BukkitCommandManager.BrigadierFailureException {
|
||||
if (!CommodoreProvider.isSupported()) {
|
||||
throw new BukkitCommandManager.BrigadierFailureException(BukkitCommandManager
|
||||
|
|
@ -53,9 +52,9 @@ class CloudCommodoreManager<C> extends BukkitPluginRegistrationHandler<C> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void registerExternal(@Nonnull final String label,
|
||||
@Nonnull final Command<?> command,
|
||||
@Nonnull final BukkitCommand<C> bukkitCommand) {
|
||||
protected void registerExternal(@NonNull final String label,
|
||||
@NonNull final Command<?> command,
|
||||
@NonNull final BukkitCommand<C> bukkitCommand) {
|
||||
final com.mojang.brigadier.Command<?> cmd = o -> 1;
|
||||
final LiteralCommandNode<?> literalCommandNode = this.brigadierManager
|
||||
.createLiteralCommandNode(label, command, (o, p) -> true, cmd);
|
||||
|
|
|
|||
|
|
@ -23,14 +23,14 @@
|
|||
//
|
||||
package cloud.commandframework.bukkit.parsers;
|
||||
|
||||
import cloud.commandframework.arguments.parser.ArgumentParser;
|
||||
import cloud.commandframework.arguments.CommandArgument;
|
||||
import cloud.commandframework.arguments.parser.ArgumentParseResult;
|
||||
import cloud.commandframework.arguments.parser.ArgumentParser;
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import org.bukkit.Material;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
|
@ -45,9 +45,10 @@ import java.util.stream.Collectors;
|
|||
public class MaterialArgument<C> extends CommandArgument<C, Material> {
|
||||
|
||||
protected MaterialArgument(final boolean required,
|
||||
@Nonnull final String name,
|
||||
@Nonnull final String defaultValue,
|
||||
@Nullable final BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider) {
|
||||
@NonNull final String name,
|
||||
@NonNull final String defaultValue,
|
||||
@Nullable final BiFunction<@NonNull CommandContext<C>, @NonNull String,
|
||||
@NonNull List<@NonNull String>> suggestionsProvider) {
|
||||
super(required, name, new MaterialParser<>(), defaultValue, Material.class, suggestionsProvider);
|
||||
}
|
||||
|
||||
|
|
@ -58,8 +59,7 @@ public class MaterialArgument<C> extends CommandArgument<C, Material> {
|
|||
* @param <C> Command sender type
|
||||
* @return Created builder
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> MaterialArgument.Builder<C> newBuilder(@Nonnull final String name) {
|
||||
public static <C> MaterialArgument.@NonNull Builder<C> newBuilder(@NonNull final String name) {
|
||||
return new MaterialArgument.Builder<>(name);
|
||||
}
|
||||
|
||||
|
|
@ -70,8 +70,7 @@ public class MaterialArgument<C> extends CommandArgument<C, Material> {
|
|||
* @param <C> Command sender type
|
||||
* @return Created argument
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> CommandArgument<C, Material> required(@Nonnull final String name) {
|
||||
public static <C> @NonNull CommandArgument<C, Material> required(@NonNull final String name) {
|
||||
return MaterialArgument.<C>newBuilder(name).asRequired().build();
|
||||
}
|
||||
|
||||
|
|
@ -82,8 +81,7 @@ public class MaterialArgument<C> extends CommandArgument<C, Material> {
|
|||
* @param <C> Command sender type
|
||||
* @return Created argument
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> CommandArgument<C, Material> optional(@Nonnull final String name) {
|
||||
public static <C> @NonNull CommandArgument<C, Material> optional(@NonNull final String name) {
|
||||
return MaterialArgument.<C>newBuilder(name).asOptional().build();
|
||||
}
|
||||
|
||||
|
|
@ -95,15 +93,14 @@ public class MaterialArgument<C> extends CommandArgument<C, Material> {
|
|||
* @param <C> Command sender type
|
||||
* @return Created argument
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> CommandArgument<C, Material> optional(@Nonnull final String name,
|
||||
@Nonnull final Material material) {
|
||||
public static <C> @NonNull CommandArgument<C, Material> optional(@NonNull final String name,
|
||||
@NonNull final Material material) {
|
||||
return MaterialArgument.<C>newBuilder(name).asOptionalWithDefault(material.name().toLowerCase()).build();
|
||||
}
|
||||
|
||||
public static final class Builder<C> extends CommandArgument.Builder<C, Material> {
|
||||
|
||||
protected Builder(@Nonnull final String name) {
|
||||
protected Builder(@NonNull final String name) {
|
||||
super(Material.class, name);
|
||||
}
|
||||
|
||||
|
|
@ -112,10 +109,9 @@ public class MaterialArgument<C> extends CommandArgument<C, Material> {
|
|||
|
||||
public static final class MaterialParser<C> implements ArgumentParser<C, Material> {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ArgumentParseResult<Material> parse(@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final Queue<String> inputQueue) {
|
||||
public @NonNull ArgumentParseResult<Material> parse(@NonNull final CommandContext<C> commandContext,
|
||||
@NonNull final Queue<@NonNull String> inputQueue) {
|
||||
final String input = inputQueue.peek();
|
||||
if (input == null) {
|
||||
return ArgumentParseResult.failure(new NullPointerException("No input was provided"));
|
||||
|
|
@ -142,7 +138,7 @@ public class MaterialArgument<C> extends CommandArgument<C, Material> {
|
|||
*
|
||||
* @param input Input
|
||||
*/
|
||||
public MaterialParseException(@Nonnull final String input) {
|
||||
public MaterialParseException(@NonNull final String input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
|
|
@ -151,8 +147,7 @@ public class MaterialArgument<C> extends CommandArgument<C, Material> {
|
|||
*
|
||||
* @return Input
|
||||
*/
|
||||
@Nonnull
|
||||
public String getInput() {
|
||||
public @NonNull String getInput() {
|
||||
return this.input;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alexander Söderberg
|
||||
// Copyright (c) 2020 Alexander Söderberg & Contributors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
@ -30,20 +30,30 @@ import cloud.commandframework.context.CommandContext;
|
|||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* Argument type that parses into {@link OfflinePlayer}. This is not thread safe. This
|
||||
* may also result in a blocking request to get the UUID for the offline player.
|
||||
* <p>
|
||||
* Avoid using this type if possible.
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePlayer> {
|
||||
|
||||
private OfflinePlayerArgument(final boolean required,
|
||||
@Nonnull final String name,
|
||||
@Nonnull final String defaultValue,
|
||||
@Nullable final BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider) {
|
||||
@NonNull final String name,
|
||||
@NonNull final String defaultValue,
|
||||
@Nullable final BiFunction<@NonNull CommandContext<C>, @NonNull String,
|
||||
@NonNull List<@NonNull String>> suggestionsProvider) {
|
||||
super(required, name, new OfflinePlayerParser<>(), defaultValue, OfflinePlayer.class, suggestionsProvider);
|
||||
}
|
||||
|
||||
|
|
@ -54,8 +64,7 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
|
|||
* @param <C> Command sender type
|
||||
* @return Created builder
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> Builder<C> newBuilder(@Nonnull final String name) {
|
||||
public static <C> @NonNull Builder<C> newBuilder(@NonNull final String name) {
|
||||
return new Builder<>(name);
|
||||
}
|
||||
|
||||
|
|
@ -66,8 +75,7 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
|
|||
* @param <C> Command sender type
|
||||
* @return Created component
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> CommandArgument<C, OfflinePlayer> required(@Nonnull final String name) {
|
||||
public static <C> @NonNull CommandArgument<C, OfflinePlayer> required(@NonNull final String name) {
|
||||
return OfflinePlayerArgument.<C>newBuilder(name).asRequired().build();
|
||||
}
|
||||
|
||||
|
|
@ -78,8 +86,7 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
|
|||
* @param <C> Command sender type
|
||||
* @return Created component
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> CommandArgument<C, OfflinePlayer> optional(@Nonnull final String name) {
|
||||
public static <C> @NonNull CommandArgument<C, OfflinePlayer> optional(@NonNull final String name) {
|
||||
return OfflinePlayerArgument.<C>newBuilder(name).asOptional().build();
|
||||
}
|
||||
|
||||
|
|
@ -87,20 +94,19 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
|
|||
* Create a new required command component with a default value
|
||||
*
|
||||
* @param name Component name
|
||||
* @param defaultNum Default num
|
||||
* @param defaultPlayer Default player
|
||||
* @param <C> Command sender type
|
||||
* @return Created component
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> CommandArgument<C, OfflinePlayer> optional(@Nonnull final String name,
|
||||
final String defaultNum) {
|
||||
return OfflinePlayerArgument.<C>newBuilder(name).asOptionalWithDefault(defaultNum).build();
|
||||
public static <C> @NonNull CommandArgument<C, OfflinePlayer> optional(@NonNull final String name,
|
||||
@NonNull final String defaultPlayer) {
|
||||
return OfflinePlayerArgument.<C>newBuilder(name).asOptionalWithDefault(defaultPlayer).build();
|
||||
}
|
||||
|
||||
|
||||
public static final class Builder<C> extends CommandArgument.Builder<C, OfflinePlayer> {
|
||||
|
||||
protected Builder(@Nonnull final String name) {
|
||||
protected Builder(@NonNull final String name) {
|
||||
super(OfflinePlayer.class, name);
|
||||
}
|
||||
|
||||
|
|
@ -109,9 +115,8 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
|
|||
*
|
||||
* @return Constructed component
|
||||
*/
|
||||
@Nonnull
|
||||
@Override
|
||||
public OfflinePlayerArgument<C> build() {
|
||||
public @NonNull OfflinePlayerArgument<C> build() {
|
||||
return new OfflinePlayerArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(),
|
||||
this.getSuggestionsProvider());
|
||||
}
|
||||
|
|
@ -121,10 +126,9 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
|
|||
|
||||
private static final class OfflinePlayerParser<C> implements ArgumentParser<C, OfflinePlayer> {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ArgumentParseResult<OfflinePlayer> parse(@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final Queue<String> inputQueue) {
|
||||
public @NonNull ArgumentParseResult<OfflinePlayer> parse(@NonNull final CommandContext<C> commandContext,
|
||||
@NonNull final Queue<String> inputQueue) {
|
||||
final String input = inputQueue.peek();
|
||||
if (input == null) {
|
||||
return ArgumentParseResult.failure(new NullPointerException("No input was provided"));
|
||||
|
|
@ -141,10 +145,9 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
|
|||
return ArgumentParseResult.success(player);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> suggestions(@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final String input) {
|
||||
public @NonNull List<@NonNull String> suggestions(@NonNull final CommandContext<C> commandContext,
|
||||
@NonNull final String input) {
|
||||
List<String> output = new ArrayList<>();
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
|
|
@ -168,7 +171,7 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
|
|||
*
|
||||
* @param input String input
|
||||
*/
|
||||
public OfflinePlayerParseException(@Nonnull final String input) {
|
||||
public OfflinePlayerParseException(@NonNull final String input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
|
|
@ -177,7 +180,7 @@ public final class OfflinePlayerArgument<C> extends CommandArgument<C, OfflinePl
|
|||
*
|
||||
* @return String value
|
||||
*/
|
||||
public String getInput() {
|
||||
public @NonNull String getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alexander Söderberg
|
||||
// Copyright (c) 2020 Alexander Söderberg & Contributors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
@ -29,20 +29,27 @@ import cloud.commandframework.arguments.parser.ArgumentParser;
|
|||
import cloud.commandframework.context.CommandContext;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* Argument that parses into a {@link Player}
|
||||
*
|
||||
* @param <C> Command sender type
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public final class PlayerArgument<C> extends CommandArgument<C, Player> {
|
||||
|
||||
private PlayerArgument(final boolean required,
|
||||
@Nonnull final String name,
|
||||
@Nonnull final String defaultValue,
|
||||
@Nullable final BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider) {
|
||||
@NonNull final String name,
|
||||
@NonNull final String defaultValue,
|
||||
@Nullable final BiFunction<@NonNull CommandContext<C>, @NonNull String,
|
||||
@NonNull List<@NonNull String>> suggestionsProvider) {
|
||||
super(required, name, new PlayerParser<>(), defaultValue, Player.class, suggestionsProvider);
|
||||
}
|
||||
|
||||
|
|
@ -53,8 +60,7 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
|
|||
* @param <C> Command sender type
|
||||
* @return Created builder
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> Builder<C> newBuilder(@Nonnull final String name) {
|
||||
public static <C> @NonNull Builder<C> newBuilder(@NonNull final String name) {
|
||||
return new Builder<>(name);
|
||||
}
|
||||
|
||||
|
|
@ -65,8 +71,7 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
|
|||
* @param <C> Command sender type
|
||||
* @return Created component
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> CommandArgument<C, Player> required(@Nonnull final String name) {
|
||||
public static <C> @NonNull CommandArgument<C, Player> required(@NonNull final String name) {
|
||||
return PlayerArgument.<C>newBuilder(name).asRequired().build();
|
||||
}
|
||||
|
||||
|
|
@ -77,8 +82,7 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
|
|||
* @param <C> Command sender type
|
||||
* @return Created component
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> CommandArgument<C, Player> optional(@Nonnull final String name) {
|
||||
public static <C> @NonNull CommandArgument<C, Player> optional(@NonNull final String name) {
|
||||
return PlayerArgument.<C>newBuilder(name).asOptional().build();
|
||||
}
|
||||
|
||||
|
|
@ -86,20 +90,19 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
|
|||
* Create a new required command component with a default value
|
||||
*
|
||||
* @param name Component name
|
||||
* @param defaultNum Default num
|
||||
* @param defaultPlayer Default player
|
||||
* @param <C> Command sender type
|
||||
* @return Created component
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> CommandArgument<C, Player> optional(@Nonnull final String name,
|
||||
final String defaultNum) {
|
||||
return PlayerArgument.<C>newBuilder(name).asOptionalWithDefault(defaultNum).build();
|
||||
public static <C> @NonNull CommandArgument<C, Player> optional(@NonNull final String name,
|
||||
@NonNull final String defaultPlayer) {
|
||||
return PlayerArgument.<C>newBuilder(name).asOptionalWithDefault(defaultPlayer).build();
|
||||
}
|
||||
|
||||
|
||||
public static final class Builder<C> extends CommandArgument.Builder<C, Player> {
|
||||
|
||||
protected Builder(@Nonnull final String name) {
|
||||
protected Builder(@NonNull final String name) {
|
||||
super(Player.class, name);
|
||||
}
|
||||
|
||||
|
|
@ -108,9 +111,8 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
|
|||
*
|
||||
* @return Constructed component
|
||||
*/
|
||||
@Nonnull
|
||||
@Override
|
||||
public PlayerArgument<C> build() {
|
||||
public @NonNull PlayerArgument<C> build() {
|
||||
return new PlayerArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(), this.getSuggestionsProvider());
|
||||
}
|
||||
|
||||
|
|
@ -119,10 +121,9 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
|
|||
|
||||
private static final class PlayerParser<C> implements ArgumentParser<C, Player> {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ArgumentParseResult<Player> parse(@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final Queue<String> inputQueue) {
|
||||
public @NonNull ArgumentParseResult<Player> parse(@NonNull final CommandContext<C> commandContext,
|
||||
@NonNull final Queue<@NonNull String> inputQueue) {
|
||||
final String input = inputQueue.peek();
|
||||
if (input == null) {
|
||||
return ArgumentParseResult.failure(new NullPointerException("No input was provided"));
|
||||
|
|
@ -139,10 +140,9 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
|
|||
return ArgumentParseResult.success(player);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> suggestions(@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final String input) {
|
||||
public @NonNull List<@NonNull String> suggestions(@NonNull final CommandContext<C> commandContext,
|
||||
@NonNull final String input) {
|
||||
List<String> output = new ArrayList<>();
|
||||
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
|
|
@ -166,7 +166,7 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
|
|||
*
|
||||
* @param input String input
|
||||
*/
|
||||
public PlayerParseException(@Nonnull final String input) {
|
||||
public PlayerParseException(@NonNull final String input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
|
|
@ -175,7 +175,7 @@ public final class PlayerArgument<C> extends CommandArgument<C, Player> {
|
|||
*
|
||||
* @return String value
|
||||
*/
|
||||
public String getInput() {
|
||||
public @NonNull String getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ import cloud.commandframework.arguments.parser.ArgumentParser;
|
|||
import cloud.commandframework.context.CommandContext;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.function.BiFunction;
|
||||
|
|
@ -45,8 +45,8 @@ import java.util.stream.Collectors;
|
|||
public class WorldArgument<C> extends CommandArgument<C, World> {
|
||||
|
||||
protected WorldArgument(final boolean required,
|
||||
@Nonnull final String name,
|
||||
@Nonnull final String defaultValue,
|
||||
@NonNull final String name,
|
||||
@NonNull final String defaultValue,
|
||||
@Nullable final BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider) {
|
||||
super(required, name, new WorldParser<>(), defaultValue, World.class, suggestionsProvider);
|
||||
}
|
||||
|
|
@ -58,8 +58,7 @@ public class WorldArgument<C> extends CommandArgument<C, World> {
|
|||
* @param <C> Command sender type
|
||||
* @return Created builder
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> CommandArgument.Builder<C, World> newBuilder(@Nonnull final String name) {
|
||||
public static <C> CommandArgument.@NonNull Builder<C, World> newBuilder(@NonNull final String name) {
|
||||
return new WorldArgument.Builder<>(name);
|
||||
}
|
||||
|
||||
|
|
@ -70,8 +69,7 @@ public class WorldArgument<C> extends CommandArgument<C, World> {
|
|||
* @param <C> Command sender type
|
||||
* @return Created argument
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> CommandArgument<C, World> required(@Nonnull final String name) {
|
||||
public static <C> @NonNull CommandArgument<C, World> required(@NonNull final String name) {
|
||||
return WorldArgument.<C>newBuilder(name).asRequired().build();
|
||||
}
|
||||
|
||||
|
|
@ -82,8 +80,7 @@ public class WorldArgument<C> extends CommandArgument<C, World> {
|
|||
* @param <C> Command sender type
|
||||
* @return Created argument
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> CommandArgument<C, World> optional(@Nonnull final String name) {
|
||||
public static <C> @NonNull CommandArgument<C, World> optional(@NonNull final String name) {
|
||||
return WorldArgument.<C>newBuilder(name).asOptional().build();
|
||||
}
|
||||
|
||||
|
|
@ -95,22 +92,20 @@ public class WorldArgument<C> extends CommandArgument<C, World> {
|
|||
* @param <C> Command sender type
|
||||
* @return Created argument
|
||||
*/
|
||||
@Nonnull
|
||||
public static <C> CommandArgument<C, World> optional(@Nonnull final String name,
|
||||
@Nonnull final String defaultValue) {
|
||||
public static <C> @NonNull CommandArgument<C, World> optional(@NonNull final String name,
|
||||
@NonNull final String defaultValue) {
|
||||
return WorldArgument.<C>newBuilder(name).asOptionalWithDefault(defaultValue).build();
|
||||
}
|
||||
|
||||
|
||||
public static final class Builder<C> extends CommandArgument.Builder<C, World> {
|
||||
|
||||
protected Builder(@Nonnull final String name) {
|
||||
protected Builder(@NonNull final String name) {
|
||||
super(World.class, name);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public CommandArgument<C, World> build() {
|
||||
public @NonNull CommandArgument<C, World> build() {
|
||||
return new WorldArgument<>(this.isRequired(), this.getName(), this.getDefaultValue(), this.getSuggestionsProvider());
|
||||
}
|
||||
}
|
||||
|
|
@ -118,10 +113,9 @@ public class WorldArgument<C> extends CommandArgument<C, World> {
|
|||
|
||||
public static final class WorldParser<C> implements ArgumentParser<C, World> {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ArgumentParseResult<World> parse(@Nonnull final CommandContext<C> commandContext,
|
||||
@Nonnull final Queue<String> inputQueue) {
|
||||
public @NonNull ArgumentParseResult<World> parse(@NonNull final CommandContext<C> commandContext,
|
||||
@NonNull final Queue<String> inputQueue) {
|
||||
final String input = inputQueue.peek();
|
||||
if (input == null) {
|
||||
return ArgumentParseResult.failure(new NullPointerException("No input was provided"));
|
||||
|
|
@ -136,9 +130,8 @@ public class WorldArgument<C> extends CommandArgument<C, World> {
|
|||
return ArgumentParseResult.success(world);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<String> suggestions(@Nonnull final CommandContext<C> commandContext, @Nonnull final String input) {
|
||||
public @NonNull List<String> suggestions(@NonNull final CommandContext<C> commandContext, @NonNull final String input) {
|
||||
return Bukkit.getWorlds().stream().map(World::getName).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
|
@ -154,7 +147,7 @@ public class WorldArgument<C> extends CommandArgument<C, World> {
|
|||
*
|
||||
* @param input Input
|
||||
*/
|
||||
public WorldParseException(@Nonnull final String input) {
|
||||
public WorldParseException(@NonNull final String input) {
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +156,7 @@ public class WorldArgument<C> extends CommandArgument<C, World> {
|
|||
*
|
||||
* @return Input
|
||||
*/
|
||||
public String getInput() {
|
||||
public @NonNull String getInput() {
|
||||
return this.input;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue