Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api: Advanced Component joining #344

Merged
merged 22 commits into from
Sep 4, 2021
Merged

Conversation

kezz
Copy link
Member

@kezz kezz commented Apr 26, 2021

This PR adds a new class, JoinConfiguration, which introduces a more advanced method of joining components. Essentially, this class would allow you to create a component like Online players: Bob, John, Harry and Martin. from a simple collection of names with ease. A join configuration contains a series of component parts:

  • A separator to go between each component,
  • A final separator to go between the final two components,
  • A prefix to be prepended to the result,
  • A suffix to be appended to the result, and
  • An convertor to change each joined ComponentLike into a Component.
  • A predicate to determine if each component is to be included.

There are also some unit tests to ensure everything works correctly.

@kezz kezz force-pushed the join-configuration branch from af553f7 to dc1994e Compare April 27, 2021 10:38
@kezz
Copy link
Member Author

kezz commented Apr 27, 2021

Just made all those requested changes in addition to cleaning up the Javadoc a bit. I just have two further questions:

  1. Could we replace the dl/dt/dd tags with just a normal unordered list? I know these tags are used elsewhere for similar purposes but they render terribly on web browsers and don't look very nice imo.
  2. Is it okay that the use of the full word in JoinConfiguration conflicts with the shortened use of it elsewhere? For example, TextReplacementConfig.

Copy link
Contributor

@BomBardyGamer BomBardyGamer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great to me, would be very usefulness to be able to join components very easily. I think it might be useful though to be able to configure this from just a plain string as well as from component like objects, instead of having to construct a text component every time.

@kezz
Copy link
Member Author

kezz commented May 19, 2021

Changes since last commit:

  • Support for creating lists separated by an Oxford comma for all you weirdos that use it
  • Javadocs have been improved to look less ugly
  • All methods now return Component instead of ComponentLike

Looks great to me, would be very usefulness to be able to join components very easily. I think it might be useful though to be able to configure this from just a plain string as well as from component like objects, instead of having to construct a text component every time.

Yeah I'm not sure about this - there's no place in the API that accepts components that also accepts methods to "easily" create components from strings, etc.

@kezz kezz force-pushed the join-configuration branch from d519f32 to ee0ab0a Compare May 19, 2021 11:10
*/
@Deprecated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should deprecate this method - it is perfectly fine as a convenient shorthand.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Text replacement methods got the same treatment - deprecated in favour of configuration. It is easier to maintain JoinConfiguration and not worrying about updating things here too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't consider arbitrary configuration objects to be particularly idiomatic unless there's no good way to separate the logic from the configuration. To me, an API that joins things together is a natural fit for combining the two into a single interface with configuration and logic. (See a comment below for an example of my idealized version of the API.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is simply matching how we do this in the rest of the Adventure API, and I tend to stick with consistency with how to use things.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This brings up a good point -- perhaps we should un-deprecate replaceText methods for the most simple case of Pattern -> Component, as a convenience option.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another solution is to just add convenience methods to TextReplacementConfig similar to the separator methods in JoinConfiguration, for example.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, I think if we were doing this from the start that would be good, but in the interest of minimizing API churn, I think maintaining the older methods would be the better way to go for both this and replacement.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this again, and with #418 I think replacing all these methods with one that takes a JoinConfiguration is the least awkward way forward.

*/
@Deprecated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

* @since 4.8.0
*/
@Contract(value = "_, _ -> new", pure = true)
static @NonNull TextComponent join(final @NonNull JoinConfiguration config, final @NonNull Iterable<? extends ComponentLike> components) {
Copy link
Contributor

@astei astei May 19, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this API be more idiomatic as a builder, or a modifiable (albeit immutable) ComponentJoiner object?

  private static final ComponentJoiner PLUGIN_JOINER = Component.joiner(Component.text(", "))
    .prefix(prefix)
    .suffix(suffix);

  // ...

  public static void sendPluginList(Audience target) {
    target.sendMessage(PLUGIN_JOINER.join(getPluginNameComponents()));
  }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is precedent here: see Guava Joiner and Splitter which uses the pattern I've described here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is matching how we do this in the rest of the Adventure API, however.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not a big fan of it. I guess it's something that can be revisited in Adventure 5, because I still believe this pattern feels unnatural to write.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do agree that a shorthand method to join directly from a join configuration would be nice, an earlier version of the PR did include one. Although I do understand that the syntax doesn't differ too much whether you're doing config.join(components) or join(config, components) and the latter is certainly more in keeping with the rest of the API, such as the text replacement system.

@kezz
Copy link
Member Author

kezz commented May 20, 2021

Remaining questions and thoughts I have about this PR:

  • Should an additional static method of creation for the configuration be made that includes the lastSeparatorIfSerial parameter (i.e. JoinConfiguration#separators(ComponentLike separator, ComponentLike lastSeparator, ComponentLike lastSeparatorIfSerial).
  • Should these static methods just be completely dropped in favour of the builder? The equivalent TextReplacementConfig doesn't have any static methods like this.
  • Is JoinConfiguration a good name or should it be JoinConfig in keeping with TextReplacementConfig?
  • Should a utility method be created to allow config.join(components) to work?
  • Can anyone think of a better name than lastSeparatorIfSerial?
  • Do the most recent changes to the join system make sense? I believe it would be logical that if you put in a single component with no prefix or suffix that you would receive the exact component that you supplied. This does mean that the old join methods need a bit of hackery to wrap them up in a text component if the new join methods didn't return one. This might seem messier, but imo it is a lot more logical and prevents the addition of unneeded bulk.

*/
@Deprecated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This brings up a good point -- perhaps we should un-deprecate replaceText methods for the most simple case of Pattern -> Component, as a convenience option.

api/src/main/java/net/kyori/adventure/text/Component.java Outdated Show resolved Hide resolved
@lucko
Copy link
Member

lucko commented Jun 4, 2021

Looks good but I agree with @astei re: the deprecations

@kezz kezz force-pushed the join-configuration branch from 5c7e431 to 2da510e Compare June 4, 2021 15:06
@kezz
Copy link
Member Author

kezz commented Jun 4, 2021

Rebased and applied zml's suggestions.

As for the deprecation of the "shorthand" methods, I believe this PR should be merged with the deprecations in place, and the removal of the deprecations for this and the replaceText methods should be done together in another PR to avoid inconsistencies and to provide a more pertinent place for discussion.

@zml2008
Copy link
Member

zml2008 commented Jun 5, 2021

Looking back at some of your comments:

  • JoinConfig is probably a better name
  • The static methods on JoinConfig should be dropped, in favor of the existing methods on Component
  • Why return TextComponent rather than just Component? The content of the result will always be empty, right?

@kezz
Copy link
Member Author

kezz commented Jun 5, 2021

  • JoinConfig is probably a better name

Agreed, it's definitely more in keeping with TextReplacementConfig.

  • The static methods on JoinConfig should be dropped, in favor of the existing methods on Component

Do you mean the methods like separators() and noSeparators()? Or the previously added join methods that have since been removed.

  • Why return TextComponent rather than just Component? The content of the result will always be empty, right?

Yes, the return here is only done because that's what the old methods returned. TextComponent is never returned for any of the new methods added in this PR.

@zml2008
Copy link
Member

zml2008 commented Jun 5, 2021

Do you mean the methods like separators() and noSeparators()? Or the previously added join methods that have since been removed.

Yeah, I think those last two should be removed too.

Yes, the return here is only done because that's what the old methods returned. TextComponent is never returned for any of the new methods added in this PR.

oh oops, nvm that then

@kezz kezz force-pushed the join-configuration branch from 802dc42 to d5fab99 Compare June 29, 2021 16:04
@kezz
Copy link
Member Author

kezz commented Jun 29, 2021

Just rebased this for a 4.9.0 release. I still think the questions regarding deprecation are valid, but I do still think they are better served in another PR that would undeprecate both sets of methods or one that provides shorthand methods for TextReplacementConfig.

Copy link
Member

@zml2008 zml2008 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you deprecate TextComponent.ofChildren as well, to be replaced by join(JoinConfiguration.noSeparator())?

*/
@Deprecated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this again, and with #418 I think replacing all these methods with one that takes a JoinConfiguration is the least awkward way forward.

api/src/main/java/net/kyori/adventure/text/Component.java Outdated Show resolved Hide resolved
@kezz kezz requested a review from zml2008 August 23, 2021 09:12
@kezz kezz force-pushed the join-configuration branch from 0718fac to b700073 Compare September 4, 2021 18:25
@zml2008 zml2008 self-assigned this Sep 4, 2021
@zml2008 zml2008 enabled auto-merge September 4, 2021 19:05
@zml2008 zml2008 merged commit 629e2ed into KyoriPowered:master Sep 4, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Move TextComponent.ofChildren(ComponentLike...) into Component
6 participants